synced charm-helpers
This commit is contained in:
parent
d6b2d58c75
commit
db1888b566
@ -1,4 +1,4 @@
|
||||
branch: lp:charm-helpers
|
||||
branch: lp:~le-charmers/charm-helpers/leadership-election
|
||||
destination: tests/charmhelpers
|
||||
include:
|
||||
- contrib.amulet
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from base64 import b64decode
|
||||
from subprocess import check_call
|
||||
@ -46,8 +47,11 @@ from charmhelpers.core.hookenv import (
|
||||
)
|
||||
|
||||
from charmhelpers.core.sysctl import create as sysctl_create
|
||||
from charmhelpers.core.strutils import bool_from_string
|
||||
|
||||
from charmhelpers.core.host import (
|
||||
list_nics,
|
||||
get_nic_hwaddr,
|
||||
mkdir,
|
||||
write_file,
|
||||
)
|
||||
@ -64,16 +68,22 @@ from charmhelpers.contrib.hahelpers.apache import (
|
||||
)
|
||||
from charmhelpers.contrib.openstack.neutron import (
|
||||
neutron_plugin_attribute,
|
||||
parse_data_port_mappings,
|
||||
)
|
||||
from charmhelpers.contrib.openstack.ip import (
|
||||
resolve_address,
|
||||
INTERNAL,
|
||||
)
|
||||
from charmhelpers.contrib.network.ip import (
|
||||
get_address_in_network,
|
||||
get_ipv4_addr,
|
||||
get_ipv6_addr,
|
||||
get_netmask_for_address,
|
||||
format_ipv6_addr,
|
||||
is_address_in_network,
|
||||
is_bridge_member,
|
||||
)
|
||||
from charmhelpers.contrib.openstack.utils import get_host_ip
|
||||
|
||||
CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt'
|
||||
ADDRESS_TYPES = ['admin', 'internal', 'public']
|
||||
|
||||
@ -727,7 +737,14 @@ class ApacheSSLContext(OSContextGenerator):
|
||||
'endpoints': [],
|
||||
'ext_ports': []}
|
||||
|
||||
for cn in self.canonical_names():
|
||||
cns = self.canonical_names()
|
||||
if cns:
|
||||
for cn in cns:
|
||||
self.configure_cert(cn)
|
||||
else:
|
||||
# Expect cert/key provided in config (currently assumed that ca
|
||||
# uses ip for cn)
|
||||
cn = resolve_address(endpoint_type=INTERNAL)
|
||||
self.configure_cert(cn)
|
||||
|
||||
addresses = self.get_network_addresses()
|
||||
@ -883,6 +900,48 @@ class NeutronContext(OSContextGenerator):
|
||||
return ctxt
|
||||
|
||||
|
||||
class NeutronPortContext(OSContextGenerator):
|
||||
NIC_PREFIXES = ['eth', 'bond']
|
||||
|
||||
def resolve_ports(self, ports):
|
||||
"""Resolve NICs not yet bound to bridge(s)
|
||||
|
||||
If hwaddress provided then returns resolved hwaddress otherwise NIC.
|
||||
"""
|
||||
if not ports:
|
||||
return None
|
||||
|
||||
hwaddr_to_nic = {}
|
||||
hwaddr_to_ip = {}
|
||||
for nic in list_nics(self.NIC_PREFIXES):
|
||||
hwaddr = get_nic_hwaddr(nic)
|
||||
hwaddr_to_nic[hwaddr] = nic
|
||||
addresses = get_ipv4_addr(nic, fatal=False)
|
||||
addresses += get_ipv6_addr(iface=nic, fatal=False)
|
||||
hwaddr_to_ip[hwaddr] = addresses
|
||||
|
||||
resolved = []
|
||||
mac_regex = re.compile(r'([0-9A-F]{2}[:-]){5}([0-9A-F]{2})', re.I)
|
||||
for entry in ports:
|
||||
if re.match(mac_regex, entry):
|
||||
# NIC is in known NICs and does NOT hace an IP address
|
||||
if entry in hwaddr_to_nic and not hwaddr_to_ip[entry]:
|
||||
# If the nic is part of a bridge then don't use it
|
||||
if is_bridge_member(hwaddr_to_nic[entry]):
|
||||
continue
|
||||
|
||||
# Entry is a MAC address for a valid interface that doesn't
|
||||
# have an IP address assigned yet.
|
||||
resolved.append(hwaddr_to_nic[entry])
|
||||
else:
|
||||
# If the passed entry is not a MAC address, assume it's a valid
|
||||
# interface, and that the user put it there on purpose (we can
|
||||
# trust it to be the real external network).
|
||||
resolved.append(entry)
|
||||
|
||||
return resolved
|
||||
|
||||
|
||||
class OSConfigFlagContext(OSContextGenerator):
|
||||
"""Provides support for user-defined config flags.
|
||||
|
||||
@ -1104,3 +1163,145 @@ class SysctlContext(OSContextGenerator):
|
||||
sysctl_create(sysctl_dict,
|
||||
'/etc/sysctl.d/50-{0}.conf'.format(charm_name()))
|
||||
return {'sysctl': sysctl_dict}
|
||||
|
||||
|
||||
class NeutronAPIContext(OSContextGenerator):
|
||||
'''
|
||||
Inspects current neutron-plugin-api relation for neutron settings. Return
|
||||
defaults if it is not present.
|
||||
'''
|
||||
interfaces = ['neutron-plugin-api']
|
||||
|
||||
def __call__(self):
|
||||
self.neutron_defaults = {
|
||||
'l2_population': {
|
||||
'rel_key': 'l2-population',
|
||||
'default': False,
|
||||
},
|
||||
'overlay_network_type': {
|
||||
'rel_key': 'overlay-network-type',
|
||||
'default': 'gre',
|
||||
},
|
||||
'neutron_security_groups': {
|
||||
'rel_key': 'neutron-security-groups',
|
||||
'default': False,
|
||||
},
|
||||
'network_device_mtu': {
|
||||
'rel_key': 'network-device-mtu',
|
||||
'default': None,
|
||||
},
|
||||
'enable_dvr': {
|
||||
'rel_key': 'enable-dvr',
|
||||
'default': False,
|
||||
},
|
||||
'enable_l3ha': {
|
||||
'rel_key': 'enable-l3ha',
|
||||
'default': False,
|
||||
},
|
||||
}
|
||||
ctxt = self.get_neutron_options({})
|
||||
for rid in relation_ids('neutron-plugin-api'):
|
||||
for unit in related_units(rid):
|
||||
rdata = relation_get(rid=rid, unit=unit)
|
||||
if 'l2-population' in rdata:
|
||||
ctxt.update(self.get_neutron_options(rdata))
|
||||
|
||||
return ctxt
|
||||
|
||||
def get_neutron_options(self, rdata):
|
||||
settings = {}
|
||||
for nkey in self.neutron_defaults.keys():
|
||||
defv = self.neutron_defaults[nkey]['default']
|
||||
rkey = self.neutron_defaults[nkey]['rel_key']
|
||||
if rkey in rdata.keys():
|
||||
if type(defv) is bool:
|
||||
settings[nkey] = bool_from_string(rdata[rkey])
|
||||
else:
|
||||
settings[nkey] = rdata[rkey]
|
||||
else:
|
||||
settings[nkey] = defv
|
||||
return settings
|
||||
|
||||
|
||||
class ExternalPortContext(NeutronPortContext):
|
||||
|
||||
def __call__(self):
|
||||
ctxt = {}
|
||||
ports = config('ext-port')
|
||||
if ports:
|
||||
ports = [p.strip() for p in ports.split()]
|
||||
ports = self.resolve_ports(ports)
|
||||
if ports:
|
||||
ctxt = {"ext_port": ports[0]}
|
||||
napi_settings = NeutronAPIContext()()
|
||||
mtu = napi_settings.get('network_device_mtu')
|
||||
if mtu:
|
||||
ctxt['ext_port_mtu'] = mtu
|
||||
|
||||
return ctxt
|
||||
|
||||
|
||||
class DataPortContext(NeutronPortContext):
|
||||
|
||||
def __call__(self):
|
||||
ports = config('data-port')
|
||||
if ports:
|
||||
portmap = parse_data_port_mappings(ports)
|
||||
ports = portmap.values()
|
||||
resolved = self.resolve_ports(ports)
|
||||
normalized = {get_nic_hwaddr(port): port for port in resolved
|
||||
if port not in ports}
|
||||
normalized.update({port: port for port in resolved
|
||||
if port in ports})
|
||||
if resolved:
|
||||
return {bridge: normalized[port] for bridge, port in
|
||||
six.iteritems(portmap) if port in normalized.keys()}
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class PhyNICMTUContext(DataPortContext):
|
||||
|
||||
def __call__(self):
|
||||
ctxt = {}
|
||||
mappings = super(PhyNICMTUContext, self).__call__()
|
||||
if mappings and mappings.values():
|
||||
ports = mappings.values()
|
||||
napi_settings = NeutronAPIContext()()
|
||||
mtu = napi_settings.get('network_device_mtu')
|
||||
if mtu:
|
||||
ctxt["devs"] = '\\n'.join(ports)
|
||||
ctxt['mtu'] = mtu
|
||||
|
||||
return ctxt
|
||||
|
||||
|
||||
class NetworkServiceContext(OSContextGenerator):
|
||||
|
||||
def __init__(self, rel_name='quantum-network-service'):
|
||||
self.rel_name = rel_name
|
||||
self.interfaces = [rel_name]
|
||||
|
||||
def __call__(self):
|
||||
for rid in relation_ids(self.rel_name):
|
||||
for unit in related_units(rid):
|
||||
rdata = relation_get(rid=rid, unit=unit)
|
||||
ctxt = {
|
||||
'keystone_host': rdata.get('keystone_host'),
|
||||
'service_port': rdata.get('service_port'),
|
||||
'auth_port': rdata.get('auth_port'),
|
||||
'service_tenant': rdata.get('service_tenant'),
|
||||
'service_username': rdata.get('service_username'),
|
||||
'service_password': rdata.get('service_password'),
|
||||
'quantum_host': rdata.get('quantum_host'),
|
||||
'quantum_port': rdata.get('quantum_port'),
|
||||
'quantum_url': rdata.get('quantum_url'),
|
||||
'region': rdata.get('region'),
|
||||
'service_protocol':
|
||||
rdata.get('service_protocol') or 'http',
|
||||
'auth_protocol':
|
||||
rdata.get('auth_protocol') or 'http',
|
||||
}
|
||||
if context_complete(ctxt):
|
||||
return ctxt
|
||||
return {}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
# Various utilies for dealing with Neutron and the renaming from Quantum.
|
||||
|
||||
import six
|
||||
from subprocess import check_output
|
||||
|
||||
from charmhelpers.core.hookenv import (
|
||||
@ -237,3 +238,72 @@ def network_manager():
|
||||
else:
|
||||
# ensure accurate naming for all releases post-H
|
||||
return 'neutron'
|
||||
|
||||
|
||||
def parse_mappings(mappings):
|
||||
parsed = {}
|
||||
if mappings:
|
||||
mappings = mappings.split(' ')
|
||||
for m in mappings:
|
||||
p = m.partition(':')
|
||||
if p[1] == ':':
|
||||
parsed[p[0].strip()] = p[2].strip()
|
||||
|
||||
return parsed
|
||||
|
||||
|
||||
def parse_bridge_mappings(mappings):
|
||||
"""Parse bridge mappings.
|
||||
|
||||
Mappings must be a space-delimited list of provider:bridge mappings.
|
||||
|
||||
Returns dict of the form {provider:bridge}.
|
||||
"""
|
||||
return parse_mappings(mappings)
|
||||
|
||||
|
||||
def parse_data_port_mappings(mappings, default_bridge='br-data'):
|
||||
"""Parse data port mappings.
|
||||
|
||||
Mappings must be a space-delimited list of bridge:port mappings.
|
||||
|
||||
Returns dict of the form {bridge:port}.
|
||||
"""
|
||||
_mappings = parse_mappings(mappings)
|
||||
if not _mappings:
|
||||
if not mappings:
|
||||
return {}
|
||||
|
||||
# For backwards-compatibility we need to support port-only provided in
|
||||
# config.
|
||||
_mappings = {default_bridge: mappings.split(' ')[0]}
|
||||
|
||||
bridges = _mappings.keys()
|
||||
ports = _mappings.values()
|
||||
if len(set(bridges)) != len(bridges):
|
||||
raise Exception("It is not allowed to have more than one port "
|
||||
"configured on the same bridge")
|
||||
|
||||
if len(set(ports)) != len(ports):
|
||||
raise Exception("It is not allowed to have the same port configured "
|
||||
"on more than one bridge")
|
||||
|
||||
return _mappings
|
||||
|
||||
|
||||
def parse_vlan_range_mappings(mappings):
|
||||
"""Parse vlan range mappings.
|
||||
|
||||
Mappings must be a space-delimited list of provider:start:end mappings.
|
||||
|
||||
Returns dict of the form {provider: (start, end)}.
|
||||
"""
|
||||
_mappings = parse_mappings(mappings)
|
||||
if not _mappings:
|
||||
return {}
|
||||
|
||||
mappings = {}
|
||||
for p, r in six.iteritems(_mappings):
|
||||
mappings[p] = tuple(r.split(':'))
|
||||
|
||||
return mappings
|
||||
|
@ -0,0 +1,9 @@
|
||||
{% if auth_host -%}
|
||||
[keystone_authtoken]
|
||||
identity_uri = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }}/{{ auth_admin_prefix }}
|
||||
auth_uri = {{ service_protocol }}://{{ service_host }}:{{ service_port }}/{{ service_admin_prefix }}
|
||||
admin_tenant_name = {{ admin_tenant_name }}
|
||||
admin_user = {{ admin_user }}
|
||||
admin_password = {{ admin_password }}
|
||||
signing_dir = {{ signing_dir }}
|
||||
{% endif -%}
|
@ -0,0 +1,22 @@
|
||||
{% if rabbitmq_host or rabbitmq_hosts -%}
|
||||
[oslo_messaging_rabbit]
|
||||
rabbit_userid = {{ rabbitmq_user }}
|
||||
rabbit_virtual_host = {{ rabbitmq_virtual_host }}
|
||||
rabbit_password = {{ rabbitmq_password }}
|
||||
{% if rabbitmq_hosts -%}
|
||||
rabbit_hosts = {{ rabbitmq_hosts }}
|
||||
{% if rabbitmq_ha_queues -%}
|
||||
rabbit_ha_queues = True
|
||||
rabbit_durable_queues = False
|
||||
{% endif -%}
|
||||
{% else -%}
|
||||
rabbit_host = {{ rabbitmq_host }}
|
||||
{% endif -%}
|
||||
{% if rabbit_ssl_port -%}
|
||||
rabbit_use_ssl = True
|
||||
rabbit_port = {{ rabbit_ssl_port }}
|
||||
{% if rabbit_ssl_ca -%}
|
||||
kombu_ssl_ca_certs = {{ rabbit_ssl_ca }}
|
||||
{% endif -%}
|
||||
{% endif -%}
|
||||
{% endif -%}
|
@ -352,6 +352,17 @@ def relation_set(relation_id=None, relation_settings=None, **kwargs):
|
||||
flush(local_unit())
|
||||
|
||||
|
||||
def relation_clear(r_id=None):
|
||||
''' Clears any relation data already set on relation r_id '''
|
||||
settings = relation_get(rid=r_id,
|
||||
unit=local_unit())
|
||||
for setting in settings:
|
||||
if setting not in ['public-address', 'private-address']:
|
||||
settings[setting] = None
|
||||
relation_set(relation_id=r_id,
|
||||
**settings)
|
||||
|
||||
|
||||
@cached
|
||||
def relation_ids(reltype=None):
|
||||
"""A list of relation_ids"""
|
||||
@ -627,3 +638,29 @@ def leader_set(settings=None, **kwargs):
|
||||
cmd.append('{}={}'.format(k, v))
|
||||
|
||||
subprocess.check_call(cmd)
|
||||
|
||||
|
||||
@cached
|
||||
def action_get(key=None):
|
||||
"""Gets the value of an action parameter, or all key/value param pairs"""
|
||||
cmd = ['action-get']
|
||||
if key is not None:
|
||||
cmd.append(key)
|
||||
cmd.append('--format=json')
|
||||
action_data = json.loads(subprocess.check_output(cmd).decode('UTF-8'))
|
||||
return action_data
|
||||
|
||||
|
||||
def action_set(values):
|
||||
"""Sets the values to be returned after the action finishes"""
|
||||
cmd = ['action-set']
|
||||
for k, v in list(values.items()):
|
||||
cmd.append('{}={}'.format(k, v))
|
||||
subprocess.check_call(cmd)
|
||||
|
||||
|
||||
def action_fail(message):
|
||||
"""Sets the action status to failed and sets the error message.
|
||||
|
||||
The results set by action_set are preserved."""
|
||||
subprocess.check_call(['action-fail', message])
|
||||
|
@ -339,12 +339,16 @@ def lsb_release():
|
||||
def pwgen(length=None):
|
||||
"""Generate a random pasword."""
|
||||
if length is None:
|
||||
# A random length is ok to use a weak PRNG
|
||||
length = random.choice(range(35, 45))
|
||||
alphanumeric_chars = [
|
||||
l for l in (string.ascii_letters + string.digits)
|
||||
if l not in 'l0QD1vAEIOUaeiou']
|
||||
# Use a crypto-friendly PRNG (e.g. /dev/urandom) for making the
|
||||
# actual password
|
||||
random_generator = random.SystemRandom()
|
||||
random_chars = [
|
||||
random.choice(alphanumeric_chars) for _ in range(length)]
|
||||
random_generator.choice(alphanumeric_chars) for _ in range(length)]
|
||||
return(''.join(random_chars))
|
||||
|
||||
|
||||
|
@ -139,7 +139,7 @@ class MysqlRelation(RelationContext):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.required_keys = ['host', 'user', 'password', 'database']
|
||||
super(HttpRelation).__init__(self, *args, **kwargs)
|
||||
RelationContext.__init__(self, *args, **kwargs)
|
||||
|
||||
|
||||
class HttpRelation(RelationContext):
|
||||
@ -154,7 +154,7 @@ class HttpRelation(RelationContext):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.required_keys = ['host', 'port']
|
||||
super(HttpRelation).__init__(self, *args, **kwargs)
|
||||
RelationContext.__init__(self, *args, **kwargs)
|
||||
|
||||
def provide_data(self):
|
||||
return {
|
||||
|
Loading…
Reference in New Issue
Block a user