~openstack-charmers-next/charms/trusty/openstack-dashboard/trunk

« back to all changes in this revision

Viewing changes to hooks/charmhelpers/contrib/openstack/utils.py

  • Committer: James Page
  • Date: 2016-05-27 11:30:04 UTC
  • Revision ID: james.page@ubuntu.com-20160527113004-00q8evummknai3q6
Resync charm helpers

Add support for OpenStack Newton and Ocata.

Rework version detection code to just match on major version for
OpenStack projects using semantic versioning.

Provide fallback version detection based on major.minor versions
for swift packages.

Rework config-flags support helpers.

Fix is_ip function to correctly detect both IPv4 and IPv6 addresses.

Change-Id: Idc2a0bc60fa75fe96ca2cb83967c5434143ad0ef

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
import re
26
26
import itertools
27
27
import functools
 
28
import shutil
28
29
 
29
30
import six
30
31
import tempfile
46
47
    charm_dir,
47
48
    DEBUG,
48
49
    INFO,
 
50
    ERROR,
49
51
    related_units,
50
52
    relation_ids,
51
53
    relation_set,
82
84
from charmhelpers.fetch import apt_install, apt_cache, install_remote
83
85
from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk
84
86
from charmhelpers.contrib.storage.linux.loopback import ensure_loopback_device
 
87
from charmhelpers.contrib.openstack.exceptions import OSContextError
85
88
 
86
89
CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu"
87
90
CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA'
100
103
    ('vivid', 'kilo'),
101
104
    ('wily', 'liberty'),
102
105
    ('xenial', 'mitaka'),
 
106
    ('yakkety', 'newton'),
 
107
    ('zebra', 'ocata'),  # TODO: upload with real Z name
103
108
])
104
109
 
105
110
 
114
119
    ('2015.1', 'kilo'),
115
120
    ('2015.2', 'liberty'),
116
121
    ('2016.1', 'mitaka'),
 
122
    ('2016.2', 'newton'),
 
123
    ('2017.1', 'ocata'),
117
124
])
118
125
 
119
126
# The ugly duckling - must list releases oldest to newest
138
145
        ['2.3.0', '2.4.0', '2.5.0']),
139
146
    ('mitaka',
140
147
        ['2.5.0', '2.6.0', '2.7.0']),
 
148
    ('newton',
 
149
        ['2.8.0']),
141
150
])
142
151
 
143
152
# >= Liberty version->codename mapping
144
153
PACKAGE_CODENAMES = {
145
154
    'nova-common': OrderedDict([
146
 
        ('12.0', 'liberty'),
147
 
        ('13.0', 'mitaka'),
 
155
        ('12', 'liberty'),
 
156
        ('13', 'mitaka'),
 
157
        ('14', 'newton'),
 
158
        ('15', 'ocata'),
148
159
    ]),
149
160
    'neutron-common': OrderedDict([
150
 
        ('7.0', 'liberty'),
151
 
        ('8.0', 'mitaka'),
152
 
        ('8.1', 'mitaka'),
 
161
        ('7', 'liberty'),
 
162
        ('8', 'mitaka'),
 
163
        ('9', 'newton'),
 
164
        ('10', 'ocata'),
153
165
    ]),
154
166
    'cinder-common': OrderedDict([
155
 
        ('7.0', 'liberty'),
156
 
        ('8.0', 'mitaka'),
 
167
        ('7', 'liberty'),
 
168
        ('8', 'mitaka'),
 
169
        ('9', 'newton'),
 
170
        ('10', 'ocata'),
157
171
    ]),
158
172
    'keystone': OrderedDict([
159
 
        ('8.0', 'liberty'),
160
 
        ('8.1', 'liberty'),
161
 
        ('9.0', 'mitaka'),
 
173
        ('8', 'liberty'),
 
174
        ('9', 'mitaka'),
 
175
        ('10', 'newton'),
 
176
        ('11', 'ocata'),
162
177
    ]),
163
178
    'horizon-common': OrderedDict([
164
 
        ('8.0', 'liberty'),
165
 
        ('9.0', 'mitaka'),
 
179
        ('8', 'liberty'),
 
180
        ('9', 'mitaka'),
 
181
        ('10', 'newton'),
 
182
        ('11', 'ocata'),
166
183
    ]),
167
184
    'ceilometer-common': OrderedDict([
168
 
        ('5.0', 'liberty'),
169
 
        ('6.0', 'mitaka'),
 
185
        ('5', 'liberty'),
 
186
        ('6', 'mitaka'),
 
187
        ('7', 'newton'),
 
188
        ('8', 'ocata'),
170
189
    ]),
171
190
    'heat-common': OrderedDict([
172
 
        ('5.0', 'liberty'),
173
 
        ('6.0', 'mitaka'),
 
191
        ('5', 'liberty'),
 
192
        ('6', 'mitaka'),
 
193
        ('7', 'newton'),
 
194
        ('8', 'ocata'),
174
195
    ]),
175
196
    'glance-common': OrderedDict([
176
 
        ('11.0', 'liberty'),
177
 
        ('12.0', 'mitaka'),
 
197
        ('11', 'liberty'),
 
198
        ('12', 'mitaka'),
 
199
        ('13', 'newton'),
 
200
        ('14', 'ocata'),
178
201
    ]),
179
202
    'openstack-dashboard': OrderedDict([
180
 
        ('8.0', 'liberty'),
181
 
        ('9.0', 'mitaka'),
 
203
        ('8', 'liberty'),
 
204
        ('9', 'mitaka'),
 
205
        ('10', 'newton'),
 
206
        ('11', 'ocata'),
182
207
    ]),
183
208
}
184
209
 
