Repository: openshift/openshift-ansible

Issue:
openshift_facts module inside openshift_facts role causes runtime error on python3 host
#### Description

The module `openshift_facts` inside the `openshift_facts` role causes a python runtime error in python3 mode, which is now required for a Fedora installation.

##### Version

* Ansible version per `ansible --version`

```
ansible 2.3.1.0
  config file = /root/openshift-ansible/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.5 (default, Aug  2 2016, 04:20:16) [GCC 4.8.5 20150623 (Red Hat 4.8.5-4)]
```

* The output of `git describe` -> `openshift-ansible-3.5.101-1`

##### Steps To Reproduce
1. `ansible-playbook playbooks/byo/config.yml -i path/to/inventory` against Fedora 26 hosts using Python3 

##### Expected Results

The playbook to not error.

##### Observed Results

```
TASK [openshift_facts : Gather Cluster facts and set is_containerized if needed] **************************************
task path: /root/openshift-ansible/roles/openshift_facts/tasks/main.yml:71
Using module file /root/openshift-ansible/roles/openshift_facts/library/openshift_facts.py
<osbs-master01.stg.phx2.fedoraproject.org> ESTABLISH SSH CONNECTION FOR USER: root
<osbs-master01.stg.phx2.fedoraproject.org> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=root -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/dbbd3b6aef osbs-master01.stg.phx2.fedoraproject.org '/bin/sh -c '"'"'/usr/bin/python3 && sleep 0'"'"''
Using module file /root/openshift-ansible/roles/openshift_facts/library/openshift_facts.py
<osbs-node01.stg.phx2.fedoraproject.org> ESTABLISH SSH CONNECTION FOR USER: root
<osbs-node01.stg.phx2.fedoraproject.org> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=root -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/f265a6ea1f osbs-node01.stg.phx2.fedoraproject.org '/bin/sh -c '"'"'/usr/bin/python3 && sleep 0'"'"''
Using module file /root/openshift-ansible/roles/openshift_facts/library/openshift_facts.py
<osbs-node02.stg.phx2.fedoraproject.org> ESTABLISH SSH CONNECTION FOR USER: root
<osbs-node02.stg.phx2.fedoraproject.org> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o User=root -o ConnectTimeout=10 -o ControlPath=/root/.ansible/cp/a893e1f1b4 osbs-node02.stg.phx2.fedoraproject.org '/bin/sh -c '"'"'/usr/bin/python3 && sleep 0'"'"''
<osbs-master01.stg.phx2.fedoraproject.org> (1, '', 'Traceback (most recent call last):\n  File "/tmp/ansible_47dtpzw8/ansible_module_openshift_facts.py", line 2491, in <module>\n    main()\n  File "/tmp/ansible_47dtpzw8/ansible_module_openshift_facts.py", line 2478, in main\n    protected_facts_to_overwrite)\n  File "/tmp/ansible_47dtpzw8/ansible_module_openshift_facts.py", line 1899, in __init__\n    for (k, v) in self.system_facts.items():\nRuntimeError: dictionary changed size during iteration\n')
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_47dtpzw8/ansible_module_openshift_facts.py", line 2491, in <module>
    main()
  File "/tmp/ansible_47dtpzw8/ansible_module_openshift_facts.py", line 2478, in main
    protected_facts_to_overwrite)
  File "/tmp/ansible_47dtpzw8/ansible_module_openshift_facts.py", line 1899, in __init__
    for (k, v) in self.system_facts.items():
RuntimeError: dictionary changed size during iteration

fatal: [osbs-master01.stg.phx2.fedoraproject.org]: FAILED! => {
    "changed": false,
    "failed": true,
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_47dtpzw8/ansible_module_openshift_facts.py\", line 2491, in <module>\n    main()\n  File \"/tmp/ansible_47dtpzw8/ansible_module_openshift_facts.py\", line 2478, in main\n    protected_facts_to_overwrite)\n  File \"/tmp/ansible_47dtpzw8/ansible_module_openshift_facts.py\", line 1899, in __init__\n    for (k, v) in self.system_facts.items():\nRuntimeError: dictionary changed size during iteration\n",
    "module_stdout": "",
    "rc": 1
}

MSG:

MODULE FAILURE

<osbs-node01.stg.phx2.fedoraproject.org> (1, '', 'Traceback (most recent call last):\n  File "/tmp/ansible_8w3_0w4b/ansible_module_openshift_facts.py", line 2491, in <module>\n    main()\n  File "/tmp/ansible_8w3_0w4b/ansible_module_openshift_facts.py", line 2478, in main\n    protected_facts_to_overwrite)\n  File "/tmp/ansible_8w3_0w4b/ansible_module_openshift_facts.py", line 1899, in __init__\n    for (k, v) in self.system_facts.items():\nRuntimeError: dictionary changed size during iteration\n')
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_8w3_0w4b/ansible_module_openshift_facts.py", line 2491, in <module>
    main()
  File "/tmp/ansible_8w3_0w4b/ansible_module_openshift_facts.py", line 2478, in main
    protected_facts_to_overwrite)
  File "/tmp/ansible_8w3_0w4b/ansible_module_openshift_facts.py", line 1899, in __init__
    for (k, v) in self.system_facts.items():
RuntimeError: dictionary changed size during iteration

fatal: [osbs-node01.stg.phx2.fedoraproject.org]: FAILED! => {
    "changed": false,
    "failed": true,
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_8w3_0w4b/ansible_module_openshift_facts.py\", line 2491, in <module>\n    main()\n  File \"/tmp/ansible_8w3_0w4b/ansible_module_openshift_facts.py\", line 2478, in main\n    protected_facts_to_overwrite)\n  File \"/tmp/ansible_8w3_0w4b/ansible_module_openshift_facts.py\", line 1899, in __init__\n    for (k, v) in self.system_facts.items():\nRuntimeError: dictionary changed size during iteration\n",
    "module_stdout": "",
    "rc": 1
}

MSG:

MODULE FAILURE

<osbs-node02.stg.phx2.fedoraproject.org> (1, '', 'Traceback (most recent call last):\n  File "/tmp/ansible_p7vpddi8/ansible_module_openshift_facts.py", line 2491, in <module>\n    main()\n  File "/tmp/ansible_p7vpddi8/ansible_module_openshift_facts.py", line 2478, in main\n    protected_facts_to_overwrite)\n  File "/tmp/ansible_p7vpddi8/ansible_module_openshift_facts.py", line 1899, in __init__\n    for (k, v) in self.system_facts.items():\nRuntimeError: dictionary changed size during iteration\n')
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_p7vpddi8/ansible_module_openshift_facts.py", line 2491, in <module>
    main()
  File "/tmp/ansible_p7vpddi8/ansible_module_openshift_facts.py", line 2478, in main
    protected_facts_to_overwrite)
  File "/tmp/ansible_p7vpddi8/ansible_module_openshift_facts.py", line 1899, in __init__
    for (k, v) in self.system_facts.items():
RuntimeError: dictionary changed size during iteration

fatal: [osbs-node02.stg.phx2.fedoraproject.org]: FAILED! => {
    "changed": false,
    "failed": true,
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_p7vpddi8/ansible_module_openshift_facts.py\", line 2491, in <module>\n    main()\n  File \"/tmp/ansible_p7vpddi8/ansible_module_openshift_facts.py\", line 2478, in main\n    protected_facts_to_overwrite)\n  File \"/tmp/ansible_p7vpddi8/ansible_module_openshift_facts.py\", line 1899, in __init__\n    for (k, v) in self.system_facts.items():\nRuntimeError: dictionary changed size during iteration\n",
    "module_stdout": "",
    "rc": 1
}

MSG:

MODULE FAILURE

```

