227 lines
9.0 KiB
Python
227 lines
9.0 KiB
Python
import argparse
|
|
import logging
|
|
import logging.handlers
|
|
import os
|
|
import re
|
|
import requests
|
|
import yaml
|
|
|
|
# Define releases
|
|
RELEASES = ['newton', 'ocata', 'pike', 'queens', 'master']
|
|
# Define long term releases
|
|
LONG_TERM_SUPPORT_RELEASES = ['queens']
|
|
|
|
|
|
def get_relative_release(release, relative_idx):
|
|
current_idx = RELEASES.index(release)
|
|
absolute_idx = current_idx + relative_idx
|
|
return RELEASES[absolute_idx]
|
|
|
|
|
|
def setup_logging(log_file):
|
|
'''Setup logging for the script'''
|
|
logger = logging.getLogger('emit-releases')
|
|
logger.setLevel(logging.DEBUG)
|
|
log_handler = logging.handlers.WatchedFileHandler(
|
|
os.path.expanduser(log_file))
|
|
logger.addHandler(log_handler)
|
|
|
|
|
|
def load_featureset_file(featureset_file):
|
|
logger = logging.getLogger('emit-releases')
|
|
try:
|
|
with open(featureset_file, 'r') as stream:
|
|
featureset = yaml.safe_load(stream)
|
|
except Exception as e:
|
|
logger.error("The featureset file: {} can not be "
|
|
"opened.".format(featureset_file))
|
|
logger.exception(e)
|
|
raise e
|
|
return featureset
|
|
|
|
|
|
def get_dlrn_hash(release, hash_name, retries=10):
|
|
logger = logging.getLogger('emit-releases')
|
|
full_hash_pattern = re.compile('[a-z,0-9]{40}_[a-z,0-9]{8}')
|
|
repo_url = ('https://trunk.rdoproject.org/centos7-%s/%s/delorean.repo' %
|
|
(release, hash_name))
|
|
for retry_num in range(retries):
|
|
repo_file = None
|
|
# Timeout if initial connection is longer than default
|
|
# TCP packet retransmission window (3 secs), or if the
|
|
# sending of the data takes more than 27 seconds.
|
|
try:
|
|
repo_file = requests.get(repo_url, timeout=(3.05, 27))
|
|
except Exception as e:
|
|
logger.exception(e)
|
|
pass
|
|
else:
|
|
if repo_file is not None and repo_file.ok:
|
|
break
|
|
|
|
if repo_file is None or not repo_file.ok:
|
|
raise RuntimeError("Failed to retrieve repo file from {} after "
|
|
"{} retries".format(repo_url, retries))
|
|
|
|
full_hash = full_hash_pattern.findall(repo_file.content)
|
|
return full_hash[0]
|
|
|
|
|
|
def compose_releases_dictionary(stable_release, featureset):
|
|
logger = logging.getLogger('emit-releases')
|
|
if stable_release not in RELEASES:
|
|
raise RuntimeError("The {} release is not supported by this tool"
|
|
"Supported releases: {}".format(
|
|
stable_release, RELEASES))
|
|
|
|
if (featureset.get('overcloud_upgrade') or
|
|
featureset.get('undercloud_upgrade')) and \
|
|
stable_release == RELEASES[0]:
|
|
raise RuntimeError("Cannot upgrade to {}".format(RELEASES[0]))
|
|
|
|
if featureset.get('undercloud_upgrade') and stable_release == 'ocata':
|
|
raise RuntimeError("Undercloud upgrades are not supported from "
|
|
"newton to ocata")
|
|
|
|
if featureset.get('overcloud_upgrade') and \
|
|
featureset.get('undercloud_upgrade'):
|
|
raise RuntimeError("This tool currently only supports upgrading the "
|
|
"undercloud OR the overcloud NOT both.")
|
|
|
|
if (featureset.get('overcloud_upgrade') or
|
|
featureset.get('ffu_overcloud_upgrade')) and \
|
|
not featureset.get('mixed_upgrade'):
|
|
raise RuntimeError("Overcloud upgrade has to be mixed upgrades")
|
|
|
|
if featureset.get('ffu_overcloud_upgrade') and \
|
|
stable_release not in LONG_TERM_SUPPORT_RELEASES:
|
|
raise RuntimeError(
|
|
"{} is not a long-term support release, and cannot be "
|
|
"used in a fast forward upgrade. Current long-term support "
|
|
"releases: {}".format(stable_release, LONG_TERM_SUPPORT_RELEASES))
|
|
|
|
releases_dictionary = {
|
|
'undercloud_install_release': stable_release,
|
|
'undercloud_install_hash': 'current-tripleo',
|
|
'undercloud_target_release': stable_release,
|
|
'undercloud_target_hash': 'current-tripleo',
|
|
'overcloud_deploy_release': stable_release,
|
|
'overcloud_deploy_hash': 'current-tripleo',
|
|
'overcloud_target_release': stable_release,
|
|
'overcloud_target_hash': 'current-tripleo'
|
|
}
|
|
|
|
if featureset.get('mixed_upgrade'):
|
|
if featureset.get('overcloud_upgrade'):
|
|
logger.info('Doing an overcloud upgrade')
|
|
deploy_release = get_relative_release(stable_release, -1)
|
|
releases_dictionary['overcloud_deploy_release'] = deploy_release
|
|
|
|
elif featureset.get('ffu_overcloud_upgrade'):
|
|
logger.info('Doing an overcloud fast forward upgrade')
|
|
deploy_release = get_relative_release(stable_release, -3)
|
|
releases_dictionary['overcloud_deploy_release'] = deploy_release
|
|
|
|
elif featureset.get('undercloud_upgrade'):
|
|
logger.info('Doing an undercloud upgrade')
|
|
install_release = get_relative_release(stable_release, -1)
|
|
releases_dictionary['undercloud_install_release'] = install_release
|
|
|
|
elif featureset.get('overcloud_update'):
|
|
logger.info('Doing an overcloud update')
|
|
releases_dictionary['overcloud_deploy_hash'] = \
|
|
'previous-current-tripleo'
|
|
|
|
logger.debug("stable_release: %s, featureset: %s", stable_release,
|
|
featureset)
|
|
|
|
logger.info('output releases: %s', releases_dictionary)
|
|
|
|
return releases_dictionary
|
|
|
|
|
|
def shim_convert_old_release_names(releases_names):
|
|
# TODO(trown): Remove this shim when we no longer need to use the
|
|
# old style double release files.
|
|
|
|
oc_deploy_release = releases_names['overcloud_deploy_release']
|
|
oc_target_release = releases_names['overcloud_target_release']
|
|
uc_install_release = releases_names['undercloud_install_release']
|
|
|
|
if oc_deploy_release != oc_target_release:
|
|
release_file = "undercloud-{}-overcloud-{}".format(
|
|
uc_install_release, oc_deploy_release)
|
|
releases_names['undercloud_install_release'] = release_file
|
|
releases_names['undercloud_target_release'] = release_file
|
|
releases_names['overcloud_deploy_release'] = release_file
|
|
releases_names['overcloud_target_release'] = release_file
|
|
|
|
return releases_names
|
|
|
|
|
|
def write_releases_dictionary_to_bash(releases_dictionary, bash_file_name):
|
|
logger = logging.getLogger('emit-releases')
|
|
# Make it deterministic, expected constants in the proper order
|
|
try:
|
|
bash_script = '''#!/bin/env bash
|
|
export UNDERCLOUD_INSTALL_RELEASE="{undercloud_install_release}"
|
|
export UNDERCLOUD_INSTALL_HASH="{undercloud_install_hash}"
|
|
export UNDERCLOUD_TARGET_RELEASE="{undercloud_target_release}"
|
|
export UNDERCLOUD_TARGET_HASH="{undercloud_target_hash}"
|
|
export OVERCLOUD_DEPLOY_RELEASE="{overcloud_deploy_release}"
|
|
export OVERCLOUD_DEPLOY_HASH="{overcloud_deploy_hash}"
|
|
export OVERCLOUD_TARGET_RELEASE="{overcloud_target_release}"
|
|
export OVERCLOUD_TARGET_HASH="{overcloud_target_hash}"
|
|
'''.format(**releases_dictionary)
|
|
with open(bash_file_name, 'w') as bash_file:
|
|
bash_file.write(bash_script)
|
|
except Exception:
|
|
logger.exception("Writting releases dictionary")
|
|
return False
|
|
return True
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
default_log_file = '{}.log'.format(os.path.basename(__file__))
|
|
default_output_file = '{}.out'.format(os.path.basename(__file__))
|
|
|
|
parser = argparse.ArgumentParser(
|
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
description='Get a dictionary of releases from a release '
|
|
'and a featureset file.')
|
|
parser.add_argument('--stable-release',
|
|
choices=RELEASES,
|
|
required=True,
|
|
help='Release that the change being tested is from.\n'
|
|
'All other releases are calculated from this\n'
|
|
'basis.')
|
|
parser.add_argument('--featureset-file',
|
|
required=True,
|
|
help='Featureset file which will be introspected to\n'
|
|
'infer what type of upgrade is being performed\n'
|
|
'(if any).')
|
|
parser.add_argument('--output-file', default=default_output_file,
|
|
help='Output file containing dictionary of releases\n'
|
|
'for the provided featureset and release.\n'
|
|
'(default: %(default)s)')
|
|
parser.add_argument('--log-file', default=default_log_file,
|
|
help='log file to print debug information from\n'
|
|
'running the script.\n'
|
|
'(default: %(default)s)')
|
|
args = parser.parse_args()
|
|
|
|
setup_logging(args.log_file)
|
|
logger = logging.getLogger('emit-releases')
|
|
|
|
featureset = load_featureset_file(args.featureset_file)
|
|
|
|
releases_dictionary = compose_releases_dictionary(args.stable_release,
|
|
featureset)
|
|
|
|
releases_dictionary = shim_convert_old_release_names(releases_dictionary)
|
|
|
|
if not write_releases_dictionary_to_bash(
|
|
releases_dictionary, args.output_file):
|
|
exit(1)
|