inventory.py 7.9 KB

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