Automated resync of charm-helpers

This commit is contained in:
James Page 2015-03-04 09:50:34 +00:00
parent 61ccd7df7f
commit 13cad7e51e
5 changed files with 178 additions and 87 deletions

View File

@ -17,13 +17,16 @@
import glob import glob
import re import re
import subprocess import subprocess
import six
import socket
from functools import partial from functools import partial
from charmhelpers.core.hookenv import unit_get from charmhelpers.core.hookenv import unit_get
from charmhelpers.fetch import apt_install from charmhelpers.fetch import apt_install
from charmhelpers.core.hookenv import ( from charmhelpers.core.hookenv import (
log log,
WARNING,
) )
try: try:
@ -365,3 +368,83 @@ def is_bridge_member(nic):
return True return True
return False return False
def is_ip(address):
"""
Returns True if address is a valid IP address.
"""
try:
# Test to see if already an IPv4 address
socket.inet_aton(address)
return True
except socket.error:
return False
def ns_query(address):
try:
import dns.resolver
except ImportError:
apt_install('python-dnspython')
import dns.resolver
if isinstance(address, dns.name.Name):
rtype = 'PTR'
elif isinstance(address, six.string_types):
rtype = 'A'
else:
return None
answers = dns.resolver.query(address, rtype)
if answers:
return str(answers[0])
return None
def get_host_ip(hostname, fallback=None):
"""
Resolves the IP for a given hostname, or returns
the input if it is already an IP.
"""
if is_ip(hostname):
return hostname
ip_addr = ns_query(hostname)
if not ip_addr:
try:
ip_addr = socket.gethostbyname(hostname)
except:
log("Failed to resolve hostname '%s'" % (hostname),
level=WARNING)
return fallback
return ip_addr
def get_hostname(address, fqdn=True):
"""
Resolves hostname for given IP, or returns the input
if it is already a hostname.
"""
if is_ip(address):
try:
import dns.reversename
except ImportError:
apt_install("python-dnspython")
import dns.reversename
rev = dns.reversename.from_address(address)
result = ns_query(rev)
if not result:
return None
else:
result = address
if fqdn:
# strip trailing .
if result.endswith('.'):
return result[:-1]
else:
return result
else:
return result.split('.')[0]

View File

