We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Fix multimaster OpenStack deployment failure
When trying to deploy multimaster OpenStack with nsupdate and either no
load balancer or OpenStack LBaaS, the creation of the DNS records would
fail.

This is because it was still relying on the old `lb` inventory group for
all multimaster scenarios, rather than just in the
`openshift_openstack_use_lbaas_load_balancer` case.

So we define a new inventory variable
`openshift_openstack_private_api_ip` (which is already defined in the
Heat templates) and use that instead.

Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1588428

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `playbooks/openstack/inventory.py`
Content:
```
1 #!/usr/bin/env python
2 """
3 This is an Ansible dynamic inventory for OpenStack.
4 
5 It requires your OpenStack credentials to be set in clouds.yaml or your shell
6 environment.
7 
8 """
9 
10 from __future__ import print_function
11 
12 from collections import Mapping
13 import json
14 import os
15 
16 from keystoneauth1.exceptions.catalog import EndpointNotFound
17 import shade
18 
19 
20 def base_openshift_inventory(cluster_hosts):
21     '''Set the base openshift inventory.'''
22     inventory = {}
23 
24     masters = [server.name for server in cluster_hosts
25                if server.metadata['host-type'] == 'master']
26 
27     etcd = [server.name for server in cluster_hosts
28             if server.metadata['host-type'] == 'etcd']
29     if not etcd:
30         etcd = masters
31 
32     infra_hosts = [server.name for server in cluster_hosts
33                    if server.metadata['host-type'] == 'node' and
34                    server.metadata['sub-host-type'] == 'infra']
35 
36     app = [server.name for server in cluster_hosts
37            if server.metadata['host-type'] == 'node' and
38            server.metadata['sub-host-type'] == 'app']
39 
40     cns = [server.name for server in cluster_hosts
41            if server.metadata['host-type'] == 'cns']
42 
43     nodes = list(set(masters + infra_hosts + app + cns))
44 
45     dns = [server.name for server in cluster_hosts
46            if server.metadata['host-type'] == 'dns']
47 
48     load_balancers = [server.name for server in cluster_hosts
49                       if server.metadata['host-type'] == 'lb']
50 
51     osev3 = list(set(nodes + etcd + load_balancers))
52 
53     inventory['cluster_hosts'] = {'hosts': [s.name for s in cluster_hosts]}
54     inventory['OSEv3'] = {'hosts': osev3, 'vars': {}}
55     inventory['masters'] = {'hosts': masters}
56     inventory['etcd'] = {'hosts': etcd}
57     inventory['nodes'] = {'hosts': nodes}
58     inventory['infra_hosts'] = {'hosts': infra_hosts}
59     inventory['app'] = {'hosts': app}
60     inventory['glusterfs'] = {'hosts': cns}
61     inventory['dns'] = {'hosts': dns}
62     inventory['lb'] = {'hosts': load_balancers}
63     inventory['localhost'] = {'ansible_connection': 'local'}
64 
65     return inventory
66 
67 
68 def get_docker_storage_mountpoints(volumes):
69     '''Check volumes to see if they're being used for docker storage'''
70     docker_storage_mountpoints = {}
71     for volume in volumes:
72         if volume.metadata.get('purpose') == "openshift_docker_storage":
73             for attachment in volume.attachments:
74                 if attachment.server_id in docker_storage_mountpoints:
75                     docker_storage_mountpoints[attachment.server_id].append(attachment.device)
76                 else:
77                     docker_storage_mountpoints[attachment.server_id] = [attachment.device]
78     return docker_storage_mountpoints
79 
80 
81 def _get_hostvars(server, docker_storage_mountpoints):
82     ssh_ip_address = server.public_v4 or server.private_v4
83     hostvars = {
84         'ansible_host': ssh_ip_address
85     }
86 
87     public_v4 = server.public_v4 or server.private_v4
88     if public_v4:
89         hostvars['public_v4'] = server.public_v4
90         hostvars['openshift_public_ip'] = server.public_v4
91     # TODO(shadower): what about multiple networks?
92     if server.private_v4:
93         hostvars['private_v4'] = server.private_v4
94         hostvars['openshift_ip'] = server.private_v4
95 
96         # NOTE(shadower): Yes, we set both hostname and IP to the private
97         # IP address for each node. OpenStack doesn't resolve nodes by
98         # name at all, so using a hostname here would require an internal
99         # DNS which would complicate the setup and potentially introduce
100         # performance issues.
101         hostvars['openshift_hostname'] = server.metadata.get(
102             'openshift_hostname', server.private_v4)
103     hostvars['openshift_public_hostname'] = server.name
104 
105     if server.metadata['host-type'] == 'cns':
106         hostvars['glusterfs_devices'] = ['/dev/nvme0n1']
107 
108     node_labels = server.metadata.get('node_labels')
109     # NOTE(shadower): the node_labels value must be a dict not string
110     if not isinstance(node_labels, Mapping):
111         node_labels = json.loads(node_labels)
112 
113     if node_labels:
114         hostvars['openshift_node_labels'] = node_labels
115 
116     # check for attached docker storage volumes
117     if 'os-extended-volumes:volumes_attached' in server:
118         if server.id in docker_storage_mountpoints:
119             hostvars['docker_storage_mountpoints'] = ' '.join(
120                 docker_storage_mountpoints[server.id])
121     return hostvars
122 
123 
124 def build_inventory():
125     '''Build the dynamic inventory.'''
126     cloud = shade.openstack_cloud()
127 
128     # TODO(shadower): filter the servers based on the `OPENSHIFT_CLUSTER`
129     # environment variable.
130     cluster_hosts = [
131         server for server in cloud.list_servers()
132         if 'metadata' in server and 'clusterid' in server.metadata]
133 
134     inventory = base_openshift_inventory(cluster_hosts)
135 
136     for server in cluster_hosts:
137         if 'group' in server.metadata:
138             group = server.metadata.get('group')
139             if group not in inventory:
140                 inventory[group] = {'hosts': []}
141             inventory[group]['hosts'].append(server.name)
142 
143     inventory['_meta'] = {'hostvars': {}}
144 
145     # Some clouds don't have Cinder. That's okay:
146     try:
147         volumes = cloud.list_volumes()
148     except EndpointNotFound:
149         volumes = []
150 
151     # cinder volumes used for docker storage
152     docker_storage_mountpoints = get_docker_storage_mountpoints(volumes)
153     for server in cluster_hosts:
154         inventory['_meta']['hostvars'][server.name] = _get_hostvars(
155             server,
156             docker_storage_mountpoints)
157 
158     stout = _get_stack_outputs(cloud)
159     if stout is not None:
160         try:
161             inventory['localhost'].update({
162                 'openshift_openstack_api_lb_provider':
163                 stout['api_lb_provider'],
164                 'openshift_openstack_api_lb_port_id':
165                 stout['api_lb_vip_port_id'],
166                 'openshift_openstack_api_lb_sg_id':
167                 stout['api_lb_sg_id']})
168         except KeyError:
169             pass  # Not an API load balanced deployment
170 
171         try:
172             inventory['OSEv3']['vars'][
173                 'openshift_master_cluster_hostname'] = stout['private_api_ip']
174         except KeyError:
175             pass  # Internal LB not specified
176 
177         inventory['localhost']['openshift_openstack_public_api_ip'] = \
178             stout.get('public_api_ip')
179         inventory['localhost']['openshift_openstack_public_router_ip'] = \
180             stout.get('public_router_ip')
181 
182         try:
183             inventory['OSEv3']['vars'] = _get_kuryr_vars(cloud, stout)
184         except KeyError:
185             pass  # Not a kuryr deployment
186     return inventory
187 
188 
189 def _get_stack_outputs(cloud_client):
190     """Returns a dictionary with the stack outputs"""
191     cluster_name = os.getenv('OPENSHIFT_CLUSTER', 'openshift-cluster')
192 
193     stack = cloud_client.get_stack(cluster_name)
194     if stack is None or stack['stack_status'] not in (
195             'CREATE_COMPLETE', 'UPDATE_COMPLETE'):
196         return None
197 
198     data = {}
199     for output in stack['outputs']:
200         data[output['output_key']] = output['output_value']
201     return data
202 
203 
204 def _get_kuryr_vars(cloud_client, data):
205     """Returns a dictionary of Kuryr variables resulting of heat stacking"""
206     settings = {}
207     settings['kuryr_openstack_pod_subnet_id'] = data['pod_subnet']
208     settings['kuryr_openstack_worker_nodes_subnet_id'] = data['vm_subnet']
209     settings['kuryr_openstack_service_subnet_id'] = data['service_subnet']
210     settings['kuryr_openstack_pod_sg_id'] = data['pod_access_sg_id']
211     settings['kuryr_openstack_pod_project_id'] = (
212         cloud_client.current_project_id)
213     settings['kuryr_openstack_api_lb_ip'] = data['private_api_ip']
214 
215     settings['kuryr_openstack_auth_url'] = cloud_client.auth['auth_url']
216     settings['kuryr_openstack_username'] = cloud_client.auth['username']
217     settings['kuryr_openstack_password'] = cloud_client.auth['password']
218     if 'user_domain_id' in cloud_client.auth:
219         settings['kuryr_openstack_user_domain_name'] = (
220             cloud_client.auth['user_domain_id'])
221     else:
222         settings['kuryr_openstack_user_domain_name'] = (
223             cloud_client.auth['user_domain_name'])
224     # FIXME(apuimedo): consolidate kuryr controller credentials into the same
225     #                  vars the openstack playbook uses.
226     settings['kuryr_openstack_project_id'] = cloud_client.current_project_id
227     if 'project_domain_id' in cloud_client.auth:
228         settings['kuryr_openstack_project_domain_name'] = (
229             cloud_client.auth['project_domain_id'])
230     else:
231         settings['kuryr_openstack_project_domain_name'] = (
232             cloud_client.auth['project_domain_name'])
233     return settings
234 
235 
236 if __name__ == '__main__':
237     print(json.dumps(build_inventory(), indent=4, sort_keys=True))
238
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/playbooks/openstack/inventory.py b/playbooks/openstack/inventory.py
--- a/playbooks/openstack/inventory.py
+++ b/playbooks/openstack/inventory.py
@@ -9,7 +9,6 @@
 
 from __future__ import print_function
 
-from collections import Mapping
 import json
 import os
 
@@ -105,13 +104,8 @@
     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
+    group_name = server.metadata.get('openshift_node_group_name')
+    hostvars['openshift_node_group_name'] = group_name
 
     # check for attached docker storage volumes
     if 'os-extended-volumes:volumes_attached' in server:

{"golden_diff": "diff --git a/playbooks/openstack/inventory.py b/playbooks/openstack/inventory.py\n--- a/playbooks/openstack/inventory.py\n+++ b/playbooks/openstack/inventory.py\n@@ -9,7 +9,6 @@\n \n from __future__ import print_function\n \n-from collections import Mapping\n import json\n import os\n \n@@ -105,13 +104,8 @@\n     if server.metadata['host-type'] == 'cns':\n         hostvars['glusterfs_devices'] = ['/dev/nvme0n1']\n \n-    node_labels = server.metadata.get('node_labels')\n-    # NOTE(shadower): the node_labels value must be a dict not string\n-    if not isinstance(node_labels, Mapping):\n-        node_labels = json.loads(node_labels)\n-\n-    if node_labels:\n-        hostvars['openshift_node_labels'] = node_labels\n+    group_name = server.metadata.get('openshift_node_group_name')\n+    hostvars['openshift_node_group_name'] = group_name\n \n     # check for attached docker storage volumes\n     if 'os-extended-volumes:volumes_attached' in server:\n", "issue": "Fix multimaster OpenStack deployment failure\nWhen trying to deploy multimaster OpenStack with nsupdate and either no\r\nload balancer or OpenStack LBaaS, the creation of the DNS records would\r\nfail.\r\n\r\nThis is because it was still relying on the old `lb` inventory group for\r\nall multimaster scenarios, rather than just in the\r\n`openshift_openstack_use_lbaas_load_balancer` case.\r\n\r\nSo we define a new inventory variable\r\n`openshift_openstack_private_api_ip` (which is already defined in the\r\nHeat templates) and use that instead.\r\n\r\nFixes: https://bugzilla.redhat.com/show_bug.cgi?id=1588428\n", "before_files": [{"content": "#!/usr/bin/env python\n\"\"\"\nThis is an Ansible dynamic inventory for OpenStack.\n\nIt requires your OpenStack credentials to be set in clouds.yaml or your shell\nenvironment.\n\n\"\"\"\n\nfrom __future__ import print_function\n\nfrom collections import Mapping\nimport json\nimport os\n\nfrom keystoneauth1.exceptions.catalog import EndpointNotFound\nimport shade\n\n\ndef base_openshift_inventory(cluster_hosts):\n    '''Set the base openshift inventory.'''\n    inventory = {}\n\n    masters = [server.name for server in cluster_hosts\n               if server.metadata['host-type'] == 'master']\n\n    etcd = [server.name for server in cluster_hosts\n            if server.metadata['host-type'] == 'etcd']\n    if not etcd:\n        etcd = masters\n\n    infra_hosts = [server.name for server in cluster_hosts\n                   if server.metadata['host-type'] == 'node' and\n                   server.metadata['sub-host-type'] == 'infra']\n\n    app = [server.name for server in cluster_hosts\n           if server.metadata['host-type'] == 'node' and\n           server.metadata['sub-host-type'] == 'app']\n\n    cns = [server.name for server in cluster_hosts\n           if server.metadata['host-type'] == 'cns']\n\n    nodes = list(set(masters + infra_hosts + app + cns))\n\n    dns = [server.name for server in cluster_hosts\n           if server.metadata['host-type'] == 'dns']\n\n    load_balancers = [server.name for server in cluster_hosts\n                      if server.metadata['host-type'] == 'lb']\n\n    osev3 = list(set(nodes + etcd + load_balancers))\n\n    inventory['cluster_hosts'] = {'hosts': [s.name for s in cluster_hosts]}\n    inventory['OSEv3'] = {'hosts': osev3, 'vars': {}}\n    inventory['masters'] = {'hosts': masters}\n    inventory['etcd'] = {'hosts': etcd}\n    inventory['nodes'] = {'hosts': nodes}\n    inventory['infra_hosts'] = {'hosts': infra_hosts}\n    inventory['app'] = {'hosts': app}\n    inventory['glusterfs'] = {'hosts': cns}\n    inventory['dns'] = {'hosts': dns}\n    inventory['lb'] = {'hosts': load_balancers}\n    inventory['localhost'] = {'ansible_connection': 'local'}\n\n    return inventory\n\n\ndef get_docker_storage_mountpoints(volumes):\n    '''Check volumes to see if they're being used for docker storage'''\n    docker_storage_mountpoints = {}\n    for volume in volumes:\n        if volume.metadata.get('purpose') == \"openshift_docker_storage\":\n            for attachment in volume.attachments:\n                if attachment.server_id in docker_storage_mountpoints:\n                    docker_storage_mountpoints[attachment.server_id].append(attachment.device)\n                else:\n                    docker_storage_mountpoints[attachment.server_id] = [attachment.device]\n    return docker_storage_mountpoints\n\n\ndef _get_hostvars(server, docker_storage_mountpoints):\n    ssh_ip_address = server.public_v4 or server.private_v4\n    hostvars = {\n        'ansible_host': ssh_ip_address\n    }\n\n    public_v4 = server.public_v4 or server.private_v4\n    if public_v4:\n        hostvars['public_v4'] = server.public_v4\n        hostvars['openshift_public_ip'] = server.public_v4\n    # TODO(shadower): what about multiple networks?\n    if server.private_v4:\n        hostvars['private_v4'] = server.private_v4\n        hostvars['openshift_ip'] = server.private_v4\n\n        # NOTE(shadower): Yes, we set both hostname and IP to the private\n        # IP address for each node. OpenStack doesn't resolve nodes by\n        # name at all, so using a hostname here would require an internal\n        # DNS which would complicate the setup and potentially introduce\n        # performance issues.\n        hostvars['openshift_hostname'] = server.metadata.get(\n            'openshift_hostname', server.private_v4)\n    hostvars['openshift_public_hostname'] = server.name\n\n    if server.metadata['host-type'] == 'cns':\n        hostvars['glusterfs_devices'] = ['/dev/nvme0n1']\n\n    node_labels = server.metadata.get('node_labels')\n    # NOTE(shadower): the node_labels value must be a dict not string\n    if not isinstance(node_labels, Mapping):\n        node_labels = json.loads(node_labels)\n\n    if node_labels:\n        hostvars['openshift_node_labels'] = node_labels\n\n    # check for attached docker storage volumes\n    if 'os-extended-volumes:volumes_attached' in server:\n        if server.id in docker_storage_mountpoints:\n            hostvars['docker_storage_mountpoints'] = ' '.join(\n                docker_storage_mountpoints[server.id])\n    return hostvars\n\n\ndef build_inventory():\n    '''Build the dynamic inventory.'''\n    cloud = shade.openstack_cloud()\n\n    # TODO(shadower): filter the servers based on the `OPENSHIFT_CLUSTER`\n    # environment variable.\n    cluster_hosts = [\n        server for server in cloud.list_servers()\n        if 'metadata' in server and 'clusterid' in server.metadata]\n\n    inventory = base_openshift_inventory(cluster_hosts)\n\n    for server in cluster_hosts:\n        if 'group' in server.metadata:\n            group = server.metadata.get('group')\n            if group not in inventory:\n                inventory[group] = {'hosts': []}\n            inventory[group]['hosts'].append(server.name)\n\n    inventory['_meta'] = {'hostvars': {}}\n\n    # Some clouds don't have Cinder. That's okay:\n    try:\n        volumes = cloud.list_volumes()\n    except EndpointNotFound:\n        volumes = []\n\n    # cinder volumes used for docker storage\n    docker_storage_mountpoints = get_docker_storage_mountpoints(volumes)\n    for server in cluster_hosts:\n        inventory['_meta']['hostvars'][server.name] = _get_hostvars(\n            server,\n            docker_storage_mountpoints)\n\n    stout = _get_stack_outputs(cloud)\n    if stout is not None:\n        try:\n            inventory['localhost'].update({\n                'openshift_openstack_api_lb_provider':\n                stout['api_lb_provider'],\n                'openshift_openstack_api_lb_port_id':\n                stout['api_lb_vip_port_id'],\n                'openshift_openstack_api_lb_sg_id':\n                stout['api_lb_sg_id']})\n        except KeyError:\n            pass  # Not an API load balanced deployment\n\n        try:\n            inventory['OSEv3']['vars'][\n                'openshift_master_cluster_hostname'] = stout['private_api_ip']\n        except KeyError:\n            pass  # Internal LB not specified\n\n        inventory['localhost']['openshift_openstack_public_api_ip'] = \\\n            stout.get('public_api_ip')\n        inventory['localhost']['openshift_openstack_public_router_ip'] = \\\n            stout.get('public_router_ip')\n\n        try:\n            inventory['OSEv3']['vars'] = _get_kuryr_vars(cloud, stout)\n        except KeyError:\n            pass  # Not a kuryr deployment\n    return inventory\n\n\ndef _get_stack_outputs(cloud_client):\n    \"\"\"Returns a dictionary with the stack outputs\"\"\"\n    cluster_name = os.getenv('OPENSHIFT_CLUSTER', 'openshift-cluster')\n\n    stack = cloud_client.get_stack(cluster_name)\n    if stack is None or stack['stack_status'] not in (\n            'CREATE_COMPLETE', 'UPDATE_COMPLETE'):\n        return None\n\n    data = {}\n    for output in stack['outputs']:\n        data[output['output_key']] = output['output_value']\n    return data\n\n\ndef _get_kuryr_vars(cloud_client, data):\n    \"\"\"Returns a dictionary of Kuryr variables resulting of heat stacking\"\"\"\n    settings = {}\n    settings['kuryr_openstack_pod_subnet_id'] = data['pod_subnet']\n    settings['kuryr_openstack_worker_nodes_subnet_id'] = data['vm_subnet']\n    settings['kuryr_openstack_service_subnet_id'] = data['service_subnet']\n    settings['kuryr_openstack_pod_sg_id'] = data['pod_access_sg_id']\n    settings['kuryr_openstack_pod_project_id'] = (\n        cloud_client.current_project_id)\n    settings['kuryr_openstack_api_lb_ip'] = data['private_api_ip']\n\n    settings['kuryr_openstack_auth_url'] = cloud_client.auth['auth_url']\n    settings['kuryr_openstack_username'] = cloud_client.auth['username']\n    settings['kuryr_openstack_password'] = cloud_client.auth['password']\n    if 'user_domain_id' in cloud_client.auth:\n        settings['kuryr_openstack_user_domain_name'] = (\n            cloud_client.auth['user_domain_id'])\n    else:\n        settings['kuryr_openstack_user_domain_name'] = (\n            cloud_client.auth['user_domain_name'])\n    # FIXME(apuimedo): consolidate kuryr controller credentials into the same\n    #                  vars the openstack playbook uses.\n    settings['kuryr_openstack_project_id'] = cloud_client.current_project_id\n    if 'project_domain_id' in cloud_client.auth:\n        settings['kuryr_openstack_project_domain_name'] = (\n            cloud_client.auth['project_domain_id'])\n    else:\n        settings['kuryr_openstack_project_domain_name'] = (\n            cloud_client.auth['project_domain_name'])\n    return settings\n\n\nif __name__ == '__main__':\n    print(json.dumps(build_inventory(), indent=4, sort_keys=True))\n", "path": "playbooks/openstack/inventory.py"}], "after_files": [{"content": "#!/usr/bin/env python\n\"\"\"\nThis is an Ansible dynamic inventory for OpenStack.\n\nIt requires your OpenStack credentials to be set in clouds.yaml or your shell\nenvironment.\n\n\"\"\"\n\nfrom __future__ import print_function\n\nimport json\nimport os\n\nfrom keystoneauth1.exceptions.catalog import EndpointNotFound\nimport shade\n\n\ndef base_openshift_inventory(cluster_hosts):\n    '''Set the base openshift inventory.'''\n    inventory = {}\n\n    masters = [server.name for server in cluster_hosts\n               if server.metadata['host-type'] == 'master']\n\n    etcd = [server.name for server in cluster_hosts\n            if server.metadata['host-type'] == 'etcd']\n    if not etcd:\n        etcd = masters\n\n    infra_hosts = [server.name for server in cluster_hosts\n                   if server.metadata['host-type'] == 'node' and\n                   server.metadata['sub-host-type'] == 'infra']\n\n    app = [server.name for server in cluster_hosts\n           if server.metadata['host-type'] == 'node' and\n           server.metadata['sub-host-type'] == 'app']\n\n    cns = [server.name for server in cluster_hosts\n           if server.metadata['host-type'] == 'cns']\n\n    nodes = list(set(masters + infra_hosts + app + cns))\n\n    dns = [server.name for server in cluster_hosts\n           if server.metadata['host-type'] == 'dns']\n\n    load_balancers = [server.name for server in cluster_hosts\n                      if server.metadata['host-type'] == 'lb']\n\n    osev3 = list(set(nodes + etcd + load_balancers))\n\n    inventory['cluster_hosts'] = {'hosts': [s.name for s in cluster_hosts]}\n    inventory['OSEv3'] = {'hosts': osev3, 'vars': {}}\n    inventory['masters'] = {'hosts': masters}\n    inventory['etcd'] = {'hosts': etcd}\n    inventory['nodes'] = {'hosts': nodes}\n    inventory['infra_hosts'] = {'hosts': infra_hosts}\n    inventory['app'] = {'hosts': app}\n    inventory['glusterfs'] = {'hosts': cns}\n    inventory['dns'] = {'hosts': dns}\n    inventory['lb'] = {'hosts': load_balancers}\n    inventory['localhost'] = {'ansible_connection': 'local'}\n\n    return inventory\n\n\ndef get_docker_storage_mountpoints(volumes):\n    '''Check volumes to see if they're being used for docker storage'''\n    docker_storage_mountpoints = {}\n    for volume in volumes:\n        if volume.metadata.get('purpose') == \"openshift_docker_storage\":\n            for attachment in volume.attachments:\n                if attachment.server_id in docker_storage_mountpoints:\n                    docker_storage_mountpoints[attachment.server_id].append(attachment.device)\n                else:\n                    docker_storage_mountpoints[attachment.server_id] = [attachment.device]\n    return docker_storage_mountpoints\n\n\ndef _get_hostvars(server, docker_storage_mountpoints):\n    ssh_ip_address = server.public_v4 or server.private_v4\n    hostvars = {\n        'ansible_host': ssh_ip_address\n    }\n\n    public_v4 = server.public_v4 or server.private_v4\n    if public_v4:\n        hostvars['public_v4'] = server.public_v4\n        hostvars['openshift_public_ip'] = server.public_v4\n    # TODO(shadower): what about multiple networks?\n    if server.private_v4:\n        hostvars['private_v4'] = server.private_v4\n        hostvars['openshift_ip'] = server.private_v4\n\n        # NOTE(shadower): Yes, we set both hostname and IP to the private\n        # IP address for each node. OpenStack doesn't resolve nodes by\n        # name at all, so using a hostname here would require an internal\n        # DNS which would complicate the setup and potentially introduce\n        # performance issues.\n        hostvars['openshift_hostname'] = server.metadata.get(\n            'openshift_hostname', server.private_v4)\n    hostvars['openshift_public_hostname'] = server.name\n\n    if server.metadata['host-type'] == 'cns':\n        hostvars['glusterfs_devices'] = ['/dev/nvme0n1']\n\n    group_name = server.metadata.get('openshift_node_group_name')\n    hostvars['openshift_node_group_name'] = group_name\n\n    # check for attached docker storage volumes\n    if 'os-extended-volumes:volumes_attached' in server:\n        if server.id in docker_storage_mountpoints:\n            hostvars['docker_storage_mountpoints'] = ' '.join(\n                docker_storage_mountpoints[server.id])\n    return hostvars\n\n\ndef build_inventory():\n    '''Build the dynamic inventory.'''\n    cloud = shade.openstack_cloud()\n\n    # TODO(shadower): filter the servers based on the `OPENSHIFT_CLUSTER`\n    # environment variable.\n    cluster_hosts = [\n        server for server in cloud.list_servers()\n        if 'metadata' in server and 'clusterid' in server.metadata]\n\n    inventory = base_openshift_inventory(cluster_hosts)\n\n    for server in cluster_hosts:\n        if 'group' in server.metadata:\n            group = server.metadata.get('group')\n            if group not in inventory:\n                inventory[group] = {'hosts': []}\n            inventory[group]['hosts'].append(server.name)\n\n    inventory['_meta'] = {'hostvars': {}}\n\n    # Some clouds don't have Cinder. That's okay:\n    try:\n        volumes = cloud.list_volumes()\n    except EndpointNotFound:\n        volumes = []\n\n    # cinder volumes used for docker storage\n    docker_storage_mountpoints = get_docker_storage_mountpoints(volumes)\n    for server in cluster_hosts:\n        inventory['_meta']['hostvars'][server.name] = _get_hostvars(\n            server,\n            docker_storage_mountpoints)\n\n    stout = _get_stack_outputs(cloud)\n    if stout is not None:\n        try:\n            inventory['localhost'].update({\n                'openshift_openstack_api_lb_provider':\n                stout['api_lb_provider'],\n                'openshift_openstack_api_lb_port_id':\n                stout['api_lb_vip_port_id'],\n                'openshift_openstack_api_lb_sg_id':\n                stout['api_lb_sg_id']})\n        except KeyError:\n            pass  # Not an API load balanced deployment\n\n        try:\n            inventory['OSEv3']['vars'][\n                'openshift_master_cluster_hostname'] = stout['private_api_ip']\n        except KeyError:\n            pass  # Internal LB not specified\n\n        inventory['localhost']['openshift_openstack_public_api_ip'] = \\\n            stout.get('public_api_ip')\n        inventory['localhost']['openshift_openstack_public_router_ip'] = \\\n            stout.get('public_router_ip')\n\n        try:\n            inventory['OSEv3']['vars'] = _get_kuryr_vars(cloud, stout)\n        except KeyError:\n            pass  # Not a kuryr deployment\n    return inventory\n\n\ndef _get_stack_outputs(cloud_client):\n    \"\"\"Returns a dictionary with the stack outputs\"\"\"\n    cluster_name = os.getenv('OPENSHIFT_CLUSTER', 'openshift-cluster')\n\n    stack = cloud_client.get_stack(cluster_name)\n    if stack is None or stack['stack_status'] not in (\n            'CREATE_COMPLETE', 'UPDATE_COMPLETE'):\n        return None\n\n    data = {}\n    for output in stack['outputs']:\n        data[output['output_key']] = output['output_value']\n    return data\n\n\ndef _get_kuryr_vars(cloud_client, data):\n    \"\"\"Returns a dictionary of Kuryr variables resulting of heat stacking\"\"\"\n    settings = {}\n    settings['kuryr_openstack_pod_subnet_id'] = data['pod_subnet']\n    settings['kuryr_openstack_worker_nodes_subnet_id'] = data['vm_subnet']\n    settings['kuryr_openstack_service_subnet_id'] = data['service_subnet']\n    settings['kuryr_openstack_pod_sg_id'] = data['pod_access_sg_id']\n    settings['kuryr_openstack_pod_project_id'] = (\n        cloud_client.current_project_id)\n    settings['kuryr_openstack_api_lb_ip'] = data['private_api_ip']\n\n    settings['kuryr_openstack_auth_url'] = cloud_client.auth['auth_url']\n    settings['kuryr_openstack_username'] = cloud_client.auth['username']\n    settings['kuryr_openstack_password'] = cloud_client.auth['password']\n    if 'user_domain_id' in cloud_client.auth:\n        settings['kuryr_openstack_user_domain_name'] = (\n            cloud_client.auth['user_domain_id'])\n    else:\n        settings['kuryr_openstack_user_domain_name'] = (\n            cloud_client.auth['user_domain_name'])\n    # FIXME(apuimedo): consolidate kuryr controller credentials into the same\n    #                  vars the openstack playbook uses.\n    settings['kuryr_openstack_project_id'] = cloud_client.current_project_id\n    if 'project_domain_id' in cloud_client.auth:\n        settings['kuryr_openstack_project_domain_name'] = (\n            cloud_client.auth['project_domain_id'])\n    else:\n        settings['kuryr_openstack_project_domain_name'] = (\n            cloud_client.auth['project_domain_name'])\n    return settings\n\n\nif __name__ == '__main__':\n    print(json.dumps(build_inventory(), indent=4, sort_keys=True))\n", "path": "playbooks/openstack/inventory.py"}]}