CI for the TripleO project
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

emit_releases_file.py 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. import argparse
  2. import logging
  3. import logging.handlers
  4. import os
  5. import re
  6. import requests
  7. import yaml
  8. # Define releases
  9. RELEASES = ['newton', 'ocata', 'pike', 'queens', 'master']
  10. # Define long term releases
  11. LONG_TERM_SUPPORT_RELEASES = ['queens']
  12. def get_relative_release(release, relative_idx):
  13. current_idx = RELEASES.index(release)
  14. absolute_idx = current_idx + relative_idx
  15. return RELEASES[absolute_idx]
  16. def setup_logging(log_file):
  17. '''Setup logging for the script'''
  18. logger = logging.getLogger('emit-releases')
  19. logger.setLevel(logging.DEBUG)
  20. log_handler = logging.handlers.WatchedFileHandler(
  21. os.path.expanduser(log_file))
  22. logger.addHandler(log_handler)
  23. def load_featureset_file(featureset_file):
  24. logger = logging.getLogger('emit-releases')
  25. try:
  26. with open(featureset_file, 'r') as stream:
  27. featureset = yaml.safe_load(stream)
  28. except Exception as e:
  29. logger.error("The featureset file: {} can not be "
  30. "opened.".format(featureset_file))
  31. logger.exception(e)
  32. raise e
  33. return featureset
  34. def get_dlrn_hash(release, hash_name, retries=10):
  35. logger = logging.getLogger('emit-releases')
  36. full_hash_pattern = re.compile('[a-z,0-9]{40}_[a-z,0-9]{8}')
  37. repo_url = ('https://trunk.rdoproject.org/centos7-%s/%s/delorean.repo' %
  38. (release, hash_name))
  39. for retry_num in range(retries):
  40. repo_file = None
  41. # Timeout if initial connection is longer than default
  42. # TCP packet retransmission window (3 secs), or if the
  43. # sending of the data takes more than 27 seconds.
  44. try:
  45. repo_file = requests.get(repo_url, timeout=(3.05, 27))
  46. except Exception as e:
  47. logger.exception(e)
  48. pass
  49. else:
  50. if repo_file is not None and repo_file.ok:
  51. break
  52. if repo_file is None or not repo_file.ok:
  53. raise RuntimeError("Failed to retrieve repo file from {} after "
  54. "{} retries".format(repo_url, retries))
  55. full_hash = full_hash_pattern.findall(repo_file.content)
  56. return full_hash[0]
  57. def compose_releases_dictionary(stable_release, featureset):
  58. logger = logging.getLogger('emit-releases')
  59. if stable_release not in RELEASES:
  60. raise RuntimeError("The {} release is not supported by this tool"
  61. "Supported releases: {}".format(
  62. stable_release, RELEASES))
  63. if (featureset.get('overcloud_upgrade') or
  64. featureset.get('undercloud_upgrade')) and \
  65. stable_release == RELEASES[0]:
  66. raise RuntimeError("Cannot upgrade to {}".format(RELEASES[0]))
  67. if featureset.get('overcloud_upgrade') and \
  68. featureset.get('undercloud_upgrade'):
  69. raise RuntimeError("This tool currently only supports upgrading the "
  70. "undercloud OR the overcloud NOT both.")
  71. if (featureset.get('overcloud_upgrade') or
  72. featureset.get('ffu_overcloud_upgrade')) and \
  73. not featureset.get('mixed_upgrade'):
  74. raise RuntimeError("Overcloud upgrade has to be mixed upgrades")
  75. if featureset.get('ffu_overcloud_upgrade') and \
  76. stable_release not in LONG_TERM_SUPPORT_RELEASES:
  77. raise RuntimeError(
  78. "{} is not a long-term support release, and cannot be "
  79. "used in a fast forward upgrade. Current long-term support "
  80. "releases: {}".format(stable_release, LONG_TERM_SUPPORT_RELEASES))
  81. releases_dictionary = {
  82. 'undercloud_install_release': stable_release,
  83. 'undercloud_install_hash': 'current-tripleo',
  84. 'undercloud_target_release': stable_release,
  85. 'undercloud_target_hash': 'current-tripleo',
  86. 'overcloud_deploy_release': stable_release,
  87. 'overcloud_deploy_hash': 'current-tripleo',
  88. 'overcloud_target_release': stable_release,
  89. 'overcloud_target_hash': 'current-tripleo'
  90. }
  91. if featureset.get('mixed_upgrade'):
  92. if featureset.get('overcloud_upgrade'):
  93. logger.info('Doing an overcloud upgrade')
  94. deploy_release = get_relative_release(stable_release, -1)
  95. releases_dictionary['overcloud_deploy_release'] = deploy_release
  96. elif featureset.get('ffu_overcloud_upgrade'):
  97. logger.info('Doing an overcloud fast forward upgrade')
  98. deploy_release = get_relative_release(stable_release, -3)
  99. releases_dictionary['overcloud_deploy_release'] = deploy_release
  100. elif featureset.get('undercloud_upgrade'):
  101. logger.info('Doing an undercloud upgrade')
  102. install_release = get_relative_release(stable_release, -1)
  103. releases_dictionary['undercloud_install_release'] = install_release
  104. elif featureset.get('overcloud_update'):
  105. logger.info('Doing an overcloud update')
  106. releases_dictionary['overcloud_deploy_hash'] = \
  107. 'previous-current-tripleo'
  108. logger.debug("stable_release: %s, featureset: %s", stable_release,
  109. featureset)
  110. logger.info('output releases: %s', releases_dictionary)
  111. return releases_dictionary
  112. def shim_convert_old_release_names(releases_names):
  113. # TODO(trown): Remove this shim when we no longer need to use the
  114. # old style double release files.
  115. oc_deploy_release = releases_names['overcloud_deploy_release']
  116. oc_target_release = releases_names['overcloud_target_release']
  117. uc_install_release = releases_names['undercloud_install_release']
  118. if oc_deploy_release != oc_target_release:
  119. release_file = "undercloud-{}-overcloud-{}".format(
  120. uc_install_release, oc_deploy_release)
  121. releases_names['undercloud_install_release'] = release_file
  122. releases_names['undercloud_target_release'] = release_file
  123. releases_names['overcloud_deploy_release'] = release_file
  124. releases_names['overcloud_target_release'] = release_file
  125. return releases_names
  126. def write_releases_dictionary_to_bash(releases_dictionary, bash_file_name):
  127. logger = logging.getLogger('emit-releases')
  128. # Make it deterministic, expected constants in the proper order
  129. try:
  130. bash_script = '''#!/bin/env bash
  131. export UNDERCLOUD_INSTALL_RELEASE="{undercloud_install_release}"
  132. export UNDERCLOUD_INSTALL_HASH="{undercloud_install_hash}"
  133. export UNDERCLOUD_TARGET_RELEASE="{undercloud_target_release}"
  134. export UNDERCLOUD_TARGET_HASH="{undercloud_target_hash}"
  135. export OVERCLOUD_DEPLOY_RELEASE="{overcloud_deploy_release}"
  136. export OVERCLOUD_DEPLOY_HASH="{overcloud_deploy_hash}"
  137. export OVERCLOUD_TARGET_RELEASE="{overcloud_target_release}"
  138. export OVERCLOUD_TARGET_HASH="{overcloud_target_hash}"
  139. '''.format(**releases_dictionary)
  140. with open(bash_file_name, 'w') as bash_file:
  141. bash_file.write(bash_script)
  142. except Exception:
  143. logger.exception("Writting releases dictionary")
  144. return False
  145. return True
  146. if __name__ == '__main__':
  147. default_log_file = '{}.log'.format(os.path.basename(__file__))
  148. default_output_file = '{}.out'.format(os.path.basename(__file__))
  149. parser = argparse.ArgumentParser(
  150. formatter_class=argparse.RawTextHelpFormatter,
  151. description='Get a dictionary of releases from a release '
  152. 'and a featureset file.')
  153. parser.add_argument('--stable-release',
  154. choices=RELEASES,
  155. required=True,
  156. help='Release that the change being tested is from.\n'
  157. 'All other releases are calculated from this\n'
  158. 'basis.')
  159. parser.add_argument('--featureset-file',
  160. required=True,
  161. help='Featureset file which will be introspected to\n'
  162. 'infer what type of upgrade is being performed\n'
  163. '(if any).')
  164. parser.add_argument('--output-file', default=default_output_file,
  165. help='Output file containing dictionary of releases\n'
  166. 'for the provided featureset and release.\n'
  167. '(default: %(default)s)')
  168. parser.add_argument('--log-file', default=default_log_file,
  169. help='log file to print debug information from\n'
  170. 'running the script.\n'
  171. '(default: %(default)s)')
  172. args = parser.parse_args()
  173. setup_logging(args.log_file)
  174. logger = logging.getLogger('emit-releases')
  175. featureset = load_featureset_file(args.featureset_file)
  176. releases_dictionary = compose_releases_dictionary(args.stable_release,
  177. featureset)
  178. releases_dictionary = shim_convert_old_release_names(releases_dictionary)
  179. if not write_releases_dictionary_to_bash(
  180. releases_dictionary, args.output_file):
  181. exit(1)