From fe358166db182e337e43aea1a4f9ae6a05279f16 Mon Sep 17 00:00:00 2001
From: "James E. Blair" <jeblair@redhat.com>
Date: Wed, 22 Aug 2018 16:02:46 -0700
Subject: [PATCH] Add write-inventory role

Change-Id: Ife274ddcf78398eef730951635ca0a2a78096871
---
 roles/write-inventory/README.rst              |  26 ++++
 roles/write-inventory/__init__.py             |   0
 roles/write-inventory/library/__init__.py     |   0
 .../library/test_write_inventory.py           | 131 ++++++++++++++++++
 .../library/write_inventory.py                |  74 ++++++++++
 roles/write-inventory/tasks/main.yaml         |   6 +
 6 files changed, 237 insertions(+)
 create mode 100644 roles/write-inventory/README.rst
 create mode 100644 roles/write-inventory/__init__.py
 create mode 100644 roles/write-inventory/library/__init__.py
 create mode 100644 roles/write-inventory/library/test_write_inventory.py
 create mode 100755 roles/write-inventory/library/write_inventory.py
 create mode 100644 roles/write-inventory/tasks/main.yaml

diff --git a/roles/write-inventory/README.rst b/roles/write-inventory/README.rst
new file mode 100644
index 000000000..9d465c3f1
--- /dev/null
+++ b/roles/write-inventory/README.rst
@@ -0,0 +1,26 @@
+Write an abbreviated version of the Zuul inventory to a file
+
+This writes the minimal information about hosts from the current Zuul
+inventory to a file.  It may be used to subsequently invoke Ansible
+with the inventory for the job.
+
+**Role Variables**
+
+.. zuul:rolevar:: write_inventory_dest
+
+   The path of the inventory file to write.
+
+.. zuul:rolevar:: write_inventory_include_hostvars
+   :type: list
+
+   A list of facts about the host to include.  By default this
+   parameter is omitted and all variables about a host will be
+   included.  To only include certain variables, list them here.  The
+   empty list will cause no variables to be included.
+
+.. zuul:rolevar:: write_inventory_exclude_hostvars
+   :type: list
+
+   A list of facts about the host to include.  By default, all
+   variables about a host will be included.  To exclude certain
+   variables, list them here.
diff --git a/roles/write-inventory/__init__.py b/roles/write-inventory/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/roles/write-inventory/library/__init__.py b/roles/write-inventory/library/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/roles/write-inventory/library/test_write_inventory.py b/roles/write-inventory/library/test_write_inventory.py
new file mode 100644
index 000000000..7951e84d3
--- /dev/null
+++ b/roles/write-inventory/library/test_write_inventory.py
@@ -0,0 +1,131 @@
+# Copyright (C) 2018 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+#
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import
+import testtools
+import fixtures
+import os
+
+import yaml
+
+from .write_inventory import run
+
+INPUT = yaml.safe_load("""
+bionic:
+  ansible_connection: ssh
+  ansible_host: 104.130.217.77
+  ansible_port: 22
+  ansible_user: zuul
+  nodepool:
+    az: null
+    cloud: rax
+    interface_ip: 104.130.217.77
+    label: ubuntu-bionic
+    private_ipv4: 10.210.196.115
+    provider: rax-ord
+    public_ipv4: 104.130.217.77
+    public_ipv6: 2001:4801:7828:101:be76:4eff:fe10:14eb
+    region: ORD
+xenial:
+  ansible_connection: ssh
+  ansible_host: 149.202.170.85
+  ansible_port: 22
+  ansible_user: zuul
+  nodepool:
+    az: nova
+    cloud: ovh
+    interface_ip: 149.202.170.85
+    label: ubuntu-xenial
+    private_ipv4: 149.202.170.85
+    provider: ovh-gra1
+    public_ipv4: 149.202.170.85
+    public_ipv6: 2001:41d0:302:1000::17:a32b
+    region: GRA1
+""")
+
+
+class TestWriteInventory(testtools.TestCase):
+    def assertOutput(self, dest, ref):
+        with open(dest) as f:
+            out = yaml.safe_load(f)
+        self.assertEqual(ref, out)
+
+    def test_all(self):
+        '''Test passing all variables'''
+        dest = self.useFixture(fixtures.TempDir()).path
+        dest = os.path.join(dest, 'out.yaml')
+        run(dest, INPUT, None, None)
+
+        self.assertOutput(dest, {
+            'all': {
+                'hosts': {
+                    'bionic': {
+                        "ansible_connection": "ssh",
+                        "ansible_user": "zuul",
+                        "ansible_host": "104.130.217.77",
+                        "ansible_port": 22
+                    },
+                    'xenial': {
+                        "ansible_connection": "ssh",
+                        "ansible_user": "zuul",
+                        "ansible_host": "149.202.170.85",
+                        "ansible_port": 22,
+                    }
+                }
+            }
+        })
+
+    def test_include(self):
+        '''Test incuding vars'''
+        dest = self.useFixture(fixtures.TempDir()).path
+        dest = os.path.join(dest, 'out.yaml')
+        run(dest, INPUT, ['ansible_host'], None)
+
+        self.assertOutput(dest, {
+            'all': {
+                'hosts': {
+                    'bionic': {
+                        "ansible_host": "104.130.217.77",
+                    },
+                    'xenial': {
+                        "ansible_host": "149.202.170.85",
+                    }
+                }
+            }
+        })
+
+    def test_exclude(self):
+        '''Test passing all variables'''
+        dest = self.useFixture(fixtures.TempDir()).path
+        dest = os.path.join(dest, 'out.yaml')
+        run(dest, INPUT, None, ['ansible_user'])
+
+        self.assertOutput(dest, {
+            'all': {
+                'hosts': {
+                    'bionic': {
+                        "ansible_connection": "ssh",
+                        "ansible_host": "104.130.217.77",
+                        "ansible_port": 22
+                    },
+                    'xenial': {
+                        "ansible_connection": "ssh",
+                        "ansible_host": "149.202.170.85",
+                        "ansible_port": 22,
+                    }
+                }
+            }
+        })
diff --git a/roles/write-inventory/library/write_inventory.py b/roles/write-inventory/library/write_inventory.py
new file mode 100755
index 000000000..0cb966271
--- /dev/null
+++ b/roles/write-inventory/library/write_inventory.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+#
+# Copyright 2018 Red Hat, Inc
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+
+from ansible.module_utils.basic import AnsibleModule
+
+# The list of variables we might include
+VARS = [
+    'ansible_connection',
+    'ansible_host',
+    'ansible_port',
+    'ansible_user'
+]
+
+
+def run(dest, hostvars, include, exclude):
+    out_all = {}
+    out = {'all': {'hosts': out_all}}
+    for host, hvars in hostvars.items():
+        d = {}
+        for v in VARS:
+            if v not in hvars:
+                continue
+            if include is not None:
+                if v not in include:
+                    continue
+            if exclude is not None:
+                if v in exclude:
+                    continue
+            d[v] = hvars[v]
+        out_all[host] = d
+
+    with open(dest, 'w') as f:
+        f.write(json.dumps(out))
+
+
+def ansible_main():
+    module = AnsibleModule(
+        argument_spec=dict(
+            dest=dict(required=True, type='path'),
+            hostvars=dict(required=True, type='raw'),
+            include_hostvars=dict(type='list'),
+            exclude_hostvars=dict(type='list'),
+        )
+    )
+
+    p = module.params
+
+    dest = p.get('dest')
+    hostvars = p.get('hostvars')
+    include = p.get('include_hostvars')
+    exclude = p.get('exclude_hostvars')
+
+    run(dest, hostvars, include, exclude)
+
+    module.exit_json(changed=True)
+
+
+if __name__ == '__main__':
+    ansible_main()
diff --git a/roles/write-inventory/tasks/main.yaml b/roles/write-inventory/tasks/main.yaml
new file mode 100644
index 000000000..52ae232dd
--- /dev/null
+++ b/roles/write-inventory/tasks/main.yaml
@@ -0,0 +1,6 @@
+- name: Write inventory file
+  write_inventory:
+    dest: "{{ write_inventory_dest }}"
+    hostvars: "{{ hostvars }}"
+    include_hostvars: "{{ write_inventory_include_hostvars | default(omit) }}"
+    exclude_hostvars: "{{ write_inventory_exclude_hostvars | default(omit) }}"