@ -21,6 +21,7 @@ from base64 import b64decode
from subprocess import check_call from subprocess import check_call
import six import six
import yaml
from charmhelpers.fetch import ( from charmhelpers.fetch import (
apt_install, apt_install,
@ -104,9 +105,41 @@ def context_complete(ctxt):
def config_flags_parser(config_flags): def config_flags_parser(config_flags):
"""Parses config flags string into dict. """Parses config flags string into dict.
This parsing method supports a few different formats for the config
flag values to be parsed:
1. A string in the simple format of key=value pairs, with the possibility
of specifying multiple key value pairs within the same string. For
example, a string in the format of 'key1=value1, key2=value2' will
return a dict of:
{'key1': 'value1',
'key2': 'value2'}.
2. A string in the above format, but supporting a comma-delimited list
of values for the same key. For example, a string in the format of
'key1=value1, key2=value3,value4,value5' will return a dict of:
{'key1', 'value1',
'key2', 'value2,value3,value4'}
3. A string containing a colon character (:) prior to an equal
character (=) will be treated as yaml and parsed as such. This can be
used to specify more complex key value pairs. For example,
a string in the format of 'key1: subkey1=value1, subkey2=value2' will
return a dict of:
{'key1', 'subkey1=value1, subkey2=value2'}
The provided config_flags string may be a list of comma-separated values The provided config_flags string may be a list of comma-separated values
which themselves may be comma-separated list of values. which themselves may be comma-separated list of values.
""" """
# If we find a colon before an equals sign then treat it as yaml.
# Note: limit it to finding the colon first since this indicates assignment
# for inline yaml.
colon = config_flags.find(':')
equals = config_flags.find('=')
if colon > 0:
if colon < equals or equals < 0:
return yaml.safe_load(config_flags)
if config_flags.find('==') >= 0: if config_flags.find('==') >= 0:
log("config_flags is not in expected format (key=value)", level=ERROR) log("config_flags is not in expected format (key=value)", level=ERROR)
raise OSContextError raise OSContextError
@ -191,7 +224,7 @@ class SharedDBContext(OSContextGenerator):
unit=local_unit()) unit=local_unit())
if set_hostname != access_hostname: if set_hostname != access_hostname:
relation_set(relation_settings={hostname_key: access_hostname}) relation_set(relation_settings={hostname_key: access_hostname})
return ctxt # Defer any further hook execution for now.... return None # Defer any further hook execution for now....
password_setting = 'password' password_setting = 'password'
if self.relation_prefix: if self.relation_prefix:
@ -279,9 +312,25 @@ def db_ssl(rdata, ctxt, ssl_dir):
class IdentityServiceContext(OSContextGenerator): class IdentityServiceContext(OSContextGenerator):
interfaces = ['identity-service'] interfaces = ['identity-service']
def __init__(self, service=None, service_user=None):
self.service = service
self.service_user = service_user
def __call__(self): def __call__(self):
log('Generating template context for identity-service', level=DEBUG) log('Generating template context for identity-service', level=DEBUG)
ctxt = {} ctxt = {}
if self.service and self.service_user:
# This is required for pki token signing if we don't want /tmp to
# be used.
cachedir = '/var/cache/%s' % (self.service)
if not os.path.isdir(cachedir):
log("Creating service cache dir %s" % (cachedir), level=DEBUG)
mkdir(path=cachedir, owner=self.service_user,
group=self.service_user, perms=0o700)
ctxt['signing_dir'] = cachedir
for rid in relation_ids('identity-service'): for rid in relation_ids('identity-service'):
for unit in related_units(rid): for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit) rdata = relation_get(rid=rid, unit=unit)
@ -291,7 +340,7 @@ class IdentityServiceContext(OSContextGenerator):
auth_host = format_ipv6_addr(auth_host) or auth_host auth_host = format_ipv6_addr(auth_host) or auth_host
svc_protocol = rdata.get('service_protocol') or 'http' svc_protocol = rdata.get('service_protocol') or 'http'
auth_protocol = rdata.get('auth_protocol') or 'http' auth_protocol = rdata.get('auth_protocol') or 'http'
ctxt = {'service_port': rdata.get('service_port'), ctxt.update({'service_port': rdata.get('service_port'),
'service_host': serv_host, 'service_host': serv_host,
'auth_host': auth_host, 'auth_host': auth_host,
'auth_port': rdata.get('auth_port'), 'auth_port': rdata.get('auth_port'),
@ -299,7 +348,8 @@ class IdentityServiceContext(OSContextGenerator):
'admin_user': rdata.get('service_username'), 'admin_user': rdata.get('service_username'),
'admin_password': rdata.get('service_password'), 'admin_password': rdata.get('service_password'),
'service_protocol': svc_protocol, 'service_protocol': svc_protocol,
'auth_protocol': auth_protocol} 'auth_protocol': auth_protocol})
if context_complete(ctxt): if context_complete(ctxt):
# NOTE(jamespage) this is required for >= icehouse # NOTE(jamespage) this is required for >= icehouse
# so a missing value just indicates keystone needs # so a missing value just indicates keystone needs
@ -1021,6 +1071,8 @@ class ZeroMQContext(OSContextGenerator):
for unit in related_units(rid): for unit in related_units(rid):
ctxt['zmq_nonce'] = relation_get('nonce', unit, rid) ctxt['zmq_nonce'] = relation_get('nonce', unit, rid)
ctxt['zmq_host'] = relation_get('host', unit, rid) ctxt['zmq_host'] = relation_get('host', unit, rid)
ctxt['zmq_redis_address'] = relation_get(
'zmq_redis_address', unit, rid)
return ctxt return ctxt

View File