254
279
def get_swift_codename(version):
255
280
    '''Determine OpenStack codename that corresponds to swift version.'''
256
281
    codenames = [k for k, v in six.iteritems(SWIFT_CODENAMES) if version in v]
 
282
 
257
283
    if len(codenames) > 1:
258
284
        # If more than one release codename contains this version we determine
259
285
        # the actual codename based on the highest available install source.
265
291
                return codename
266
292
    elif len(codenames) == 1:
267
293
        return codenames[0]
 
294
 
 
295
    # NOTE: fallback - attempt to match with just major.minor version
 
296
    match = re.match('^(\d+)\.(\d+)', version)
 
297
    if match:
 
298
        major_minor_version = match.group(0)
 
299
        for codename, versions in six.iteritems(SWIFT_CODENAMES):
 
300
            for release_version in versions:
 
301
                if release_version.startswith(major_minor_version):
 
302
                    return codename
 
303
 
268
304
    return None
269
305
 
270
306
 
303
339
    if match:
304
340
        vers = match.group(0)
305
341
 
 
342
    # Generate a major version number for newer semantic
 
343
    # versions of openstack projects
 
344
    major_vers = vers.split('.')[0]
306
345
    # >= Liberty independent project versions
307
346
    if (package in PACKAGE_CODENAMES and
308
 
            vers in PACKAGE_CODENAMES[package]):
309
 
        return PACKAGE_CODENAMES[package][vers]
 
347
            major_vers in PACKAGE_CODENAMES[package]):
 
348
        return PACKAGE_CODENAMES[package][major_vers]
310
349
    else:
311
350
        # < Liberty co-ordinated project versions
312
351
        try:
466
505
            'mitaka': 'trusty-updates/mitaka',
467
506
            'mitaka/updates': 'trusty-updates/mitaka',
468
507
            'mitaka/proposed': 'trusty-proposed/mitaka',
 
508
            'newton': 'xenial-updates/newton',
 
509
            'newton/updates': 'xenial-updates/newton',
 
510
            'newton/proposed': 'xenial-proposed/newton',
469
511
        }
470
512
 
471
513
        try:
858
900
    return None
859
901
 
860
902
 
 
903
def git_generate_systemd_init_files(templates_dir):
 
904
    """
 
905
    Generate systemd init files.
 
906
 
 
907
    Generates and installs systemd init units and script files based on the
 
908
    *.init.in files contained in the templates_dir directory.
 
909
 
 
910
    This code is based on the openstack-pkg-tools package and its init
 
911
    script generation, which is used by the OpenStack packages.
 
912
    """
 
913
    for f in os.listdir(templates_dir):
 
914
        if f.endswith(".init.in"):
 
915
            init_in_file = f
 
916
            init_file = f[:-8]
 
917
            service_file = "{}.service".format(init_file)
 
918
 
 
919
            init_in_source = os.path.join(templates_dir, init_in_file)
 
920
            init_source = os.path.join(templates_dir, init_file)
 
921
            service_source = os.path.join(templates_dir, service_file)
 
922
 
 
923
            init_dest = os.path.join('/etc/init.d', init_file)
 
924
            service_dest = os.path.join('/lib/systemd/system', service_file)
 
925
 
 
926
            shutil.copyfile(init_in_source, init_source)
 
