UpgradeExecutor.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. #!/usr/bin/env python2.6
  2. '''
  3. Licensed to the Apache Software Foundation (ASF) under one
  4. or more contributor license agreements. See the NOTICE file
  5. distributed with this work for additional information
  6. regarding copyright ownership. The ASF licenses this file
  7. to you under the Apache License, Version 2.0 (the
  8. "License"); you may not use this file except in compliance
  9. with the License. You may obtain a copy of the License at
  10. http://www.apache.org/licenses/LICENSE-2.0
  11. Unless required by applicable law or agreed to in writing, software
  12. distributed under the License is distributed on an "AS IS" BASIS,
  13. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. See the License for the specific language governing permissions and
  15. limitations under the License.
  16. '''
  17. import json
  18. import os.path
  19. import logging
  20. import subprocess
  21. from manifestGenerator import generateManifest
  22. from RepoInstaller import RepoInstaller
  23. import pprint, threading
  24. from Grep import Grep
  25. from threading import Thread
  26. import shell
  27. import traceback
  28. from Grep import Grep
  29. from StackVersionsFileHandler import StackVersionsFileHandler
  30. import re
  31. logger = logging.getLogger()
  32. grep = Grep()
  33. class UpgradeExecutor:
  34. """ Class that performs the StackVersion stack upgrade"""
  35. SCRIPT_DIRS = [
  36. 'pre-upgrade.d',
  37. 'upgrade.d',
  38. 'post-upgrade.d'
  39. ]
  40. NAME_PARSING_FAILED_CODE = 999
  41. def __init__(self, pythonExecutor, puppetExecutor, config):
  42. self.pythonExecutor = pythonExecutor
  43. self.puppetExecutor = puppetExecutor
  44. self.stacksDir = config.get('stack', 'upgradeScriptsDir')
  45. self.config = config
  46. versionsFileDir = config.get('agent', 'prefix')
  47. self.versionsHandler = StackVersionsFileHandler(versionsFileDir)
  48. def perform_stack_upgrade(self, command, tmpout, tmperr):
  49. logger.info("Performing stack upgrade")
  50. params = command['commandParams']
  51. srcStack = params['source_stack_version']
  52. tgtStack = params['target_stack_version']
  53. component = command['component']
  54. srcStackTuple = self.split_stack_version(srcStack)
  55. tgtStackTuple = self.split_stack_version(srcStack)
  56. if srcStackTuple == None or tgtStackTuple == None:
  57. errorstr = "Source (%s) or target (%s) version does not match pattern \
  58. <Name>-<Version>" % (srcStack, tgtStack)
  59. logger.info(errorstr)
  60. result = {
  61. 'exitcode' : 1,
  62. 'stdout' : 'None',
  63. 'stderr' : errorstr
  64. }
  65. elif srcStack != tgtStack:
  66. paramTuple = sum((srcStackTuple, tgtStackTuple), ())
  67. upgradeId = "%s-%s.%s_%s-%s.%s" % paramTuple
  68. # Check stack version (do we need upgrade?)
  69. basedir = os.path.join(self.stacksDir, upgradeId, component)
  70. if not os.path.isdir(basedir):
  71. errorstr = "Upgrade %s is not supported" % upgradeId
  72. logger.error(errorstr)
  73. result = {
  74. 'exitcode' : 1,
  75. 'stdout' : errorstr,
  76. 'stderr' : errorstr
  77. }
  78. else:
  79. result = {
  80. 'exitcode' : 0,
  81. 'stdout' : '',
  82. 'stderr' : ''
  83. }
  84. for dir in self.SCRIPT_DIRS:
  85. if result['exitcode'] != 0:
  86. break
  87. tmpRes = self.execute_dir(command, basedir, dir, tmpout, tmperr)
  88. result = {
  89. 'exitcode' : result['exitcode'] or tmpRes['exitcode'],
  90. 'stdout' : "%s\n%s" % (result['stdout'], tmpRes['stdout']),
  91. 'stderr' : "%s\n%s" % (result['stderr'], tmpRes['stderr']),
  92. }
  93. if result['exitcode'] == 0:
  94. logger.info("Upgrade %s successfully finished" % upgradeId)
  95. self.versionsHandler.write_stack_version(component, tgtStack)
  96. else:
  97. infostr = "target_stack_version (%s) matches current stack version" \
  98. " for component %s, nothing to do" % (tgtStack, component)
  99. logger.info(infostr)
  100. result = {
  101. 'exitcode' : 0,
  102. 'stdout' : infostr,
  103. 'stderr' : 'None'
  104. }
  105. result = {
  106. 'exitcode' : result['exitcode'],
  107. 'stdout' : grep.tail(result['stdout'], grep.OUTPUT_LAST_LINES),
  108. 'stderr' : grep.tail(result['stderr'], grep.OUTPUT_LAST_LINES)
  109. }
  110. return result
  111. def get_key_func(self, name):
  112. """
  113. Returns a number from filenames like 70-foobar.* or 999 for not matching
  114. filenames
  115. """
  116. parts = name.split('-', 1)
  117. if not parts or not parts[0].isdigit():
  118. logger.warn("Can't parse script filename number %s" % name)
  119. return self.NAME_PARSING_FAILED_CODE # unknown element will be placed to the end of list
  120. return int(parts[0])
  121. def split_stack_version(self, verstr):
  122. matchObj = re.match( r'^(.*)-(\d+).(\d+)', verstr.strip(), re.M|re.I)
  123. stack_name = matchObj.group(1)
  124. stack_major_ver = matchObj.group(2)
  125. stack_minor_ver = matchObj.group(3)
  126. if matchObj:
  127. return stack_name, stack_major_ver, stack_minor_ver
  128. else:
  129. return None
  130. def execute_dir(self, command, basedir, dir, tmpout, tmperr):
  131. """
  132. Executes *.py and *.pp files located in a given directory.
  133. Files a executed in a numeric sorting order.
  134. """
  135. dirpath = os.path.join(basedir, dir)
  136. logger.info("Executing %s" % dirpath)
  137. if not os.path.isdir(dirpath):
  138. logger.warn("Script directory %s does not exist, skipping")
  139. return
  140. fileList=os.listdir(dirpath)
  141. fileList.sort(key = self.get_key_func)
  142. formattedResult = {
  143. 'exitcode' : 0,
  144. 'stdout' : '',
  145. 'stderr' : ''
  146. }
  147. for filename in fileList:
  148. prevcode = formattedResult['exitcode']
  149. if prevcode != 0 or self.get_key_func(filename) == self.NAME_PARSING_FAILED_CODE:
  150. break
  151. filepath = os.path.join(dirpath, filename)
  152. if filename.endswith(".pp"):
  153. logger.info("Running puppet file %s" % filepath)
  154. result = self.puppetExecutor.just_run_one_file(command, filename,
  155. tmpout, tmperr)
  156. elif filename.endswith(".py"):
  157. logger.info("Running python file %s" % filepath)
  158. result = self.pythonExecutor.run_file(filepath, tmpout, tmperr)
  159. elif filename.endswith(".pyc"):
  160. pass # skipping compiled files
  161. else:
  162. warnstr = "Unrecognized file type, skipping: %s" % filepath
  163. logger.warn(warnstr)
  164. result = {
  165. 'exitcode' : 0,
  166. 'stdout' : warnstr,
  167. 'stderr' : 'None'
  168. }
  169. formattedResult = {
  170. 'exitcode' : prevcode or result['exitcode'],
  171. 'stdout' : "%s\n%s" % (formattedResult['stdout'], result['stdout']),
  172. 'stderr' : "%s\n%s" % (formattedResult['stderr'], result['stderr']),
  173. }
  174. logger.debug("Result of %s: \n %s" % (dirpath, pprint.pformat(formattedResult)))
  175. return formattedResult