~ajkavanagh/openstack-mojo-specs/remove-cinder-from-vrrp-ha

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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
#!/usr/bin/env python
import logging
import os
import string
import sys
import yaml

# LOGLEVEL = logging.DEBUG
# LOGLEVEL = logging.INFO
LOGLEVEL = logging.WARN

WHITELIST = ['manifest', 'helper', 'utils', 'scripts', 'SPEC_INFO.txt']

YAML_MAP = {
    'images.yaml': 'image_setup.py',
    'keystone_users.yaml': 'keystone_setup.py',
    'network.yaml': 'network_setup.py',
    'tempest_expected_results.yaml': 'tempest_test.py',
}


# OpenStack-to-Ubuntu Release Map
UBUNTU_RELEASES = {}
OPENSTACK_RELEASES = {
    'icehouse': ['trusty'],
    'juno': ['trusty'],
    'kilo': ['trusty'],
    'liberty': ['trusty'],
    'mitaka': ['trusty', 'xenial'],
    'newton': ['xenial'],
    'ocata': ['xenial', 'zesty'],
    'pike': ['xenial', 'artful'],
}


# Helpers
def invert_dict_of_lists(d):
    """Inverts a dictionary of lists."""
    _inverted = {}
    for k, v in d.iteritems():
        for item in v:
            if item in _inverted.keys():
                _inverted[item].append(k)
            else:
                _inverted[item] = [k]
    return _inverted


def get_directive_opt(line, opt):
    """Return value for kv pair in manifest line"""
    for word in line:
        if opt in word:
            return word.split('=')[1]


def get_manifest_ubuntu_release():
    """Return Ubuntu release referenced in deploy manifest directive"""
    releases = []
    with open('manifest', 'r') as f:
        for line in f.readlines():
            words = line.split()
            if words and words[0] == "deploy":
                target_arg = get_directive_opt(words, 'target')
                if target_arg:
                    releases.append(target_arg)
                else:
                    logging.warn('No target is set for juju deployer, this '
                                 'spec will not support multiple Ubuntu '
                                 'releases, specify a target with '
                                 '${MOJO_SERIES}')
    logging.debug('Releases: {}'.format(releases))
    return releases


def get_manifest_referenced(openstack_release):
    """Return all files referenced in the manifest"""
    config_files = []
    with open('manifest', 'r') as f:
        for line in f.readlines():
            for word in line.split():
                if 'config=' in word:
                    config_files.extend(resolve_file(word.split('=')[1],
                                                     openstack_release))
    logging.debug('Expected files: {}'.format(config_files))
    return config_files


def resolve_file(rfile, openstack_release):
    """Substitute vars into string and return list of possibilities"""
    check_files = []
    if 'MOJO_SERIES' in rfile:
        # Add expected Ubuntu series based on OpenStack release
        if openstack_release:
            logging.debug('Expecting {} x {}'.format(
                rfile, OPENSTACK_RELEASES[openstack_release]))
            for series in OPENSTACK_RELEASES[openstack_release]:
                template_data = {'MOJO_SERIES': series}
                check_files.append(
                    string.Template(rfile).substitute(template_data))
        else:
            logging.warn('Skipping {}'.format(rfile))
    else:
        check_files.append(rfile)
    return check_files


def get_openstack_release_name():
    """Return OpenStack release name based on the current directory."""
    openstack_release = os.getcwd().split('/')[-1]
    logging.debug('OpenStack release: {}'.format(openstack_release))
    if openstack_release not in OPENSTACK_RELEASES.keys():
        logging.error('Unable to determine OpenStack release name '
                      'from spec directory')
        return None
    else:
        return openstack_release


# Checks
def check_manifest_ubuntu_release():
    """Check Ubuntu release is not hardcoded in manifest"""
    logging.info('Checking that Ubuntu release is not '
                 'hard-coded in manifest...')
    for target_arg in get_manifest_ubuntu_release():
        for rel in UBUNTU_RELEASES.keys():
            if rel in target_arg:
                logging.warn('Manifest deploy target contains a hard-coded '
                             'Ubuntu release. Consider using ${MOJO_SERIES}')


def check_manifest_no_wait(openstack_release):
    """Check that manifests do not use Mojo built-in wait feature"""
    logging.info('Checking that manifests do not use built-in wait feature')
    with open('manifest', 'r') as f:
        for line in f.readlines():
            words = line.split()
            if words and words[0] == "deploy" and "wait=True" in words:
                logging.error('Manifest calls built-in Mojo wait, which is '
                              'not allowed in these specs.')