927
            with open(init_source, 'a') as outfile:
 
928
                template = '/usr/share/openstack-pkg-tools/init-script-template'
 
929
                with open(template) as infile:
 
930
                    outfile.write('\n\n{}'.format(infile.read()))
 
931
 
 
932
            cmd = ['pkgos-gen-systemd-unit', init_in_source]
 
933
            subprocess.check_call(cmd)
 
934
 
 
935
            if os.path.exists(init_dest):
 
936
                os.remove(init_dest)
 
937
            if os.path.exists(service_dest):
 
938
                os.remove(service_dest)
 
939
            shutil.move(init_source, init_dest)
 
940
            shutil.move(service_source, service_dest)
 
941
            os.chmod(init_dest, 0o755)
 
942
 
 
943
 
861
944
def os_workload_status(configs, required_interfaces, charm_func=None):
862
945
    """
863
946
    Decorator to set workload status based on complete contexts
1574
1657
                restart_functions)
1575
1658
        return wrapped_f
1576
1659
    return wrap
 
1660
 
 
1661
 
 
1662
def config_flags_parser(config_flags):
 
1663
    """Parses config flags string into dict.
 
1664
 
 
1665
    This parsing method supports a few different formats for the config
 
1666
    flag values to be parsed:
 
1667
 
 
1668
      1. A string in the simple format of key=value pairs, with the possibility
 
1669
         of specifying multiple key value pairs within the same string. For
 
1670
         example, a string in the format of 'key1=value1, key2=value2' will
 
1671
         return a dict of:
 
1672
 
 
1673
             {'key1': 'value1',
 
1674
              'key2': 'value2'}.
 
1675
 
 
1676
      2. A string in the above format, but supporting a comma-delimited list
 
1677
         of values for the same key. For example, a string in the format of
 
1678
         'key1=value1, key2=value3,value4,value5' will return a dict of:
 
1679
 
 
1680
             {'key1', 'value1',
 
1681
              'key2', 'value2,value3,value4'}
 
1682
 
 
1683
      3. A string containing a colon character (:) prior to an equal
 
1684
         character (=) will be treated as yaml and parsed as such. This can be
 
1685
         used to specify more complex key value pairs. For example,
 
1686
         a string in the format of 'key1: subkey1=value1, subkey2=value2' will
 
1687
         return a dict of:
 
1688
 
 
1689
             {'key1', 'subkey1=value1, subkey2=value2'}
 
1690
 
 
1691
    The provided config_flags string may be a list of comma-separated values
 
1692
    which themselves may be comma-separated list of values.
 
1693
    """
 
1694
    # If we find a colon before an equals sign then treat it as yaml.
 
1695
    # Note: limit it to finding the colon first since this indicates assignment
 
1696
    # for inline yaml.
 
1697
    colon = config_flags.find(':')
 
1698
    equals = config_flags.find('=')
 
1699
    if colon > 0:
 
1700
        if colon < equals or equals < 0:
 
1701
            return yaml.safe_load(config_flags)
 
1702
 
 
1703
    if config_flags.find('==') >= 0:
 
1704
        juju_log("config_flags is not in expected format (key=value)",
 
1705
                 level=ERROR)
 
1706
        raise OSContextError
 
1707
 
 
1708
    # strip the following from each value.
 
1709
    post_strippers = ' ,'
 
1710
    # we strip any leading/trailing '=' or ' ' from the string then
 
1711
    # split on '='.
 
1712
    split = config_flags.strip(' =').split('=')
 
1713
    limit = len(split)
 
1714
    flags = {}
 
1715
    for i in range(0, limit - 1):
 
1716
        current = split[i]
 
1717
        next = split[i + 1]
 
1718
        vindex = next.rfind(',')
 
1719
        if (i == limit - 2) or (vindex < 0):
 
1720
            value = next
 
1721
        else:
 
1722
            value = next[:vindex]
 
1723
 
 
1724
        if i == 0:
 
1725
            key = current
 
1726
        else:
 
1727
            # if this not the first entry, expect an embedded key.
 
1728
            index = current.rfind(',')
 
1729
            if index < 0:
 
1730
                juju_log("Invalid config value(s) at index %s" % (i),
 
1731
                         level=ERROR)
 
1732
                raise OSContextError
 
1733
            key = current[index + 1:]
 
1734
 
 
1735
        # Add to collection.
 
1736
        flags[key.strip(post_strippers)] = value.rstrip(post_strippers)
 
1737
 
 
1738
    return flags