puppet_950.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. # -*- coding: utf-8 -*-
  2. # Licensed under the Apache License, Version 2.0 (the "License");
  3. # you may not use this file except in compliance with the License.
  4. # You may obtain a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software
  9. # distributed under the License is distributed on an "AS IS" BASIS,
  10. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  11. # implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """
  15. Installs and configures Puppet
  16. """
  17. import sys
  18. import logging
  19. import os
  20. import time
  21. from packstack.installer import utils
  22. from packstack.installer import basedefs
  23. from packstack.installer.exceptions import PuppetError
  24. from packstack.installer.exceptions import ScriptRuntimeError
  25. from packstack.installer.utils import split_hosts
  26. from packstack.modules.common import filtered_hosts
  27. from packstack.modules.ospluginutils import appendManifestFile
  28. from packstack.modules.ospluginutils import generateHieraDataFile
  29. from packstack.modules.ospluginutils import getManifestTemplate
  30. from packstack.modules.ospluginutils import manifestfiles
  31. from packstack.modules.puppet import validate_logfile
  32. from packstack.modules.puppet import scan_logfile
  33. # ------------- Puppet Packstack Plugin Initialization --------------
  34. PLUGIN_NAME = "Puppet"
  35. PLUGIN_NAME_COLORED = utils.color_text(PLUGIN_NAME, 'blue')
  36. PUPPET_DIR = os.environ.get('PACKSTACK_PUPPETDIR',
  37. '/usr/share/openstack-puppet/')
  38. MODULE_DIR = os.path.join(PUPPET_DIR, 'modules')
  39. def initConfig(controller):
  40. group = {"GROUP_NAME": "PUPPET",
  41. "DESCRIPTION": "Puppet Config parameters",
  42. "PRE_CONDITION": lambda x: 'yes',
  43. "PRE_CONDITION_MATCH": "yes",
  44. "POST_CONDITION": False,
  45. "POST_CONDITION_MATCH": True}
  46. controller.addGroup(group, [])
  47. def initSequences(controller):
  48. puppetpresteps = [
  49. {'title': 'Clean Up', 'functions': [run_cleanup]},
  50. ]
  51. controller.insertSequence("Clean Up", [], [], puppetpresteps, index=0)
  52. puppetsteps = [
  53. {'title': 'Preparing Puppet manifests',
  54. 'functions': [prepare_puppet_modules]},
  55. {'title': 'Copying Puppet modules and manifests',
  56. 'functions': [copy_puppet_modules]},
  57. {'title': 'Applying Puppet manifests',
  58. 'functions': [apply_puppet_manifest]},
  59. {'title': 'Finalizing',
  60. 'functions': [finalize]}
  61. ]
  62. controller.addSequence("Puppet", [], [], puppetsteps)
  63. # ------------------------- helper functions -------------------------
  64. def wait_for_puppet(currently_running, messages):
  65. log_len = 0
  66. twirl = ["-", "\\", "|", "/"]
  67. while currently_running:
  68. for hostname, finished_logfile in currently_running:
  69. log_file = os.path.splitext(os.path.basename(finished_logfile))[0]
  70. if len(log_file) > log_len:
  71. log_len = len(log_file)
  72. if hasattr(sys.stdout, "isatty") and sys.stdout.isatty():
  73. twirl = twirl[-1:] + twirl[:-1]
  74. sys.stdout.write(("\rTesting if puppet apply is finished: %s"
  75. % log_file).ljust(40 + log_len))
  76. sys.stdout.write("[ %s ]" % twirl[0])
  77. sys.stdout.flush()
  78. try:
  79. # Once a remote puppet run has finished, we retrieve the log
  80. # file and check it for errors
  81. local_server = utils.ScriptRunner()
  82. log = os.path.join(basedefs.PUPPET_MANIFEST_DIR,
  83. os.path.basename(finished_logfile))
  84. log = log.replace(".finished", ".log")
  85. local_server.append('scp -o StrictHostKeyChecking=no '
  86. '-o UserKnownHostsFile=/dev/null '
  87. 'root@[%s]:%s %s'
  88. % (hostname, finished_logfile, log))
  89. # To not pollute logs we turn of logging of command execution
  90. local_server.execute(log=False)
  91. # If we got to this point the puppet apply has finished
  92. currently_running.remove((hostname, finished_logfile))
  93. # clean off the last "testing apply" msg
  94. if hasattr(sys.stdout, "isatty") and sys.stdout.isatty():
  95. sys.stdout.write(("\r").ljust(45 + log_len))
  96. except ScriptRuntimeError:
  97. # the test raises an exception if the file doesn't exist yet
  98. # TO-DO: We need to start testing 'e' for unexpected exceptions
  99. time.sleep(3)
  100. continue
  101. # check log file for relevant notices
  102. messages.extend(scan_logfile(log))
  103. # check the log file for errors
  104. sys.stdout.write('\r')
  105. try:
  106. validate_logfile(log)
  107. state = utils.state_message('%s:' % log_file, 'DONE', 'green')
  108. sys.stdout.write('%s\n' % state)
  109. sys.stdout.flush()
  110. except PuppetError:
  111. state = utils.state_message('%s:' % log_file, 'ERROR', 'red')
  112. sys.stdout.write('%s\n' % state)
  113. sys.stdout.flush()
  114. raise
  115. # -------------------------- step functions --------------------------
  116. def run_cleanup(config, messages):
  117. localserver = utils.ScriptRunner()
  118. localserver.append("rm -rf %s/*pp" % basedefs.PUPPET_MANIFEST_DIR)
  119. localserver.execute()
  120. def copy_puppet_modules(config, messages):
  121. os_modules = ' '.join(('aodh', 'apache', 'ceilometer', 'certmonger',
  122. 'cinder', 'concat', 'firewall', 'glance',
  123. 'gnocchi', 'heat', 'horizon', 'inifile', 'ironic',
  124. 'keystone', 'magnum', 'manila', 'memcached', 'mongodb',
  125. 'mysql', 'neutron', 'nova', 'nssdb', 'openstack',
  126. 'openstacklib', 'oslo', 'packstack', 'panko', 'rabbitmq',
  127. 'redis', 'remote', 'rsync', 'sahara', 'ssh',
  128. 'stdlib', 'swift', 'sysctl', 'tempest', 'trove',
  129. 'vcsrepo', 'vswitch', 'xinetd', ))
  130. # write puppet manifest to disk
  131. manifestfiles.writeManifests()
  132. # write hieradata file to disk
  133. generateHieraDataFile()
  134. server = utils.ScriptRunner()
  135. for hostname in filtered_hosts(config):
  136. host_dir = config['HOST_DETAILS'][hostname]['tmpdir']
  137. # copy hiera defaults.yaml file
  138. server.append("cd %s" % basedefs.HIERADATA_DIR)
  139. server.append("tar --dereference -cpzf - ../hieradata | "
  140. "ssh -o StrictHostKeyChecking=no "
  141. "-o UserKnownHostsFile=/dev/null "
  142. "root@%s tar -C %s -xpzf -" % (hostname, host_dir))
  143. # copy Packstack manifests
  144. server.append("cd %s/puppet" % basedefs.DIR_PROJECT_DIR)
  145. server.append("cd %s" % basedefs.PUPPET_MANIFEST_DIR)
  146. server.append("tar --dereference -cpzf - ../manifests | "
  147. "ssh -o StrictHostKeyChecking=no "
  148. "-o UserKnownHostsFile=/dev/null "
  149. "root@%s tar -C %s -xpzf -" % (hostname, host_dir))
  150. # copy resources
  151. resources = config.get('RESOURCES', {})
  152. for path, localname in resources.get(hostname, []):
  153. server.append("scp -o StrictHostKeyChecking=no "
  154. "-o UserKnownHostsFile=/dev/null "
  155. "%s root@[%s]:%s/resources/%s" %
  156. (path, hostname, host_dir, localname))
  157. # copy Puppet modules required by Packstack
  158. server.append("cd %s" % MODULE_DIR)
  159. server.append("tar --dereference -cpzf - %s | "
  160. "ssh -o StrictHostKeyChecking=no "
  161. "-o UserKnownHostsFile=/dev/null "
  162. "root@%s tar -C %s -xpzf -" %
  163. (os_modules, hostname,
  164. os.path.join(host_dir, 'modules')))
  165. server.execute()
  166. def apply_puppet_manifest(config, messages):
  167. if config.get("DRY_RUN"):
  168. return
  169. currently_running = []
  170. lastmarker = None
  171. loglevel = ''
  172. logcmd = False
  173. if logging.root.level <= logging.DEBUG:
  174. loglevel = '--debug'
  175. logcmd = True
  176. for manifest, marker in manifestfiles.getFiles():
  177. # if the marker has changed then we don't want to proceed until
  178. # all of the previous puppet runs have finished
  179. if lastmarker is not None and lastmarker != marker:
  180. wait_for_puppet(currently_running, messages)
  181. lastmarker = marker
  182. for hostname in filtered_hosts(config):
  183. if "%s_" % hostname not in manifest:
  184. continue
  185. host_dir = config['HOST_DETAILS'][hostname]['tmpdir']
  186. print("Applying %s" % manifest)
  187. server = utils.ScriptRunner(hostname)
  188. man_path = os.path.join(config['HOST_DETAILS'][hostname]['tmpdir'],
  189. basedefs.PUPPET_MANIFEST_RELATIVE,
  190. manifest)
  191. running_logfile = "%s.running" % man_path
  192. finished_logfile = "%s.finished" % man_path
  193. currently_running.append((hostname, finished_logfile))
  194. server.append("touch %s" % running_logfile)
  195. server.append("chmod 600 %s" % running_logfile)
  196. server.append("export PACKSTACK_VAR_DIR=%s" % host_dir)
  197. cmd = ("( flock %s/ps.lock "
  198. "puppet apply %s --modulepath %s/modules %s > %s "
  199. "2>&1 < /dev/null ; "
  200. "mv %s %s ) > /dev/null 2>&1 < /dev/null &"
  201. % (host_dir, loglevel, host_dir, man_path, running_logfile,
  202. running_logfile, finished_logfile))
  203. server.append(cmd)
  204. server.execute(log=logcmd)
  205. # wait for outstanding puppet runs before exiting
  206. wait_for_puppet(currently_running, messages)
  207. def prepare_puppet_modules(config, messages):
  208. network_hosts = split_hosts(config['CONFIG_NETWORK_HOSTS'])
  209. compute_hosts = split_hosts(config['CONFIG_COMPUTE_HOSTS'])
  210. manifestdata = getManifestTemplate("controller")
  211. manifestfile = "%s_controller.pp" % config['CONFIG_CONTROLLER_HOST']
  212. appendManifestFile(manifestfile, manifestdata, marker='controller')
  213. for host in network_hosts:
  214. manifestdata = getManifestTemplate("network")
  215. manifestfile = "%s_network.pp" % host
  216. appendManifestFile(manifestfile, manifestdata, marker='network')
  217. for host in compute_hosts:
  218. manifestdata = getManifestTemplate("compute")
  219. manifestfile = "%s_compute.pp" % host
  220. appendManifestFile(manifestfile, manifestdata, marker='compute')
  221. def finalize(config, messages):
  222. for hostname in filtered_hosts(config):
  223. server = utils.ScriptRunner(hostname)
  224. server.append("installed=$(rpm -q kernel --last | head -n1 | "
  225. "sed 's/kernel-\([a-z0-9\.\_\-]*\).*/\\1/g')")
  226. server.append("loaded=$(uname -r | head -n1)")
  227. server.append('[ "$loaded" == "$installed" ]')
  228. try:
  229. rc, out = server.execute()
  230. except ScriptRuntimeError:
  231. messages.append('Because of the kernel update the host %s '
  232. 'requires reboot.' % hostname)