@ -0,0 +1,14 @@
{% if zmq_host -%}
# ZeroMQ configuration (restart-nonce: {{ zmq_nonce }})
rpc_backend = zmq
rpc_zmq_host = {{ zmq_host }}
{% if zmq_redis_address -%}
rpc_zmq_matchmaker = oslo.messaging._drivers.matchmaker_redis.MatchMakerRedis
matchmaker_heartbeat_freq = 15
matchmaker_heartbeat_ttl = 30
[matchmaker_redis]
host = {{ zmq_redis_address }}
{% else -%}
rpc_zmq_matchmaker = oslo.messaging._drivers.matchmaker_ring.MatchMakerRing
{% endif -%}
{% endif -%}

View File

@ -23,12 +23,13 @@ from functools import wraps
import subprocess import subprocess
import json import json
import os import os
import socket
import sys import sys
import six import six
import yaml import yaml
from charmhelpers.contrib.network import ip
from charmhelpers.core.hookenv import ( from charmhelpers.core.hookenv import (
config, config,
log as juju_log, log as juju_log,
@ -421,77 +422,10 @@ def clean_storage(block_device):
else: else:
zap_disk(block_device) zap_disk(block_device)
is_ip = ip.is_ip
def is_ip(address): ns_query = ip.ns_query
""" get_host_ip = ip.get_host_ip
Returns True if address is a valid IP address. get_hostname = ip.get_hostname
"""
try:
# Test to see if already an IPv4 address
socket.inet_aton(address)
return True
except socket.error:
return False
def ns_query(address):
try:
import dns.resolver
except ImportError:
apt_install('python-dnspython')
import dns.resolver
if isinstance(address, dns.name.Name):
rtype = 'PTR'
elif isinstance(address, six.string_types):
rtype = 'A'
else:
return None
answers = dns.resolver.query(address, rtype)
if answers:
return str(answers[0])
return None
def get_host_ip(hostname):
"""
Resolves the IP for a given hostname, or returns
the input if it is already an IP.
"""
if is_ip(hostname):
return hostname
return ns_query(hostname)
def get_hostname(address, fqdn=True):
"""
Resolves hostname for given IP, or returns the input
if it is already a hostname.
"""
if is_ip(address):
try:
import dns.reversename
except ImportError:
apt_install('python-dnspython')
import dns.reversename
rev = dns.reversename.from_address(address)
result = ns_query(rev)
if not result:
return None
else:
result = address
if fqdn:
# strip trailing .
if result.endswith('.'):
return result[:-1]
else:
return result
else:
return result.split('.')[0]
def get_matchmaker_map(mm_file='/etc/oslo/matchmaker_ring.json'): def get_matchmaker_map(mm_file='/etc/oslo/matchmaker_ring.json'):

View File

@ -45,12 +45,14 @@ class RelationContext(dict):
""" """
name = None name = None
interface = None interface = None
required_keys = []
def __init__(self, name=None, additional_required_keys=None): def __init__(self, name=None, additional_required_keys=None):
if not hasattr(self, 'required_keys'):
self.required_keys = []
if name is not None: if name is not None:
self.name = name self.name = name
if additional_required_keys is not None: if additional_required_keys:
self.required_keys.extend(additional_required_keys) self.required_keys.extend(additional_required_keys)
self.get_data() self.get_data()
@ -134,7 +136,10 @@ class MysqlRelation(RelationContext):
""" """
name = 'db' name = 'db'
interface = 'mysql' interface = 'mysql'
required_keys = ['host', 'user', 'password', 'database']
def __init__(self, *args, **kwargs):
self.required_keys = ['host', 'user', 'password', 'database']
super(HttpRelation).__init__(self, *args, **kwargs)
class HttpRelation(RelationContext): class HttpRelation(RelationContext):
@ -146,7 +151,10 @@ class HttpRelation(RelationContext):
""" """
name = 'website' name = 'website'
interface = 'http' interface = 'http'
required_keys = ['host', 'port']
def __init__(self, *args, **kwargs):
self.required_keys = ['host', 'port']
super(HttpRelation).__init__(self, *args, **kwargs)
def provide_data(self): def provide_data(self):
return { return {