first hook
This commit is contained in:
parent
ba35c31cd4
commit
bbd63210bc
|
@ -8,7 +8,7 @@ options:
|
|||
type: string
|
||||
description: "Enable verbose logging"
|
||||
openstack-origin:
|
||||
default: distro
|
||||
default: cloud:precise-folsom
|
||||
type: string
|
||||
description: |
|
||||
Repository from which to install. May be one of the following:
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
from utils import *
|
||||
from lib.openstack_common import *
|
||||
|
||||
config = config_get()
|
||||
|
||||
packages = "ceilometer"
|
||||
service = "ceilometer"
|
||||
|
||||
def install_hook():
|
||||
if config["openstack-origin"]!="distro":
|
||||
configure_installation_source(config["openstack-origin"])
|
||||
execute("apt-get update", die=True)
|
||||
execute("apt-get -y install %s" % packages, die=True, echo=True)
|
||||
|
||||
hooks = {
|
||||
"install": install_hook,
|
||||
}
|
||||
|
||||
# ceiloemter-hooks gets called by symlink corresponding to the requested relation
|
||||
# hook.
|
||||
arg0 = sys.argv[0].split("/").pop()
|
||||
if arg0 not in hooks.keys():
|
||||
error_out("Unsupported hook: %s" % arg0)
|
||||
hooks[arg0]()
|
|
@ -0,0 +1 @@
|
|||
ceilometer-hooks
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,192 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
# Common python helper functions used for OpenStack charms.
|
||||
|
||||
import subprocess
|
||||
|
||||
CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu"
|
||||
CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA'
|
||||
|
||||
ubuntu_openstack_release = {
|
||||
'oneiric': 'diablo',
|
||||
'precise': 'essex',
|
||||
'quantal': 'folsom',
|
||||
'raring' : 'grizzly'
|
||||
}
|
||||
|
||||
|
||||
openstack_codenames = {
|
||||
'2011.2': 'diablo',
|
||||
'2012.1': 'essex',
|
||||
'2012.2': 'folsom',
|
||||
'2012.3': 'grizzly'
|
||||
}
|
||||
|
||||
|
||||
def juju_log(msg):
|
||||
subprocess.check_call(['juju-log', msg])
|
||||
|
||||
|
||||
def error_out(msg):
|
||||
juju_log("FATAL ERROR: %s" % msg)
|
||||
exit(1)
|
||||
|
||||
|
||||
def lsb_release():
|
||||
'''Return /etc/lsb-release in a dict'''
|
||||
lsb = open('/etc/lsb-release', 'r')
|
||||
d = {}
|
||||
for l in lsb:
|
||||
k, v = l.split('=')
|
||||
d[k.strip()] = v.strip()
|
||||
return d
|
||||
|
||||
|
||||
def get_os_codename_install_source(src):
|
||||
'''Derive OpenStack release codename from a given installation source.'''
|
||||
ubuntu_rel = lsb_release()['DISTRIB_CODENAME']
|
||||
|
||||
rel = ''
|
||||
if src == 'distro':
|
||||
try:
|
||||
rel = ubuntu_openstack_release[ubuntu_rel]
|
||||
except KeyError:
|
||||
e = 'Code not derive openstack release for '\
|
||||
'this Ubuntu release: %s' % rel
|
||||
error_out(e)
|
||||
return rel
|
||||
|
||||
if src.startswith('cloud:'):
|
||||
ca_rel = src.split(':')[1]
|
||||
ca_rel = ca_rel.split('%s-' % ubuntu_rel)[1].split('/')[0]
|
||||
return ca_rel
|
||||
|
||||
# Best guess match based on deb string provided
|
||||
if src.startswith('deb'):
|
||||
for k, v in openstack_codenames.iteritems():
|
||||
if v in src:
|
||||
return v
|
||||
|
||||
def get_os_codename_version(vers):
|
||||
'''Determine OpenStack codename from version number.'''
|
||||
try:
|
||||
return openstack_codenames[vers]
|
||||
except KeyError:
|
||||
e = 'Could not determine OpenStack codename for version %s' % vers
|
||||
error_out(e)
|
||||
|
||||
|
||||
def get_os_version_codename(codename):
|
||||
'''Determine OpenStack version number from codename.'''
|
||||
for k, v in openstack_codenames.iteritems():
|
||||
if v == codename:
|
||||
return k
|
||||
e = 'Code not derive OpenStack version for '\
|
||||
'codename: %s' % codename
|
||||
error_out(e)
|
||||
|
||||
|
||||
def get_os_codename_package(pkg):
|
||||
'''Derive OpenStack release codename from an installed package.'''
|
||||
cmd = ['dpkg', '-l', pkg]
|
||||
|
||||
try:
|
||||
output = subprocess.check_output(cmd)
|
||||
except subprocess.CalledProcessError:
|
||||
e = 'Could not derive OpenStack version from package that is not '\
|
||||
'installed; %s' % pkg
|
||||
error_out(e)
|
||||
|
||||
def _clean(line):
|
||||
line = line.split(' ')
|
||||
clean = []
|
||||
for c in line:
|
||||
if c != '':
|
||||
clean.append(c)
|
||||
return clean
|
||||
|
||||
vers = None
|
||||
for l in output.split('\n'):
|
||||
if l.startswith('ii'):
|
||||
l = _clean(l)
|
||||
if l[1] == pkg:
|
||||
vers = l[2]
|
||||
|
||||
if not vers:
|
||||
e = 'Could not determine version of installed package: %s' % pkg
|
||||
error_out(e)
|
||||
|
||||
vers = vers[:6]
|
||||
try:
|
||||
return openstack_codenames[vers]
|
||||
except KeyError:
|
||||
e = 'Could not determine OpenStack codename for version %s' % vers
|
||||
error_out(e)
|
||||
|
||||
|
||||
def configure_installation_source(rel):
|
||||
'''Configure apt installation source.'''
|
||||
|
||||
def _import_key(id):
|
||||
cmd = "apt-key adv --keyserver keyserver.ubuntu.com " \
|
||||
"--recv-keys %s" % id
|
||||
try:
|
||||
subprocess.check_call(cmd.split(' '))
|
||||
except:
|
||||
error_out("Error importing repo key %s" % id)
|
||||
|
||||
if rel == 'distro':
|
||||
return
|
||||
elif rel[:4] == "ppa:":
|
||||
src = rel
|
||||
subprocess.check_call(["add-apt-repository", "-y", src])
|
||||
elif rel[:3] == "deb":
|
||||
l = len(rel.split('|'))
|
||||
if l == 2:
|
||||
src, key = rel.split('|')
|
||||
juju_log("Importing PPA key from keyserver for %s" % src)
|
||||
_import_key(key)
|
||||
elif l == 1:
|
||||
src = rel
|
||||
else:
|
||||
error_out("Invalid openstack-release: %s" % rel)
|
||||
|
||||
with open('/etc/apt/sources.list.d/juju_deb.list', 'w') as f:
|
||||
f.write(src)
|
||||
elif rel[:6] == 'cloud:':
|
||||
ubuntu_rel = lsb_release()['DISTRIB_CODENAME']
|
||||
rel = rel.split(':')[1]
|
||||
u_rel = rel.split('-')[0]
|
||||
ca_rel = rel.split('-')[1]
|
||||
|
||||
if u_rel != ubuntu_rel:
|
||||
e = 'Cannot install from Cloud Archive pocket %s on this Ubuntu '\
|
||||
'version (%s)' % (ca_rel, ubuntu_rel)
|
||||
error_out(e)
|
||||
|
||||
if ca_rel == 'folsom/staging':
|
||||
# staging is just a regular PPA.
|
||||
cmd = 'add-apt-repository -y ppa:ubuntu-cloud-archive/folsom-staging'
|
||||
subprocess.check_call(cmd.split(' '))
|
||||
return
|
||||
|
||||
# map charm config options to actual archive pockets.
|
||||
pockets = {
|
||||
'folsom': 'precise-updates/folsom',
|
||||
'folsom/updates': 'precise-updates/folsom',
|
||||
'folsom/proposed': 'precise-proposed/folsom'
|
||||
}
|
||||
|
||||
try:
|
||||
pocket = pockets[ca_rel]
|
||||
except KeyError:
|
||||
e = 'Invalid Cloud Archive release specified: %s' % rel
|
||||
error_out(e)
|
||||
|
||||
src = "deb %s %s main" % (CLOUD_ARCHIVE_URL, pocket)
|
||||
_import_key(CLOUD_ARCHIVE_KEY_ID)
|
||||
|
||||
with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as f:
|
||||
f.write(src)
|
||||
else:
|
||||
error_out("Invalid openstack-release specified: %s" % rel)
|
|
@ -0,0 +1,216 @@
|
|||
#!/usr/bin/python
|
||||
import subprocess
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
|
||||
from lib.openstack_common import *
|
||||
|
||||
ceilometer_conf = "/etc/ceilometer/ceilometer.conf"
|
||||
|
||||
def execute(cmd, die=False, echo=False):
|
||||
""" Executes a command
|
||||
|
||||
if die=True, script will exit(1) if command does not return 0
|
||||
if echo=True, output of command will be printed to stdout
|
||||
|
||||
returns a tuple: (stdout, stderr, return code)
|
||||
"""
|
||||
p = subprocess.Popen(cmd.split(" "),
|
||||
stdout=subprocess.PIPE,
|
||||
stdin=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stdout=""
|
||||
stderr=""
|
||||
|
||||
def print_line(l):
|
||||
if echo:
|
||||
print l.strip('\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
for l in iter(p.stdout.readline, ''):
|
||||
print_line(l)
|
||||
stdout += l
|
||||
for l in iter(p.stderr.readline, ''):
|
||||
print_line(l)
|
||||
stderr += l
|
||||
|
||||
p.communicate()
|
||||
rc = p.returncode
|
||||
|
||||
if die and rc != 0:
|
||||
error_out("ERROR: command %s return non-zero.\n" % cmd)
|
||||
return (stdout, stderr, rc)
|
||||
|
||||
|
||||
def config_get():
|
||||
""" Obtain the units config via 'config-get'
|
||||
Returns a dict representing current config.
|
||||
private-address and IP of the unit is also tacked on for
|
||||
convienence
|
||||
"""
|
||||
output = execute("config-get --format json")[0]
|
||||
if output:
|
||||
config = json.loads(output)
|
||||
# make sure no config element is blank after config-get
|
||||
for c in config.keys():
|
||||
if not config[c]:
|
||||
error_out("ERROR: Config option has no paramter: %s" % c)
|
||||
# tack on our private address and ip
|
||||
hostname = execute("unit-get private-address")[0].strip()
|
||||
config["hostname"] = execute("unit-get private-address")[0].strip()
|
||||
else:
|
||||
config = {}
|
||||
return config
|
||||
|
||||
def relation_ids(relation_name=None):
|
||||
j = execute('relation-ids --format=json %s' % relation_name)[0]
|
||||
return json.loads(j)
|
||||
|
||||
def relation_list(relation_id=None):
|
||||
cmd = 'relation-list --format=json'
|
||||
if relation_id:
|
||||
cmd += ' -r %s' % relation_id
|
||||
j = execute(cmd)[0]
|
||||
return json.loads(j)
|
||||
|
||||
def relation_set(relation_data):
|
||||
""" calls relation-set for all key=values in dict """
|
||||
for k in relation_data:
|
||||
execute("relation-set %s=%s" % (k, relation_data[k]), die=True)
|
||||
|
||||
def relation_get(relation_data):
|
||||
""" Obtain all current relation data
|
||||
relation_data is a list of options to query from the relation
|
||||
Returns a k,v dict of the results.
|
||||
Leave empty responses out of the results as they haven't yet been
|
||||
set on the other end.
|
||||
Caller can then "len(results.keys()) == len(relation_data)" to find out if
|
||||
all relation values have been set on the other side
|
||||
"""
|
||||
results = {}
|
||||
for r in relation_data:
|
||||
result = execute("relation-get %s" % r, die=True)[0].strip('\n')
|
||||
if result != "":
|
||||
results[r] = result
|
||||
return results
|
||||
|
||||
def relation_get_dict(relation_id=None, remote_unit=None):
|
||||
"""Obtain all relation data as dict by way of JSON"""
|
||||
cmd = 'relation-get --format=json'
|
||||
if relation_id:
|
||||
cmd += ' -r %s' % relation_id
|
||||
if remote_unit:
|
||||
remote_unit_orig = os.getenv('JUJU_REMOTE_UNIT', None)
|
||||
os.environ['JUJU_REMOTE_UNIT'] = remote_unit
|
||||
j = execute(cmd, die=True)[0]
|
||||
if remote_unit and remote_unit_orig:
|
||||
os.environ['JUJU_REMOTE_UNIT'] = remote_unit_orig
|
||||
d = json.loads(j)
|
||||
settings = {}
|
||||
# convert unicode to strings
|
||||
for k, v in d.iteritems():
|
||||
settings[str(k)] = str(v)
|
||||
return settings
|
||||
|
||||
def update_config_block(block, **kwargs):
|
||||
""" Updates ceilometer.conf blocks given kwargs.
|
||||
Can be used to update driver settings for a particular backend,
|
||||
setting the sql connection, etc.
|
||||
|
||||
Parses block heading as '[block]'
|
||||
|
||||
If block does not exist, a new block will be created at end of file with
|
||||
given kwargs
|
||||
"""
|
||||
f = open(ceilometer_conf, "r+")
|
||||
orig = f.readlines()
|
||||
new = []
|
||||
found_block = ""
|
||||
heading = "[%s]\n" % block
|
||||
|
||||
lines = len(orig)
|
||||
ln = 0
|
||||
|
||||
def update_block(block):
|
||||
for k, v in kwargs.iteritems():
|
||||
for l in block:
|
||||
if l.strip().split(" ")[0] == k:
|
||||
block[block.index(l)] = "%s = %s\n" % (k, v)
|
||||
return
|
||||
block.append('%s = %s\n' % (k, v))
|
||||
block.append('\n')
|
||||
|
||||
try:
|
||||
found = False
|
||||
while ln < lines:
|
||||
if orig[ln] != heading:
|
||||
new.append(orig[ln])
|
||||
ln += 1
|
||||
else:
|
||||
new.append(orig[ln])
|
||||
ln += 1
|
||||
block = []
|
||||
while orig[ln].strip() != '':
|
||||
block.append(orig[ln])
|
||||
ln += 1
|
||||
update_block(block)
|
||||
new += block
|
||||
found = True
|
||||
|
||||
if not found:
|
||||
if new[(len(new) - 1)].strip() != '':
|
||||
new.append('\n')
|
||||
new.append('%s' % heading)
|
||||
for k, v in kwargs.iteritems():
|
||||
new.append('%s = %s\n' % (k, v))
|
||||
new.append('\n')
|
||||
except:
|
||||
error_out('Error while attempting to update config block. '\
|
||||
'Refusing to overwite existing config.')
|
||||
|
||||
return
|
||||
|
||||
# backup original config
|
||||
backup = open(ceilometer_conf + '.juju-back', 'w+')
|
||||
for l in orig:
|
||||
backup.write(l)
|
||||
backup.close()
|
||||
|
||||
# update config
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
for l in new:
|
||||
f.write(l)
|
||||
|
||||
|
||||
def ceilometer_conf_update(opt, val):
|
||||
""" Updates ceilometer.conf values
|
||||
If option exists, it is reset to new value
|
||||
If it does not, it added to the top of the config file after the [DEFAULT]
|
||||
heading to keep it out of the paste deploy config
|
||||
"""
|
||||
f = open(ceilometer_conf, "r+")
|
||||
orig = f.readlines()
|
||||
new = ""
|
||||
found = False
|
||||
for l in orig:
|
||||
if l.split(' ')[0] == opt:
|
||||
juju_log("Updating %s, setting %s = %s" % (keystone_conf, opt, val))
|
||||
new += "%s = %s\n" % (opt, val)
|
||||
found = True
|
||||
else:
|
||||
new += l
|
||||
new = new.split('\n')
|
||||
# insert a new value at the top of the file, after the 'DEFAULT' header so
|
||||
# as not to muck up paste deploy configuration later in the file
|
||||
if not found:
|
||||
juju_log("Adding new config option %s = %s" % (opt, val))
|
||||
header = new.index("[DEFAULT]")
|
||||
new.insert((header+1), "%s = %s" % (opt, val))
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
for l in new:
|
||||
f.write("%s\n" % l)
|
||||
f.close
|
Loading…
Reference in New Issue