Add python script to dynamically compose releases

Currently, we do this in a combination of bash in this
repo and the release files themselves. However, as we begin
to support more different upgrade scenarios, this has proven
to be difficult to maintain.

Instead, we will use this python script to create a dictionary
of releases which describes which release will be used at each
point in the deploy: undercloud_install, undercloud_upgrade,
overcloud_deploy, and overcloud_upgrade.

This commit only provides an intial skeleton to work from.
Follow-up commits will provide:

  1. The ability to get a DLRN hash for each of the above breakpoints
  2. A CLI for the tool so it can be called from the TOCI scripts
  3. The ability to parse a featureset config for upgrade variables
  4. The ability to output the dictionary for consumption by TOCI scripts

Change-Id: Ibf61d7d12230f6714eb7dad91169aa043f5f8417
This commit is contained in:
John Trowbridge 2018-05-08 19:26:19 +00:00 committed by Enrique Llorente
parent 3801382fe5
commit d0bb823d0c
7 changed files with 420 additions and 0 deletions

10
.gitignore vendored
View File

@ -10,3 +10,13 @@ scripts/website/tripleo-docs/
scripts/website/tripleosphinx/
scripts/website/planet-2.0/
scripts/website/planet.html.tmplc
.tox/
.coverage
.pytest_cache/
__pycache__/
scripts/emit_releases_file/assets/
*.pyc
scripts/emit_releases_file/htmlcov/
scripts/emit_releases_file/pytest-report.html
tripleo_ci.egg-info/

View File

@ -0,0 +1,116 @@
import logging
RELEASES = ['newton', 'ocata', 'pike', 'queens', 'master']
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 compose_releases_dictionary(stable_release, featureset):
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('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'):
logging.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'):
logging.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'):
logging.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'):
logging.info('Doing an overcloud update')
releases_dictionary['overcloud_deploy_hash'] = \
'previous-current-tripleo'
logging.debug("stable_release: %s, featureset: %s", stable_release,
featureset)
logging.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
if __name__ == '__main__':
# TODO read the feature set from a file path passed in the arguments
featureset = {
'mixed_upgrade': True,
'overcloud_upgrade': True,
}
# TODO read this from an argumment
stable_release = 'queens'
releases_dictionary = compose_releases_dictionary(stable_release,
featureset)
releases_dictionary = shim_convert_old_release_names(
releases_dictionary)

View File

@ -0,0 +1,2 @@
[pytest]
log_level = debug

View File

@ -0,0 +1,76 @@
from emit_releases_file import shim_convert_old_release_names
def test_converting_from_oc_upgrade_has_double_release():
releases_name = {
'undercloud_install_release': 'master',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'master',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'queens',
'overcloud_deploy_hash': 'current-tripleo',
'overcloud_target_release': 'master',
'overcloud_target_hash': 'current-tripleo',
}
expected_releases_file = {
'undercloud_install_release': 'undercloud-master-overcloud-queens',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'undercloud-master-overcloud-queens',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'undercloud-master-overcloud-queens',
'overcloud_deploy_hash': 'current-tripleo',
'overcloud_target_release': 'undercloud-master-overcloud-queens',
'overcloud_target_hash': 'current-tripleo',
}
assert (shim_convert_old_release_names(releases_name) ==
expected_releases_file)
def test_converting_from_uc_upgrade_has_single_release():
releases_name = {
'undercloud_install_release': 'queens',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'master',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'master',
'overcloud_deploy_hash': 'current-tripleo',
'overcloud_target_release': 'master',
'overcloud_target_hash': 'current-tripleo',
}
expected_releases_file = {
'undercloud_install_release': 'queens',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'master',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'master',
'overcloud_deploy_hash': 'current-tripleo',
'overcloud_target_release': 'master',
'overcloud_target_hash': 'current-tripleo',
}
assert (shim_convert_old_release_names(releases_name) ==
expected_releases_file)
def test_converting_from_noop_has_single_release():
releases_name = {
'undercloud_install_release': 'master',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'master',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'master',
'overcloud_deploy_hash': 'current-tripleo',
'overcloud_target_release': 'master',
'overcloud_target_hash': 'current-tripleo',
}
expected_releases_file = {
'undercloud_install_release': 'master',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'master',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'master',
'overcloud_deploy_hash': 'current-tripleo',
'overcloud_target_release': 'master',
'overcloud_target_hash': 'current-tripleo',
}
assert (shim_convert_old_release_names(releases_name) ==
expected_releases_file)

View File

