Advertisement
Guest User

Untitled

a guest
Aug 21st, 2016
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.60 KB | None | 0 0
  1. #!/usr/bin/env python
  2. #
  3. # Red Hat Satellite Dynamic Inventory for Ansible
  4. # Copyright 2016 by Fotis Gimian (MIT License)
  5. #
  6. # Set your inventory to point to this script and ensure the script is
  7. # executable. You'll need an INI file with Satellite details of the following
  8. # style (whereby you may specify one or more Satellite servers):
  9. #
  10. # [satellite]
  11. # base_url = <base-url>
  12. # username = <username>
  13. # password = <password>
  14. #
  15. # You may set the SATELLITE_SETTINGS environment variable to the location of
  16. # this ini file or it will assume satellite.ini by default.
  17. #
  18. # You may target a specific Satellite instance by overriding the
  19. # SATELLITE_INSTANCE environment variable with the name of the instance you're
  20. # interested in.
  21. #
  22. # This script should also function with Foreman, although it hasn't been
  23. # explicitly tested against Foreman.
  24. #
  25. import argparse
  26. from datetime import datetime
  27. import ConfigParser
  28. import json
  29. import os
  30.  
  31. import requests
  32. from requests.packages.urllib3.exceptions import InsecureRequestWarning
  33.  
  34. # The location of the cached inventory
  35. CACHE_DIR = os.path.join(os.path.expanduser('~'), '.inventory_cache')
  36.  
  37. # The duration to cache for (in seconds)
  38. CACHE_TIME = 24 * 60 * 60 # 24 hours
  39.  
  40. # The number of items to retrieve per page from the Satellite API
  41. PER_PAGE = 500
  42.  
  43.  
  44. def process_hostgroup_name(hostgroup_name, instance):
  45. if hostgroup_name is None:
  46. return None
  47.  
  48. if instance is not None:
  49. hostgroup_name = instance + '/' + hostgroup_name
  50.  
  51. return hostgroup_name.replace(' ', '_')
  52.  
  53.  
  54. def main():
  55. # Setup CLI arguments and force the script only to be used with --list
  56. parser = argparse.ArgumentParser(
  57. description='Provides Ansible inventory from Red Hat Satellite'
  58. )
  59. parser.add_argument('--list', dest='list', action='store_true',
  60. required=True,
  61. help='list all hosts, hostgroups and their metadata')
  62. parser.parse_args()
  63.  
  64. # Determine the Satellite settings to use
  65. satellite_settings = os.environ.get('SATELLITE_SETTINGS', 'satellite.ini')
  66. satellite_instance = os.environ.get('SATELLITE_INSTANCE')
  67.  
  68. # Determine the cache filename
  69. cache_satellite_settings = os.path.basename(satellite_settings)
  70. if satellite_settings.endswith('.ini'):
  71. cache_satellite_settings = cache_satellite_settings[:-4]
  72.  
  73. if satellite_instance is None:
  74. cache_satellite_instance = 'all'
  75. else:
  76. cache_satellite_instance = satellite_instance.replace(' ', '_').lower()
  77.  
  78. cache_path = os.path.join(
  79. CACHE_DIR,
  80. cache_satellite_settings + '.' + cache_satellite_instance + '.cache'
  81. )
  82.  
  83. # Read the cached version if possible
  84. if os.path.isfile(cache_path):
  85. current_time = datetime.now()
  86. modified_time = datetime.fromtimestamp(os.path.getmtime(cache_path))
  87.  
  88. # If the cache file is still current, we use it instead of reaching
  89. # for the Satellite API
  90. if (current_time - modified_time).seconds < CACHE_TIME:
  91. try:
  92. with open(cache_path) as cache_fp:
  93. inventory = json.load(cache_fp)
  94. print json.dumps(inventory, indent=4)
  95. exit(0)
  96. except ValueError:
  97. pass
  98.  
  99. # Read Satellite settings
  100. instances = []
  101. config = ConfigParser.SafeConfigParser()
  102. try:
  103. config.read(satellite_settings)
  104. if satellite_instance:
  105. satellite_instances = [satellite_instance]
  106. else:
  107. satellite_instances = config.sections()
  108.  
  109. for satellite_instance in satellite_instances:
  110. base_url = config.get(satellite_instance, 'base_url')
  111. username = config.get(satellite_instance, 'username')
  112. password = config.get(satellite_instance, 'password')
  113. instances.append(
  114. (satellite_instance, base_url, username, password)
  115. )
  116. except ConfigParser.Error:
  117. print (
  118. 'error: unable to find or read all required files from '
  119. 'settings file {satellite_settings}'.format(
  120. satellite_settings=satellite_settings
  121. )
  122. )
  123. exit(1)
  124.  
  125. # Disable urllib3 SSL warnings
  126. requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  127.  
  128. content_type = 'application/json'
  129.  
  130. # Initialise our inventory dict
  131. inventory = {
  132. '_meta': {
  133. 'hostvars': {}
  134. }
  135. }
  136.  
  137. for instance, base_url, username, password in instances:
  138. # Setup our API with the provided URL and credentials
  139. session = requests.Session()
  140. session.auth = (username, password)
  141. session.verify = False
  142.  
  143. try:
  144. # Obtain hostgroups from Satellite
  145. page = 1
  146. while True:
  147. rv = session.get(
  148. base_url + '/api/v2/hostgroups',
  149. params={
  150. 'page': page,
  151. 'per_page': PER_PAGE
  152. },
  153. headers={
  154. 'Accept': content_type,
  155. 'Content-type': content_type
  156. }
  157. )
  158. rv.raise_for_status()
  159. hostgroups = rv.json()['results']
  160. if not hostgroups:
  161. break
  162. page += 1
  163.  
  164. for hostgroup in hostgroups:
  165. hostgroup_name = process_hostgroup_name(
  166. hostgroup['title'],
  167. instance if len(instances) > 1 else None
  168. )
  169. hostgroup.pop('title')
  170.  
  171. if hostgroup_name not in inventory:
  172. inventory[hostgroup_name] = {
  173. 'hosts': [],
  174. 'vars': {}
  175. }
  176.  
  177. inventory[hostgroup_name]['vars'] = hostgroup
  178.  
  179. # Obtain hosts from Satellite
  180. page = 1
  181. while True:
  182. rv = session.get(
  183. base_url + '/api/v2/hosts',
  184. params={
  185. 'page': page,
  186. 'per_page': PER_PAGE
  187. },
  188. headers={
  189. 'Accept': content_type,
  190. 'Content-type': content_type
  191. }
  192. )
  193. rv.raise_for_status()
  194. hosts = rv.json()['results']
  195. if not hosts:
  196. break
  197. page += 1
  198.  
  199. for host in hosts:
  200. hostgroup_name = process_hostgroup_name(
  201. host['hostgroup_name'],
  202. instance if len(instances) > 1 else None
  203. )
  204. hostname = host['name']
  205.  
  206. host.pop('hostgroup_name')
  207. host.pop('name')
  208.  
  209. if hostgroup_name not in inventory:
  210. inventory[hostgroup_name] = {
  211. 'hosts': [],
  212. 'vars': {}
  213. }
  214.  
  215. inventory[hostgroup_name]['hosts'].append(hostname)
  216. inventory['_meta']['hostvars'][hostname] = host
  217.  
  218. except (requests.RequestException, IndexError, ValueError):
  219. print 'error: unable to obtain host details from Satellite'
  220. exit(1)
  221. except KeyboardInterrupt:
  222. exit(2)
  223.  
  224. # Save the result to our cache
  225. if not os.path.exists(CACHE_DIR):
  226. os.makedirs(CACHE_DIR)
  227.  
  228. with open(cache_path, 'w') as cache_fp:
  229. json.dump(inventory, cache_fp)
  230.  
  231. print json.dumps(inventory, indent=4)
  232.  
  233.  
  234. if __name__ == '__main__':
  235. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement