charm-ceilometer/hooks/utils.py

217 lines
6.4 KiB
Python

#!/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