@ -0,0 +1,158 @@
from emit_releases_file import compose_releases_dictionary
import pytest
@pytest.mark.parametrize('stable_release,expected_releases',
[('master', {
'undercloud_install_release': 'master',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'master',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'queens',
'overcloud_deploy_hash': 'current-tripleo',
'overcloud_target_release': 'master',
'overcloud_target_hash': 'current-tripleo',
}), ('queens', {
'undercloud_install_release': 'queens',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'queens',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'pike',
'overcloud_deploy_hash': 'current-tripleo',
'overcloud_target_release': 'queens',
'overcloud_target_hash': 'current-tripleo',
}), ('pike', {
'undercloud_install_release': 'pike',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'pike',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'ocata',
'overcloud_deploy_hash': 'current-tripleo',
'overcloud_target_release': 'pike',
'overcloud_target_hash': 'current-tripleo',
})])
def test_overcloud_upgrade_is_n_minus_one_to_n(stable_release,
expected_releases):
featureset = {
'mixed_upgrade': True,
'overcloud_upgrade': True,
}
assert (compose_releases_dictionary(stable_release,
featureset) == expected_releases)
@pytest.mark.parametrize('stable_release,expected_releases', [
('queens', {
'undercloud_install_release': 'queens',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'queens',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'newton',
'overcloud_deploy_hash': 'current-tripleo',
'overcloud_target_release': 'queens',
'overcloud_target_hash': 'current-tripleo',
}),
])
def test_ffu_overcloud_upgrade_is_n_minus_three_to_n(stable_release,
expected_releases):
featureset = {
'mixed_upgrade': True,
'ffu_overcloud_upgrade': True,
}
assert (compose_releases_dictionary(stable_release,
featureset) == expected_releases)
@pytest.mark.parametrize('stable_release,expected_releases', [
('master', {
'undercloud_install_release': 'queens',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'master',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'master',
'overcloud_deploy_hash': 'current-tripleo',
'overcloud_target_release': 'master',
'overcloud_target_hash': 'current-tripleo',
}),
])
def test_undercloud_upgrade_is_n_minus_one_to_n(stable_release,
expected_releases):
featureset = {
'undercloud_upgrade': True,
}
assert (compose_releases_dictionary(stable_release,
featureset) == expected_releases)
@pytest.mark.parametrize(
'stable_release,expected_releases',
[('master', {
'undercloud_install_release': 'master',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'master',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'master',
'overcloud_deploy_hash': 'previous-current-tripleo',
'overcloud_target_release': 'master',
'overcloud_target_hash': 'current-tripleo',
}), ('queens', {
'undercloud_install_release': 'queens',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'queens',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'queens',
'overcloud_deploy_hash': 'previous-current-tripleo',
'overcloud_target_release': 'queens',
'overcloud_target_hash': 'current-tripleo',
})])
def test_overcloud_update_target_is_hash(stable_release, expected_releases):
featureset = {
'overcloud_update': True,
}
assert (compose_releases_dictionary(stable_release,
featureset) == expected_releases)
@pytest.mark.parametrize('stable_release,expected_releases',
[('master', {
'undercloud_install_release': 'master',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'master',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'master',
'overcloud_deploy_hash': 'current-tripleo',
'overcloud_target_release': 'master',
'overcloud_target_hash': 'current-tripleo',
}), ('queens', {
'undercloud_install_release': 'queens',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'queens',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'queens',
'overcloud_deploy_hash': 'current-tripleo',
'overcloud_target_release': 'queens',
'overcloud_target_hash': 'current-tripleo',
}), ('pike', {
'undercloud_install_release': 'pike',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'pike',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'pike',
'overcloud_deploy_hash': 'current-tripleo',
'overcloud_target_release': 'pike',
'overcloud_target_hash': 'current-tripleo',
}), ('ocata', {
'undercloud_install_release': 'ocata',
'undercloud_install_hash': 'current-tripleo',
'undercloud_target_release': 'ocata',
'undercloud_target_hash': 'current-tripleo',
'overcloud_deploy_release': 'ocata',
'overcloud_deploy_hash': 'current-tripleo',
'overcloud_target_release': 'ocata',
'overcloud_target_hash': 'current-tripleo',
})])
def test_noop_target_is_the_same(stable_release, expected_releases):
featureset = {}
assert (compose_releases_dictionary(stable_release,
featureset) == expected_releases)

View File

@ -0,0 +1,52 @@
from emit_releases_file import compose_releases_dictionary
import pytest
@pytest.mark.parametrize('featureset', [{
'mixed_upgrade': True,
'overcloud_upgrade': True
}, {
'undercloud_upgrade': True
}])
def test_upgrade_to_newton_is_unsupported(featureset):
stable_release = 'newton'
with pytest.raises(RuntimeError):
compose_releases_dictionary(stable_release, featureset)
def test_only_mixed_overcloud_upgrades_are_supported():
featureset = {
'overcloud_upgrade': True,
'undercloud_upgrade': True,
}
stable_release = 'queens'
with pytest.raises(RuntimeError):
compose_releases_dictionary(stable_release, featureset)
@pytest.mark.parametrize('upgrade_type',
['ffu_overcloud_upgrade', 'overcloud_upgrade'])
def test_overcloud_upgrades_has_to_be_mixed(upgrade_type):
featureset = {
upgrade_type: True,
}
stable_release = 'queens'
with pytest.raises(RuntimeError):
compose_releases_dictionary(stable_release, featureset)
@pytest.mark.parametrize('stable_release',
['ocata', 'pike', 'newton', 'master'])
def test_ffu_overcloud_upgrade_only_supported_from_newton(stable_release):
featureset = {
'mixed_upgrade': True,
'ffu_overcloud_upgrade': True,
}
with pytest.raises(RuntimeError):
compose_releases_dictionary(stable_release, featureset)
def test_fail_with_wrong_release():
with pytest.raises(RuntimeError):
compose_releases_dictionary('foobar', {})

View File

@ -1 +1,7 @@
flake8
pytest
pytest-html
pytest-cov
mock
requests
pprint