def check_spec_info(dir_list):
    """Check spec has a SPEC_INFO.txt file describing the spec"""
    if 'SPEC_INFO.txt' not in dir_list:
        logging.warn('No spec description file SPEC_INFO.txt')


def check_files_are_in_manifest(dir_list, openstack_release):
    """Check that all files are referenced in the manifest"""
    logging.info('Checking that all files are referenced in the manifest...')
    _referenced = get_manifest_referenced(openstack_release)

    def _manifest_check(fname):
        if fname not in _referenced:
            logging.warn('%s is not referenced by the manifest. '
                         'Can this file be removed?' % fname)

    for f in dir_list:
        if f in WHITELIST:
            continue
        if f in YAML_MAP.keys():
            _manifest_check(YAML_MAP[f])
        else:
            _manifest_check(f)


def check_files_from_manifest(dir_list, openstack_release):
    """Check that all files referenced in the manifest are present"""
    logging.info('Checking that all files referenced in the '
                 'manifest are present...')

    for f in get_manifest_referenced(openstack_release):
        # Use actual file check to address file within symlinked directories
        if f not in dir_list and not os.path.exists(f):
            logging.error('%s not found.  It is referenced by the '
                          'manifest, or expected based on Ubuntu-'
                          'OpenStack version table.  Mojo spec '
                          'will fail.' % f)
            logging.debug('{} not in {}'.format(f, dir_list))
        for yamlfile in YAML_MAP.keys():
            if f == YAML_MAP[yamlfile]:
                if yamlfile not in dir_list:
                    logging.error('%s not found.  It is referenced by the '
                                  'manifest, or expected based on Ubuntu-'
                                  'OpenStack version table.  Mojo spec '
                                  'will fail.' % f)


def check_local_changes(dir_list):
    """Check for local changes"""
    logging.info('Checking for local files...')
    for f in dir_list:
        if not os.path.islink(f) and f not in WHITELIST:
            logging.warn('Spec file %s is a local copy. Can this be replaced '
                         'with a link to a helper copy?' % f)


def check_dead_symlinks(dir_list):
    """Check for dead symlink"""
    logging.info('Checking for dead symlinks...')
    for f in dir_list:
        if os.path.islink(f) and not os.path.exists(os.readlink(f)):
            logging.error('%s is a dead symlink' % f)


def check_yaml_syntax(dir_list):
    """Check yamls are valid"""
    logging.info('Checking syntax of yaml files...')
    for f in dir_list:
        if f.endswith('.yaml'):
            stream = open(f, 'r')
            try:
                yaml.load(stream)
            except yaml.scanner.ScannerError:
                logging.error('%s contains yaml errors, '
                              'mojo spec will fail.' % f)


def check_dirname(openstack_release):
    """Check tip dirname matches Openstack release referenced in manifest"""
    logging.info('Checking dir name matches OpenStack release in manifest...')
    for target in get_manifest_ubuntu_release():
        if target.split('-')[1] != openstack_release:
            logging.warn('OpenStack series referenced in manifest'
                         'does not match directory name')


def main(argv):
    logging.basicConfig(level=LOGLEVEL)

    # Ubuntu-to-OpenStack Release Map
    global UBUNTU_RELEASES
    UBUNTU_RELEASES = invert_dict_of_lists(OPENSTACK_RELEASES)

    spec = sys.argv[1]
    os.chdir(spec)
    dir_list = os.listdir('.')
    openstack_release = get_openstack_release_name()

    logging.info('Spec: {}'.format(spec))
    logging.debug('Ubuntu release map:  {}'.format(UBUNTU_RELEASES))
    logging.debug('OpenStack release map:  {}'.format(OPENSTACK_RELEASES))
    logging.debug('Current dir: {}'.format(os.getcwd()))
    logging.debug('dir_list: {}'.format(dir_list))
    check_spec_info(dir_list)
    check_files_are_in_manifest(dir_list, openstack_release)
    check_files_from_manifest(dir_list, openstack_release)
    check_local_changes(dir_list)
    check_dead_symlinks(dir_list)
    check_manifest_ubuntu_release()
    check_yaml_syntax(dir_list)
    check_dirname(openstack_release)
    check_manifest_no_wait(openstack_release)


if __name__ == "__main__":
    sys.exit(main(sys.argv))