openshift_ansible.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. # pylint: disable=bad-continuation,missing-docstring,no-self-use,invalid-name,global-statement,global-variable-not-assigned
  2. from __future__ import (absolute_import, print_function)
  3. import socket
  4. import subprocess
  5. import sys
  6. import os
  7. import logging
  8. import yaml
  9. from ooinstall.variants import find_variant
  10. from ooinstall.utils import debug_env
  11. installer_log = logging.getLogger('installer')
  12. CFG = None
  13. ROLES_TO_GROUPS_MAP = {
  14. 'master': 'masters',
  15. 'node': 'nodes',
  16. 'etcd': 'etcd',
  17. 'storage': 'nfs',
  18. 'master_lb': 'lb'
  19. }
  20. VARIABLES_MAP = {
  21. 'ansible_ssh_user': 'ansible_ssh_user',
  22. 'deployment_type': 'deployment_type',
  23. 'variant_subtype': 'deployment_subtype',
  24. 'master_routingconfig_subdomain': 'openshift_master_default_subdomain',
  25. 'proxy_http': 'openshift_http_proxy',
  26. 'proxy_https': 'openshift_https_proxy',
  27. 'proxy_exclude_hosts': 'openshift_no_proxy',
  28. }
  29. HOST_VARIABLES_MAP = {
  30. 'ip': 'openshift_ip',
  31. 'public_ip': 'openshift_public_ip',
  32. 'hostname': 'openshift_hostname',
  33. 'public_hostname': 'openshift_public_hostname',
  34. 'containerized': 'containerized',
  35. }
  36. def set_config(cfg):
  37. global CFG
  38. CFG = cfg
  39. def generate_inventory(hosts):
  40. global CFG
  41. new_nodes = [host for host in hosts if host.is_node() and host.new_host]
  42. scaleup = len(new_nodes) > 0
  43. lb = determine_lb_configuration(hosts)
  44. base_inventory_path = CFG.settings['ansible_inventory_path']
  45. base_inventory = open(base_inventory_path, 'w')
  46. write_inventory_children(base_inventory, scaleup)
  47. write_inventory_vars(base_inventory, lb)
  48. # write_inventory_hosts
  49. for role in CFG.deployment.roles:
  50. # write group block
  51. group = ROLES_TO_GROUPS_MAP.get(role, role)
  52. base_inventory.write("\n[{}]\n".format(group))
  53. # write each host
  54. group_hosts = [host for host in hosts if role in host.roles]
  55. for host in group_hosts:
  56. schedulable = host.is_schedulable_node(hosts)
  57. write_host(host, role, base_inventory, schedulable)
  58. if scaleup:
  59. base_inventory.write('\n[new_nodes]\n')
  60. for node in new_nodes:
  61. write_host(node, 'new_nodes', base_inventory)
  62. base_inventory.close()
  63. return base_inventory_path
  64. def determine_lb_configuration(hosts):
  65. lb = next((host for host in hosts if host.is_master_lb()), None)
  66. if lb:
  67. if lb.hostname is None:
  68. lb.hostname = lb.connect_to
  69. lb.public_hostname = lb.connect_to
  70. return lb
  71. def write_inventory_children(base_inventory, scaleup):
  72. global CFG
  73. base_inventory.write('\n[OSEv3:children]\n')
  74. for role in CFG.deployment.roles:
  75. child = ROLES_TO_GROUPS_MAP.get(role, role)
  76. base_inventory.write('{}\n'.format(child))
  77. if scaleup:
  78. base_inventory.write('new_nodes\n')
  79. # pylint: disable=too-many-branches
  80. def write_inventory_vars(base_inventory, lb):
  81. global CFG
  82. base_inventory.write('\n[OSEv3:vars]\n')
  83. for variable, value in CFG.settings.items():
  84. inventory_var = VARIABLES_MAP.get(variable, None)
  85. if inventory_var and value:
  86. base_inventory.write('{}={}\n'.format(inventory_var, value))
  87. for variable, value in CFG.deployment.variables.items():
  88. inventory_var = VARIABLES_MAP.get(variable, variable)
  89. if value:
  90. base_inventory.write('{}={}\n'.format(inventory_var, value))
  91. if CFG.deployment.variables['ansible_ssh_user'] != 'root':
  92. base_inventory.write('ansible_become=yes\n')
  93. base_inventory.write('openshift_override_hostname_check=true\n')
  94. if lb is not None:
  95. base_inventory.write('openshift_master_cluster_method=native\n')
  96. base_inventory.write("openshift_master_cluster_hostname={}\n".format(lb.hostname))
  97. base_inventory.write(
  98. "openshift_master_cluster_public_hostname={}\n".format(lb.public_hostname))
  99. if CFG.settings.get('variant_version', None) == '3.1':
  100. # base_inventory.write('openshift_image_tag=v{}\n'.format(CFG.settings.get('variant_version')))
  101. base_inventory.write('openshift_image_tag=v{}\n'.format('3.1.1.6'))
  102. write_proxy_settings(base_inventory)
  103. # Find the correct deployment type for ansible:
  104. ver = find_variant(CFG.settings['variant'],
  105. version=CFG.settings.get('variant_version', None))[1]
  106. base_inventory.write('deployment_type={}\n'.format(ver.ansible_key))
  107. if getattr(ver, 'variant_subtype', False):
  108. base_inventory.write('deployment_subtype={}\n'.format(ver.deployment_subtype))
  109. if 'OO_INSTALL_ADDITIONAL_REGISTRIES' in os.environ:
  110. base_inventory.write('openshift_docker_additional_registries={}\n'.format(
  111. os.environ['OO_INSTALL_ADDITIONAL_REGISTRIES']))
  112. if 'OO_INSTALL_INSECURE_REGISTRIES' in os.environ:
  113. base_inventory.write('openshift_docker_insecure_registries={}\n'.format(
  114. os.environ['OO_INSTALL_INSECURE_REGISTRIES']))
  115. if 'OO_INSTALL_PUDDLE_REPO' in os.environ:
  116. # We have to double the '{' here for literals
  117. base_inventory.write("openshift_additional_repos=[{{'id': 'ose-devel', "
  118. "'name': 'ose-devel', "
  119. "'baseurl': '{}', "
  120. "'enabled': 1, 'gpgcheck': 0}}]\n".format(os.environ['OO_INSTALL_PUDDLE_REPO']))
  121. for name, role_obj in CFG.deployment.roles.items():
  122. if role_obj.variables:
  123. group_name = ROLES_TO_GROUPS_MAP.get(name, name)
  124. base_inventory.write("\n[{}:vars]\n".format(group_name))
  125. for variable, value in role_obj.variables.items():
  126. inventory_var = VARIABLES_MAP.get(variable, variable)
  127. if value:
  128. base_inventory.write('{}={}\n'.format(inventory_var, value))
  129. base_inventory.write("\n")
  130. def write_proxy_settings(base_inventory):
  131. try:
  132. base_inventory.write("openshift_http_proxy={}\n".format(
  133. CFG.settings['openshift_http_proxy']))
  134. except KeyError:
  135. pass
  136. try:
  137. base_inventory.write("openshift_https_proxy={}\n".format(
  138. CFG.settings['openshift_https_proxy']))
  139. except KeyError:
  140. pass
  141. try:
  142. base_inventory.write("openshift_no_proxy={}\n".format(
  143. CFG.settings['openshift_no_proxy']))
  144. except KeyError:
  145. pass
  146. def write_host(host, role, inventory, schedulable=None):
  147. global CFG
  148. if host.preconfigured:
  149. return
  150. facts = ''
  151. for prop in HOST_VARIABLES_MAP:
  152. if getattr(host, prop):
  153. facts += ' {}={}'.format(HOST_VARIABLES_MAP.get(prop), getattr(host, prop))
  154. if host.other_variables:
  155. for variable, value in host.other_variables.items():
  156. facts += " {}={}".format(variable, value)
  157. if host.node_labels and role == 'node':
  158. facts += ' openshift_node_labels="{}"'.format(host.node_labels)
  159. # Distinguish between three states, no schedulability specified (use default),
  160. # explicitly set to True, or explicitly set to False:
  161. if role != 'node' or schedulable is None:
  162. pass
  163. else:
  164. facts += " openshift_schedulable={}".format(schedulable)
  165. installer_host = socket.gethostname()
  166. if installer_host in [host.connect_to, host.hostname, host.public_hostname]:
  167. facts += ' ansible_connection=local'
  168. if os.geteuid() != 0:
  169. no_pwd_sudo = subprocess.call(['sudo', '-n', 'echo', '-n'])
  170. if no_pwd_sudo == 1:
  171. print('The atomic-openshift-installer requires sudo access without a password.')
  172. sys.exit(1)
  173. facts += ' ansible_become=yes'
  174. inventory.write('{} {}\n'.format(host.connect_to, facts))
  175. def load_system_facts(inventory_file, os_facts_path, env_vars, verbose=False):
  176. """
  177. Retrieves system facts from the remote systems.
  178. """
  179. installer_log.debug("Inside load_system_facts")
  180. installer_log.debug("load_system_facts will run with Ansible/Openshift environment variables:")
  181. debug_env(env_vars)
  182. FNULL = open(os.devnull, 'w')
  183. args = ['ansible-playbook', '-v'] if verbose \
  184. else ['ansible-playbook']
  185. args.extend([
  186. '--inventory-file={}'.format(inventory_file),
  187. os_facts_path])
  188. installer_log.debug("Going to subprocess out to ansible now with these args: %s", ' '.join(args))
  189. installer_log.debug("Subprocess will run with Ansible/Openshift environment variables:")
  190. debug_env(env_vars)
  191. status = subprocess.call(args, env=env_vars, stdout=FNULL)
  192. if status != 0:
  193. installer_log.debug("Exit status from subprocess was not 0")
  194. return [], 1
  195. with open(CFG.settings['ansible_callback_facts_yaml'], 'r') as callback_facts_file:
  196. installer_log.debug("Going to try to read this file: %s", CFG.settings['ansible_callback_facts_yaml'])
  197. try:
  198. callback_facts = yaml.safe_load(callback_facts_file)
  199. except yaml.YAMLError as exc:
  200. print("Error in {}".format(CFG.settings['ansible_callback_facts_yaml']), exc)
  201. print("Try deleting and rerunning the atomic-openshift-installer")
  202. sys.exit(1)
  203. return callback_facts, 0
  204. def default_facts(hosts, verbose=False):
  205. global CFG
  206. installer_log.debug("Current global CFG vars here: %s", CFG)
  207. inventory_file = generate_inventory(hosts)
  208. os_facts_path = '{}/playbooks/byo/openshift_facts.yml'.format(CFG.ansible_playbook_directory)
  209. facts_env = os.environ.copy()
  210. facts_env["OO_INSTALL_CALLBACK_FACTS_YAML"] = CFG.settings['ansible_callback_facts_yaml']
  211. facts_env["ANSIBLE_CALLBACK_PLUGINS"] = CFG.settings['ansible_plugins_directory']
  212. facts_env["OPENSHIFT_MASTER_CLUSTER_METHOD"] = 'native'
  213. if 'ansible_log_path' in CFG.settings:
  214. facts_env["ANSIBLE_LOG_PATH"] = CFG.settings['ansible_log_path']
  215. if 'ansible_config' in CFG.settings:
  216. facts_env['ANSIBLE_CONFIG'] = CFG.settings['ansible_config']
  217. installer_log.debug("facts_env: %s", facts_env)
  218. installer_log.debug("Going to 'load_system_facts' next")
  219. return load_system_facts(inventory_file, os_facts_path, facts_env, verbose)
  220. def run_main_playbook(inventory_file, hosts, hosts_to_run_on, verbose=False):
  221. global CFG
  222. if len(hosts_to_run_on) != len(hosts):
  223. main_playbook_path = os.path.join(CFG.ansible_playbook_directory,
  224. 'playbooks/byo/openshift-node/scaleup.yml')
  225. else:
  226. main_playbook_path = os.path.join(CFG.ansible_playbook_directory,
  227. 'playbooks/byo/openshift-cluster/config.yml')
  228. facts_env = os.environ.copy()
  229. if 'ansible_log_path' in CFG.settings:
  230. facts_env['ANSIBLE_LOG_PATH'] = CFG.settings['ansible_log_path']
  231. # override the ansible config for our main playbook run
  232. if 'ansible_quiet_config' in CFG.settings:
  233. facts_env['ANSIBLE_CONFIG'] = CFG.settings['ansible_quiet_config']
  234. return run_ansible(main_playbook_path, inventory_file, facts_env, verbose)
  235. def run_ansible(playbook, inventory, env_vars, verbose=False):
  236. installer_log.debug("run_ansible will run with Ansible/Openshift environment variables:")
  237. debug_env(env_vars)
  238. args = ['ansible-playbook', '-v'] if verbose \
  239. else ['ansible-playbook']
  240. args.extend([
  241. '--inventory-file={}'.format(inventory),
  242. playbook])
  243. installer_log.debug("Going to subprocess out to ansible now with these args: %s", ' '.join(args))
  244. return subprocess.call(args, env=env_vars)
  245. def run_uninstall_playbook(hosts, verbose=False):
  246. playbook = os.path.join(CFG.settings['ansible_playbook_directory'],
  247. 'playbooks/adhoc/uninstall.yml')
  248. inventory_file = generate_inventory(hosts)
  249. facts_env = os.environ.copy()
  250. if 'ansible_log_path' in CFG.settings:
  251. facts_env['ANSIBLE_LOG_PATH'] = CFG.settings['ansible_log_path']
  252. if 'ansible_config' in CFG.settings:
  253. facts_env['ANSIBLE_CONFIG'] = CFG.settings['ansible_config']
  254. # override the ansible config for our main playbook run
  255. if 'ansible_quiet_config' in CFG.settings:
  256. facts_env['ANSIBLE_CONFIG'] = CFG.settings['ansible_quiet_config']
  257. return run_ansible(playbook, inventory_file, facts_env, verbose)
  258. def run_upgrade_playbook(hosts, playbook, verbose=False):
  259. playbook = os.path.join(CFG.settings['ansible_playbook_directory'],
  260. 'playbooks/byo/openshift-cluster/upgrades/{}'.format(playbook))
  261. # TODO: Upgrade inventory for upgrade?
  262. inventory_file = generate_inventory(hosts)
  263. facts_env = os.environ.copy()
  264. if 'ansible_log_path' in CFG.settings:
  265. facts_env['ANSIBLE_LOG_PATH'] = CFG.settings['ansible_log_path']
  266. if 'ansible_config' in CFG.settings:
  267. facts_env['ANSIBLE_CONFIG'] = CFG.settings['ansible_config']
  268. # override the ansible config for our main playbook run
  269. if 'ansible_quiet_config' in CFG.settings:
  270. facts_env['ANSIBLE_CONFIG'] = CFG.settings['ansible_quiet_config']
  271. return run_ansible(playbook, inventory_file, facts_env, verbose)