summaryrefslogtreecommitdiff
path: root/scripts/emit_releases_file/emit_releases_file.py
blob: f18f417f3d306f0432d0ff684028dcc3ed78fd7c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
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('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


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)