3
# Common python helper functions used for OpenStack charms.
4
from collections import OrderedDict
12
from charmhelpers.core.hookenv import (
18
from charmhelpers.core.host import (
22
from charmhelpers.fetch import (
26
CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu"
27
CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA'
29
UBUNTU_OPENSTACK_RELEASE = OrderedDict([
30
('oneiric', 'diablo'),
32
('quantal', 'folsom'),
33
('raring', 'grizzly'),
38
OPENSTACK_CODENAMES = OrderedDict([
42
('2013.1', 'grizzly'),
44
('2014.1', 'icehouse'),
48
SWIFT_CODENAMES = OrderedDict([
62
juju_log("FATAL ERROR: %s" % msg, level='ERROR')
66
def get_os_codename_install_source(src):
67
'''Derive OpenStack release codename from a given installation source.'''
68
ubuntu_rel = lsb_release()['DISTRIB_CODENAME']
72
rel = UBUNTU_OPENSTACK_RELEASE[ubuntu_rel]
74
e = 'Could not derive openstack release for '\
75
'this Ubuntu release: %s' % ubuntu_rel
79
if src.startswith('cloud:'):
80
ca_rel = src.split(':')[1]
81
ca_rel = ca_rel.split('%s-' % ubuntu_rel)[1].split('/')[0]
84
# Best guess match based on deb string provided
85
if src.startswith('deb') or src.startswith('ppa'):
86
for k, v in OPENSTACK_CODENAMES.iteritems():
91
def get_os_version_install_source(src):
92
codename = get_os_codename_install_source(src)
93
return get_os_version_codename(codename)
96
def get_os_codename_version(vers):
97
'''Determine OpenStack codename from version number.'''
99
return OPENSTACK_CODENAMES[vers]
101
e = 'Could not determine OpenStack codename for version %s' % vers
105
def get_os_version_codename(codename):
106
'''Determine OpenStack version number from codename.'''
107
for k, v in OPENSTACK_CODENAMES.iteritems():
110
e = 'Could not derive OpenStack version for '\
111
'codename: %s' % codename
115
def get_os_codename_package(package, fatal=True):
116
'''Derive OpenStack release codename from an installed package.'''
125
# the package is unknown to the current apt cache.
126
e = 'Could not determine version of package with no installation '\
127
'candidate: %s' % package
130
if not pkg.current_ver:
133
# package is known, but no version is currently installed.
134
e = 'Could not determine version of uninstalled package: %s' % package
137
vers = apt.upstream_version(pkg.current_ver.ver_str)
140
if 'swift' in pkg.name:
141
swift_vers = vers[:5]
142
if swift_vers not in SWIFT_CODENAMES:
143
# Deal with 1.10.0 upward
144
swift_vers = vers[:6]
145
return SWIFT_CODENAMES[swift_vers]
148
return OPENSTACK_CODENAMES[vers]
150
e = 'Could not determine OpenStack codename for version %s' % vers
154
def get_os_version_package(pkg, fatal=True):
155
'''Derive OpenStack version number from an installed package.'''
156
codename = get_os_codename_package(pkg, fatal=fatal)
162
vers_map = SWIFT_CODENAMES
164
vers_map = OPENSTACK_CODENAMES
166
for version, cname in vers_map.iteritems():
167
if cname == codename:
169
#e = "Could not determine OpenStack version for package: %s" % pkg
176
def os_release(package, base='essex'):
178
Returns OpenStack release codename from a cached global.
179
If the codename can not be determined from either an installed package or
180
the installation source, the earliest release supported by the charm should
186
os_rel = (get_os_codename_package(package, fatal=False) or
187
get_os_codename_install_source(config('openstack-origin')) or
192
def import_key(keyid):
193
cmd = "apt-key adv --keyserver keyserver.ubuntu.com " \
194
"--recv-keys %s" % keyid
196
subprocess.check_call(cmd.split(' '))
197
except subprocess.CalledProcessError:
198
error_out("Error importing repo key %s" % keyid)
201
def configure_installation_source(rel):
202
'''Configure apt installation source.'''
205
elif rel[:4] == "ppa:":
207
subprocess.check_call(["add-apt-repository", "-y", src])
208
elif rel[:3] == "deb":
209
l = len(rel.split('|'))
211
src, key = rel.split('|')
212
juju_log("Importing PPA key from keyserver for %s" % src)
216
with open('/etc/apt/sources.list.d/juju_deb.list', 'w') as f:
218
elif rel[:6] == 'cloud:':
219
ubuntu_rel = lsb_release()['DISTRIB_CODENAME']
220
rel = rel.split(':')[1]
221
u_rel = rel.split('-')[0]
222
ca_rel = rel.split('-')[1]
224
if u_rel != ubuntu_rel:
225
e = 'Cannot install from Cloud Archive pocket %s on this Ubuntu '\
226
'version (%s)' % (ca_rel, ubuntu_rel)
229
if 'staging' in ca_rel:
230
# staging is just a regular PPA.
231
os_rel = ca_rel.split('/')[0]
232
ppa = 'ppa:ubuntu-cloud-archive/%s-staging' % os_rel
233
cmd = 'add-apt-repository -y %s' % ppa
234
subprocess.check_call(cmd.split(' '))
237
# map charm config options to actual archive pockets.
239
'folsom': 'precise-updates/folsom',
240
'folsom/updates': 'precise-updates/folsom',
241
'folsom/proposed': 'precise-proposed/folsom',
242
'grizzly': 'precise-updates/grizzly',
243
'grizzly/updates': 'precise-updates/grizzly',
244
'grizzly/proposed': 'precise-proposed/grizzly',
245
'havana': 'precise-updates/havana',
246
'havana/updates': 'precise-updates/havana',
247
'havana/proposed': 'precise-proposed/havana',
251
pocket = pockets[ca_rel]
253
e = 'Invalid Cloud Archive release specified: %s' % rel
256
src = "deb %s %s main" % (CLOUD_ARCHIVE_URL, pocket)
257
apt_install('ubuntu-cloud-keyring', fatal=True)
259
with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as f:
262
error_out("Invalid openstack-release specified: %s" % rel)
265
def save_script_rc(script_path="scripts/scriptrc", **env_vars):
267
Write an rc file in the charm-delivered directory containing
268
exported environment variables provided by env_vars. Any charm scripts run
269
outside the juju hook environment can source this scriptrc to obtain
270
updated config information necessary to perform health checks or
273
juju_rc_path = "%s/%s" % (charm_dir(), script_path)
274
if not os.path.exists(os.path.dirname(juju_rc_path)):
275
os.mkdir(os.path.dirname(juju_rc_path))
276
with open(juju_rc_path, 'wb') as rc_script:
279
[rc_script.write('export %s=%s\n' % (u, p))
280
for u, p in env_vars.iteritems() if u != "script_path"]
283
def openstack_upgrade_available(package):
285
Determines if an OpenStack upgrade is available from installation
286
source, based on version of installed package.
288
:param package: str: Name of installed package.
290
:returns: bool: : Returns True if configured installation source offers
291
a newer version of package.
295
src = config('openstack-origin')
296
cur_vers = get_os_version_package(package)
297
available_vers = get_os_version_install_source(src)
299
return apt.version_compare(available_vers, cur_vers) == 1
304
Returns True if address is a valid IP address.
307
# Test to see if already an IPv4 address
308
socket.inet_aton(address)
314
def ns_query(address):
318
apt_install('python-dnspython')
321
if isinstance(address, dns.name.Name):
323
elif isinstance(address, basestring):
326
answers = dns.resolver.query(address, rtype)
328
return str(answers[0])
332
def get_host_ip(hostname):
334
Resolves the IP for a given hostname, or returns
335
the input if it is already an IP.
340
return ns_query(hostname)
343
def get_hostname(address):
345
Resolves hostname for given IP, or returns the input
346
if it is already a hostname.
348
if not is_ip(address):
352
import dns.reversename
354
apt_install('python-dnspython')
355
import dns.reversename
357
rev = dns.reversename.from_address(address)
358
result = ns_query(rev)
363
if result.endswith('.'):