##### Additional Information


```
NAME=Fedora
VERSION="26 (Twenty Six)"
ID=fedora
VERSION_ID=26
PRETTY_NAME="Fedora 26 (Twenty Six)"
ANSI_COLOR="0;34"
CPE_NAME="cpe:/o:fedoraproject:fedora:26"
HOME_URL="https://fedoraproject.org/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_BUGZILLA_PRODUCT="Fedora"
REDHAT_BUGZILLA_PRODUCT_VERSION=26
REDHAT_SUPPORT_PRODUCT="Fedora"
REDHAT_SUPPORT_PRODUCT_VERSION=26
PRIVACY_POLICY_URL=https://fedoraproject.org/wiki/Legal:PrivacyPolicy
```



Discussion:

Resolving patch:
diff --git a/filter_plugins/oo_filters.py b/filter_plugins/oo_filters.py
--- a/filter_plugins/oo_filters.py
+++ b/filter_plugins/oo_filters.py
@@ -13,6 +13,7 @@
 import yaml
 import random
 
+from base64 import b64encode
 from ansible import errors
 from collections import Mapping
 from distutils.util import strtobool
@@ -672,8 +673,7 @@ def oo_generate_secret(num_bytes):
     if not isinstance(num_bytes, int):
         raise errors.AnsibleFilterError("|failed expects num_bytes is int")
 
-    secret = os.urandom(num_bytes)
-    return secret.encode('base-64').strip()
+    return b64encode(os.urandom(num_bytes)).decode('utf-8')
 
 
 def to_padded_yaml(data, level=0, indent=2, **kw):
diff --git a/roles/openshift_facts/library/openshift_facts.py b/roles/openshift_facts/library/openshift_facts.py
--- a/roles/openshift_facts/library/openshift_facts.py
+++ b/roles/openshift_facts/library/openshift_facts.py
@@ -1892,14 +1892,16 @@ def __init__(self, role, filename, local_facts,
             )
         self.role = role
 
+        # Collect system facts and preface each fact with 'ansible_'.
         try:
-            # ansible-2.1
             # pylint: disable=too-many-function-args,invalid-name
             self.system_facts = ansible_facts(module, ['hardware', 'network', 'virtual', 'facter'])  # noqa: F405
+            additional_facts = {}
             for (k, v) in self.system_facts.items():
-                self.system_facts["ansible_%s" % k.replace('-', '_')] = v
+                additional_facts["ansible_%s" % k.replace('-', '_')] = v
+            self.system_facts.update(additional_facts)
         except UnboundLocalError:
-            # ansible-2.2
+            # ansible-2.2,2.3
             self.system_facts = get_all_facts(module)['ansible_facts']  # noqa: F405
 
         self.facts = self.generate_facts(local_facts,


Test patch: