~1chb1n/charms/trusty/nova-cloud-controller/15.10-stable-flip-tests-helper-syncs

« back to all changes in this revision

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

Check in start of py redux.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
 
 
3
# Common python helper functions used for OpenStack charms.
 
4
 
 
5
from collections import OrderedDict
 
6
 
 
7
import apt_pkg as apt
 
8
import subprocess
 
9
import os
 
10
import sys
 
11
 
 
12
from charmhelpers.core.hookenv import (
 
13
    config,
 
14
    log as juju_log,
 
15
    charm_dir,
 
16
)
 
17
 
 
18
from charmhelpers.core.host import (
 
19
    lsb_release,
 
20
    apt_install,
 
21
)
 
22
 
 
23
CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu"
 
24
CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA'
 
25
 
 
26
UBUNTU_OPENSTACK_RELEASE = OrderedDict([
 
27
    ('oneiric', 'diablo'),
 
28
    ('precise', 'essex'),
 
29
    ('quantal', 'folsom'),
 
30
    ('raring', 'grizzly'),
 
31
    ('saucy', 'havana'),
 
32
])
 
33
 
 
34
 
 
35
OPENSTACK_CODENAMES = OrderedDict([
 
36
    ('2011.2', 'diablo'),
 
37
    ('2012.1', 'essex'),
 
38
    ('2012.2', 'folsom'),
 
39
    ('2013.1', 'grizzly'),
 
40
    ('2013.2', 'havana'),
 
41
    ('2014.1', 'icehouse'),
 
42
])
 
43
 
 
44
# The ugly duckling
 
45
SWIFT_CODENAMES = {
 
46
    '1.4.3': 'diablo',
 
47
    '1.4.8': 'essex',
 
48
    '1.7.4': 'folsom',
 
49
    '1.7.6': 'grizzly',
 
50
    '1.7.7': 'grizzly',
 
51
    '1.8.0': 'grizzly',
 
52
    '1.9.0': 'havana',
 
53
    '1.9.1': 'havana',
 
54
}
 
55
 
 
56
 
 
57
def error_out(msg):
 
58
    juju_log("FATAL ERROR: %s" % msg, level='ERROR')
 
59
    sys.exit(1)
 
60
 
 
61
 
 
62
def get_os_codename_install_source(src):
 
63
    '''Derive OpenStack release codename from a given installation source.'''
 
64
    ubuntu_rel = lsb_release()['DISTRIB_CODENAME']
 
65
    rel = ''
 
66
    if src == 'distro':
 
67
        try:
 
68
            rel = UBUNTU_OPENSTACK_RELEASE[ubuntu_rel]
 
69
        except KeyError:
 
70
            e = 'Could not derive openstack release for '\
 
71
                'this Ubuntu release: %s' % ubuntu_rel
 
72
            error_out(e)
 
73
        return rel
 
74
 
 
75
    if src.startswith('cloud:'):
 
76
        ca_rel = src.split(':')[1]
 
77
        ca_rel = ca_rel.split('%s-' % ubuntu_rel)[1].split('/')[0]
 
78
        return ca_rel
 
79
 
 
80
    # Best guess match based on deb string provided
 
81
    if src.startswith('deb') or src.startswith('ppa'):
 
82
        for k, v in OPENSTACK_CODENAMES.iteritems():
 
83
            if v in src:
 
84
                return v
 
85
 
 
86
 
 
87
def get_os_version_install_source(src):
 
88
    codename = get_os_codename_install_source(src)
 
89
    return get_os_version_codename(codename)
 
90
 
 
91
 
 
92
def get_os_codename_version(vers):
 
93
    '''Determine OpenStack codename from version number.'''
 
94
    try:
 
95
        return OPENSTACK_CODENAMES[vers]
 
96
    except KeyError:
 
97
        e = 'Could not determine OpenStack codename for version %s' % vers
 
98
        error_out(e)
 
99
 
 
100
 
 
101
def get_os_version_codename(codename):
 
102
    '''Determine OpenStack version number from codename.'''
 
103
    for k, v in OPENSTACK_CODENAMES.iteritems():
 
104
        if v == codename:
 
105
            return k
 
106
    e = 'Could not derive OpenStack version for '\
 
107
        'codename: %s' % codename
 
108
    error_out(e)
 
109
 
 
110
 
 
111
def get_os_codename_package(package, fatal=True):
 
112
    '''Derive OpenStack release codename from an installed package.'''
 
113
    apt.init()
 
114
    cache = apt.Cache()
 
115
 
 
116
    try:
 
117
        pkg = cache[package]
 
118
    except:
 
119
        if not fatal:
 
120
            return None
 
121
        # the package is unknown to the current apt cache.
 
122
        e = 'Could not determine version of package with no installation '\
 
123
            'candidate: %s' % package
 
124
        error_out(e)
 
125
 
 
126
    if not pkg.current_ver:
 
127
        if not fatal:
 
128
            return None
 
129
        # package is known, but no version is currently installed.
 
130
        e = 'Could not determine version of uninstalled package: %s' % package
 
131
        error_out(e)
 
132
 
 
133
    vers = apt.UpstreamVersion(pkg.current_ver.ver_str)
 
134
 
 
135
    try:
 
136
        if 'swift' in pkg.name:
 
137
            vers = vers[:5]
 
138
            return SWIFT_CODENAMES[vers]
 
139
        else:
 
140
            vers = vers[:6]
 
141
            return OPENSTACK_CODENAMES[vers]
 
142
    except KeyError:
 
143
        e = 'Could not determine OpenStack codename for version %s' % vers
 
144
        error_out(e)
 
145
 
 
146
 
 
147
def get_os_version_package(pkg, fatal=True):
 
148
    '''Derive OpenStack version number from an installed package.'''
 
149
    codename = get_os_codename_package(pkg, fatal=fatal)
 
150
 
 
151
    if not codename:
 
152
        return None
 
153
 
 
154
    if 'swift' in pkg:
 
155
        vers_map = SWIFT_CODENAMES
 
156
    else:
 
157
        vers_map = OPENSTACK_CODENAMES
 
158
 
 
159
    for version, cname in vers_map.iteritems():
 
160
        if cname == codename:
 
161
            return version
 
162
    #e = "Could not determine OpenStack version for package: %s" % pkg
 
163
    #error_out(e)
 
164
 
 
165
 
 
166
def import_key(keyid):
 
167
    cmd = "apt-key adv --keyserver keyserver.ubuntu.com " \
 
168
          "--recv-keys %s" % keyid
 
169
    try:
 
170
        subprocess.check_call(cmd.split(' '))
 
171
    except subprocess.CalledProcessError:
 
172
        error_out("Error importing repo key %s" % keyid)
 
173
 
 
174
 
 
175
def configure_installation_source(rel):
 
176
    '''Configure apt installation source.'''
 
177
    if rel == 'distro':
 
178
        return
 
179
    elif rel[:4] == "ppa:":
 
180
        src = rel
 
181
        subprocess.check_call(["add-apt-repository", "-y", src])
 
182
    elif rel[:3] == "deb":
 
183
        l = len(rel.split('|'))
 
184
        if l == 2:
 
185
            src, key = rel.split('|')
 
186
            juju_log("Importing PPA key from keyserver for %s" % src)
 
187
            import_key(key)
 
188
        elif l == 1:
 
189
            src = rel
 
190
        with open('/etc/apt/sources.list.d/juju_deb.list', 'w') as f:
 
191
            f.write(src)
 
192
    elif rel[:6] == 'cloud:':
 
193
        ubuntu_rel = lsb_release()['DISTRIB_CODENAME']
 
194
        rel = rel.split(':')[1]
 
195
        u_rel = rel.split('-')[0]
 
196
        ca_rel = rel.split('-')[1]
 
197
 
 
198
        if u_rel != ubuntu_rel:
 
199
            e = 'Cannot install from Cloud Archive pocket %s on this Ubuntu '\
 
200
                'version (%s)' % (ca_rel, ubuntu_rel)
 
201
            error_out(e)
 
202
 
 
203
        if 'staging' in ca_rel:
 
204
            # staging is just a regular PPA.
 
205
            os_rel = ca_rel.split('/')[0]
 
206
            ppa = 'ppa:ubuntu-cloud-archive/%s-staging' % os_rel
 
207
            cmd = 'add-apt-repository -y %s' % ppa
 
208
            subprocess.check_call(cmd.split(' '))
 
209
            return
 
210
 
 
211
        # map charm config options to actual archive pockets.
 
212
        pockets = {
 
213
            'folsom': 'precise-updates/folsom',
 
214
            'folsom/updates': 'precise-updates/folsom',
 
215
            'folsom/proposed': 'precise-proposed/folsom',
 
216
            'grizzly': 'precise-updates/grizzly',
 
217
            'grizzly/updates': 'precise-updates/grizzly',
 
218
            'grizzly/proposed': 'precise-proposed/grizzly',
 
219
            'havana': 'precise-updates/havana',
 
220
            'havana/updates': 'precise-updates/havana',
 
221
            'havana/proposed': 'precise-proposed/havana',
 
222
        }
 
223
 
 
224
        try:
 
225
            pocket = pockets[ca_rel]
 
226
        except KeyError:
 
227
            e = 'Invalid Cloud Archive release specified: %s' % rel
 
228
            error_out(e)
 
229
 
 
230
        src = "deb %s %s main" % (CLOUD_ARCHIVE_URL, pocket)
 
231
        apt_install('ubuntu-cloud-keyring', fatal=True)
 
232
 
 
233
        with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as f:
 
234
            f.write(src)
 
235
    else:
 
236
        error_out("Invalid openstack-release specified: %s" % rel)
 
237
 
 
238
 
 
239
def save_script_rc(script_path="scripts/scriptrc", **env_vars):
 
240
    """
 
241
    Write an rc file in the charm-delivered directory containing
 
242
    exported environment variables provided by env_vars. Any charm scripts run
 
243
    outside the juju hook environment can source this scriptrc to obtain
 
244
    updated config information necessary to perform health checks or
 
245
    service changes.
 
246
    """
 
247
    juju_rc_path = "%s/%s" % (charm_dir(), script_path)
 
248
    if not os.path.exists(os.path.dirname(juju_rc_path)):
 
249
        os.mkdir(os.path.dirname(juju_rc_path))
 
250
    with open(juju_rc_path, 'wb') as rc_script:
 
251
        rc_script.write(
 
252
            "#!/bin/bash\n")
 
253
        [rc_script.write('export %s=%s\n' % (u, p))
 
254
         for u, p in env_vars.iteritems() if u != "script_path"]
 
255
 
 
256
 
 
257
def openstack_upgrade_available(package):
 
258
    """
 
259
    Determines if an OpenStack upgrade is available from installation
 
260
    source, based on version of installed package.
 
261
 
 
262
    :param package: str: Name of installed package.
 
263
 
 
264
    :returns: bool:    : Returns True if configured installation source offers
 
265
                         a newer version of package.
 
266
 
 
267
    """
 
268
 
 
269
    src = config('openstack-origin')
 
270
    cur_vers = get_os_version_package(package)
 
271
    available_vers = get_os_version_install_source(src)
 
272
    apt.init()
 
273
    return apt.version_compare(available_vers, cur_vers) == 1