Rebase and resync
This commit is contained in:
commit
d3b6038871
@ -1,4 +1,4 @@
|
||||
branch: lp:~james-page/charm-helpers/multiple-https-networks
|
||||
branch: lp:charm-helpers
|
||||
destination: hooks/charmhelpers
|
||||
include:
|
||||
- core
|
||||
|
13
config.yaml
13
config.yaml
@ -161,4 +161,15 @@ options:
|
||||
192.168.0.0/24)
|
||||
.
|
||||
This network will be used for public endpoints.
|
||||
|
||||
prefer-ipv6:
|
||||
type: boolean
|
||||
default: False
|
||||
description: |
|
||||
If True enables IPv6 support. The charm will expect network interfaces
|
||||
to be configured with an IPv6 address. If set to False (default) IPv4
|
||||
is expected.
|
||||
.
|
||||
NOTE: these charms do not currently support IPv6 privacy extension. In
|
||||
order for this charm to function correctly, the privacy extension must be
|
||||
disabled and a non-temporary address must be configured/available on
|
||||
your network interface.
|
||||
|
@ -1,11 +1,16 @@
|
||||
import glob
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from functools import partial
|
||||
|
||||
from charmhelpers.core.hookenv import unit_get
|
||||
from charmhelpers.fetch import apt_install
|
||||
from charmhelpers.core.hookenv import (
|
||||
ERROR, log,
|
||||
WARNING,
|
||||
ERROR,
|
||||
log
|
||||
)
|
||||
|
||||
try:
|
||||
@ -164,13 +169,14 @@ def format_ipv6_addr(address):
|
||||
if is_ipv6(address):
|
||||
address = "[%s]" % address
|
||||
else:
|
||||
log("Not an valid ipv6 address: %s" % address,
|
||||
level=ERROR)
|
||||
log("Not a valid ipv6 address: %s" % address, level=WARNING)
|
||||
address = None
|
||||
|
||||
return address
|
||||
|
||||
|
||||
def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False, fatal=True, exc_list=None):
|
||||
def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False,
|
||||
fatal=True, exc_list=None):
|
||||
"""
|
||||
Return the assigned IP address for a given interface, if any, or [].
|
||||
"""
|
||||
@ -210,26 +216,105 @@ def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False, fatal=T
|
||||
if 'addr' in entry and entry['addr'] not in exc_list:
|
||||
addresses.append(entry['addr'])
|
||||
if fatal and not addresses:
|
||||
raise Exception("Interface '%s' doesn't have any %s addresses." % (iface, inet_type))
|
||||
raise Exception("Interface '%s' doesn't have any %s addresses." %
|
||||
(iface, inet_type))
|
||||
return addresses
|
||||
|
||||
get_ipv4_addr = partial(get_iface_addr, inet_type='AF_INET')
|
||||
|
||||
|
||||
def get_ipv6_addr(iface='eth0', inc_aliases=False, fatal=True, exc_list=None):
|
||||
def get_iface_from_addr(addr):
|
||||
"""Work out on which interface the provided address is configured."""
|
||||
for iface in netifaces.interfaces():
|
||||
addresses = netifaces.ifaddresses(iface)
|
||||
for inet_type in addresses:
|
||||
for _addr in addresses[inet_type]:
|
||||
_addr = _addr['addr']
|
||||
# link local
|
||||
ll_key = re.compile("(.+)%.*")
|
||||
raw = re.match(ll_key, _addr)
|
||||
if raw:
|
||||
_addr = raw.group(1)
|
||||
if _addr == addr:
|
||||
log("Address '%s' is configured on iface '%s'" %
|
||||
(addr, iface))
|
||||
return iface
|
||||
|
||||
msg = "Unable to infer net iface on which '%s' is configured" % (addr)
|
||||
raise Exception(msg)
|
||||
|
||||
|
||||
def sniff_iface(f):
|
||||
"""If no iface provided, inject net iface inferred from unit private
|
||||
address.
|
||||
"""
|
||||
Return the assigned IPv6 address for a given interface, if any, or [].
|
||||
def iface_sniffer(*args, **kwargs):
|
||||
if not kwargs.get('iface', None):
|
||||
kwargs['iface'] = get_iface_from_addr(unit_get('private-address'))
|
||||
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return iface_sniffer
|
||||
|
||||
|
||||
@sniff_iface
|
||||
def get_ipv6_addr(iface=None, inc_aliases=False, fatal=True, exc_list=None,
|
||||
dynamic_only=True):
|
||||
"""Get assigned IPv6 address for a given interface.
|
||||
|
||||
Returns list of addresses found. If no address found, returns empty list.
|
||||
|
||||
If iface is None, we infer the current primary interface by doing a reverse
|
||||
lookup on the unit private-address.
|
||||
|
||||
We currently only support scope global IPv6 addresses i.e. non-temporary
|
||||
addresses. If no global IPv6 address is found, return the first one found
|
||||
in the ipv6 address list.
|
||||
"""
|
||||
addresses = get_iface_addr(iface=iface, inet_type='AF_INET6',
|
||||
inc_aliases=inc_aliases, fatal=fatal,
|
||||
exc_list=exc_list)
|
||||
remotly_addressable = []
|
||||
for address in addresses:
|
||||
if not address.startswith('fe80'):
|
||||
remotly_addressable.append(address)
|
||||
if fatal and not remotly_addressable:
|
||||
raise Exception("Interface '%s' doesn't have global ipv6 address." % iface)
|
||||
return remotly_addressable
|
||||
|
||||
if addresses:
|
||||
global_addrs = []
|
||||
for addr in addresses:
|
||||
key_scope_link_local = re.compile("^fe80::..(.+)%(.+)")
|
||||
m = re.match(key_scope_link_local, addr)
|
||||
if m:
|
||||
eui_64_mac = m.group(1)
|
||||
iface = m.group(2)
|
||||
else:
|
||||
global_addrs.append(addr)
|
||||
|
||||
if global_addrs:
|
||||
# Make sure any found global addresses are not temporary
|
||||
cmd = ['ip', 'addr', 'show', iface]
|
||||
out = subprocess.check_output(cmd)
|
||||
if dynamic_only:
|
||||
key = re.compile("inet6 (.+)/[0-9]+ scope global dynamic.*")
|
||||
else:
|
||||
key = re.compile("inet6 (.+)/[0-9]+ scope global.*")
|
||||
|
||||
addrs = []
|
||||
for line in out.split('\n'):
|
||||
line = line.strip()
|
||||
m = re.match(key, line)
|
||||
if m and 'temporary' not in line:
|
||||
# Return the first valid address we find
|
||||
for addr in global_addrs:
|
||||
if m.group(1) == addr:
|
||||
if not dynamic_only or \
|
||||
m.group(1).endswith(eui_64_mac):
|
||||
addrs.append(addr)
|
||||
|
||||
if addrs:
|
||||
return addrs
|
||||
|
||||
if fatal:
|
||||
raise Exception("Interface '%s' doesn't have a scope global "
|
||||
"non-temporary ipv6 address." % iface)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
def get_bridges(vnic_dir='/sys/devices/virtual/net'):
|
||||
|
@ -10,32 +10,62 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
||||
that is specifically for use by OpenStack charms.
|
||||
"""
|
||||
|
||||
def __init__(self, series=None, openstack=None, source=None):
|
||||
def __init__(self, series=None, openstack=None, source=None, stable=True):
|
||||
"""Initialize the deployment environment."""
|
||||
super(OpenStackAmuletDeployment, self).__init__(series)
|
||||
self.openstack = openstack
|
||||
self.source = source
|
||||
self.stable = stable
|
||||
# Note(coreycb): this needs to be changed when new next branches come
|
||||
# out.
|
||||
self.current_next = "trusty"
|
||||
|
||||
def _determine_branch_locations(self, other_services):
|
||||
"""Determine the branch locations for the other services.
|
||||
|
||||
Determine if the local branch being tested is derived from its
|
||||
stable or next (dev) branch, and based on this, use the corresonding
|
||||
stable or next branches for the other_services."""
|
||||
base_charms = ['mysql', 'mongodb', 'rabbitmq-server']
|
||||
|
||||
if self.stable:
|
||||
for svc in other_services:
|
||||
temp = 'lp:charms/{}'
|
||||
svc['location'] = temp.format(svc['name'])
|
||||
else:
|
||||
for svc in other_services:
|
||||
if svc['name'] in base_charms:
|
||||
temp = 'lp:charms/{}'
|
||||
svc['location'] = temp.format(svc['name'])
|
||||
else:
|
||||
temp = 'lp:~openstack-charmers/charms/{}/{}/next'
|
||||
svc['location'] = temp.format(self.current_next,
|
||||
svc['name'])
|
||||
return other_services
|
||||
|
||||
def _add_services(self, this_service, other_services):
|
||||
"""Add services to the deployment and set openstack-origin."""
|
||||
"""Add services to the deployment and set openstack-origin/source."""
|
||||
other_services = self._determine_branch_locations(other_services)
|
||||
|
||||
super(OpenStackAmuletDeployment, self)._add_services(this_service,
|
||||
other_services)
|
||||
name = 0
|
||||
|
||||
services = other_services
|
||||
services.append(this_service)
|
||||
use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph']
|
||||
use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph',
|
||||
'ceph-osd', 'ceph-radosgw']
|
||||
|
||||
if self.openstack:
|
||||
for svc in services:
|
||||
if svc[name] not in use_source:
|
||||
if svc['name'] not in use_source:
|
||||
config = {'openstack-origin': self.openstack}
|
||||
self.d.configure(svc[name], config)
|
||||
self.d.configure(svc['name'], config)
|
||||
|
||||
if self.source:
|
||||
for svc in services:
|
||||
if svc[name] in use_source:
|
||||
if svc['name'] in use_source:
|
||||
config = {'source': self.source}
|
||||
self.d.configure(svc[name], config)
|
||||
self.d.configure(svc['name'], config)
|
||||
|
||||
def _configure_services(self, configs):
|
||||
"""Configure all of the services."""
|
||||
|
@ -187,15 +187,16 @@ class OpenStackAmuletUtils(AmuletUtils):
|
||||
|
||||
f = opener.open("http://download.cirros-cloud.net/version/released")
|
||||
version = f.read().strip()
|
||||
cirros_img = "tests/cirros-{}-x86_64-disk.img".format(version)
|
||||
cirros_img = "cirros-{}-x86_64-disk.img".format(version)
|
||||
local_path = os.path.join('tests', cirros_img)
|
||||
|
||||
if not os.path.exists(cirros_img):
|
||||
if not os.path.exists(local_path):
|
||||
cirros_url = "http://{}/{}/{}".format("download.cirros-cloud.net",
|
||||
version, cirros_img)
|
||||
opener.retrieve(cirros_url, cirros_img)
|
||||
opener.retrieve(cirros_url, local_path)
|
||||
f.close()
|
||||
|
||||
with open(cirros_img) as f:
|
||||
with open(local_path) as f:
|
||||
image = glance.images.create(name=image_name, is_public=True,
|
||||
disk_format='qcow2',
|
||||
container_format='bare', data=f)
|
||||
|
@ -52,8 +52,9 @@ from charmhelpers.contrib.openstack.neutron import (
|
||||
from charmhelpers.contrib.network.ip import (
|
||||
get_address_in_network,
|
||||
get_ipv6_addr,
|
||||
is_address_in_network,
|
||||
get_netmask_for_address
|
||||
get_netmask_for_address,
|
||||
format_ipv6_addr,
|
||||
is_address_in_network
|
||||
)
|
||||
|
||||
CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt'
|
||||
@ -175,8 +176,10 @@ class SharedDBContext(OSContextGenerator):
|
||||
for rid in relation_ids('shared-db'):
|
||||
for unit in related_units(rid):
|
||||
rdata = relation_get(rid=rid, unit=unit)
|
||||
host = rdata.get('db_host')
|
||||
host = format_ipv6_addr(host) or host
|
||||
ctxt = {
|
||||
'database_host': rdata.get('db_host'),
|
||||
'database_host': host,
|
||||
'database': self.database,
|
||||
'database_user': self.user,
|
||||
'database_password': rdata.get(password_setting),
|
||||
@ -252,10 +255,15 @@ class IdentityServiceContext(OSContextGenerator):
|
||||
for rid in relation_ids('identity-service'):
|
||||
for unit in related_units(rid):
|
||||
rdata = relation_get(rid=rid, unit=unit)
|
||||
serv_host = rdata.get('service_host')
|
||||
serv_host = format_ipv6_addr(serv_host) or serv_host
|
||||
auth_host = rdata.get('auth_host')
|
||||
auth_host = format_ipv6_addr(auth_host) or auth_host
|
||||
|
||||
ctxt = {
|
||||
'service_port': rdata.get('service_port'),
|
||||
'service_host': rdata.get('service_host'),
|
||||
'auth_host': rdata.get('auth_host'),
|
||||
'service_host': serv_host,
|
||||
'auth_host': auth_host,
|
||||
'auth_port': rdata.get('auth_port'),
|
||||
'admin_tenant_name': rdata.get('service_tenant'),
|
||||
'admin_user': rdata.get('service_username'),
|
||||
@ -304,11 +312,13 @@ class AMQPContext(OSContextGenerator):
|
||||
for unit in related_units(rid):
|
||||
if relation_get('clustered', rid=rid, unit=unit):
|
||||
ctxt['clustered'] = True
|
||||
ctxt['rabbitmq_host'] = relation_get('vip', rid=rid,
|
||||
unit=unit)
|
||||
vip = relation_get('vip', rid=rid, unit=unit)
|
||||
vip = format_ipv6_addr(vip) or vip
|
||||
ctxt['rabbitmq_host'] = vip
|
||||
else:
|
||||
ctxt['rabbitmq_host'] = relation_get('private-address',
|
||||
rid=rid, unit=unit)
|
||||
host = relation_get('private-address', rid=rid, unit=unit)
|
||||
host = format_ipv6_addr(host) or host
|
||||
ctxt['rabbitmq_host'] = host
|
||||
ctxt.update({
|
||||
'rabbitmq_user': username,
|
||||
'rabbitmq_password': relation_get('password', rid=rid,
|
||||
@ -347,8 +357,9 @@ class AMQPContext(OSContextGenerator):
|
||||
and len(related_units(rid)) > 1:
|
||||
rabbitmq_hosts = []
|
||||
for unit in related_units(rid):
|
||||
rabbitmq_hosts.append(relation_get('private-address',
|
||||
rid=rid, unit=unit))
|
||||
host = relation_get('private-address', rid=rid, unit=unit)
|
||||
host = format_ipv6_addr(host) or host
|
||||
rabbitmq_hosts.append(host)
|
||||
ctxt['rabbitmq_hosts'] = ','.join(rabbitmq_hosts)
|
||||
if not context_complete(ctxt):
|
||||
return {}
|
||||
@ -377,6 +388,7 @@ class CephContext(OSContextGenerator):
|
||||
ceph_addr = \
|
||||
relation_get('ceph-public-address', rid=rid, unit=unit) or \
|
||||
relation_get('private-address', rid=rid, unit=unit)
|
||||
ceph_addr = format_ipv6_addr(ceph_addr) or ceph_addr
|
||||
mon_hosts.append(ceph_addr)
|
||||
|
||||
ctxt = {
|
||||
@ -413,8 +425,9 @@ class HAProxyContext(OSContextGenerator):
|
||||
return {}
|
||||
|
||||
l_unit = local_unit().replace('/', '-')
|
||||
|
||||
if config('prefer-ipv6'):
|
||||
addr = get_ipv6_addr()
|
||||
addr = get_ipv6_addr(exc_list=[config('vip')])[0]
|
||||
else:
|
||||
addr = unit_get('private-address')
|
||||
|
||||
@ -443,7 +456,7 @@ class HAProxyContext(OSContextGenerator):
|
||||
|
||||
# NOTE(jamespage) no split configurations found, just use
|
||||
# private addresses
|
||||
if len(cluster_hosts) < 1:
|
||||
if not cluster_hosts:
|
||||
cluster_hosts[addr] = {}
|
||||
cluster_hosts[addr]['network'] = "{}/{}".format(
|
||||
addr,
|
||||
@ -463,6 +476,11 @@ class HAProxyContext(OSContextGenerator):
|
||||
'frontends': cluster_hosts,
|
||||
}
|
||||
|
||||
if config('haproxy-server-timeout'):
|
||||
ctxt['haproxy_server_timeout'] = config('haproxy-server-timeout')
|
||||
if config('haproxy-client-timeout'):
|
||||
ctxt['haproxy_client_timeout'] = config('haproxy-client-timeout')
|
||||
|
||||
if config('prefer-ipv6'):
|
||||
ctxt['local_host'] = 'ip6-localhost'
|
||||
ctxt['haproxy_host'] = '::'
|
||||
@ -870,3 +888,16 @@ class SyslogContext(OSContextGenerator):
|
||||
'use_syslog': config('use-syslog')
|
||||
}
|
||||
return ctxt
|
||||
|
||||
|
||||
class BindHostContext(OSContextGenerator):
|
||||
|
||||
def __call__(self):
|
||||
if config('prefer-ipv6'):
|
||||
return {
|
||||
'bind_host': '::'
|
||||
}
|
||||
else:
|
||||
return {
|
||||
'bind_host': '0.0.0.0'
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ def resolve_address(endpoint_type=PUBLIC):
|
||||
resolved_address = vip
|
||||
else:
|
||||
if config('prefer-ipv6'):
|
||||
fallback_addr = get_ipv6_addr()
|
||||
fallback_addr = get_ipv6_addr(exc_list=[config('vip')])[0]
|
||||
else:
|
||||
fallback_addr = unit_get(_address_map[endpoint_type]['fallback'])
|
||||
resolved_address = get_address_in_network(
|
||||
|
@ -14,8 +14,17 @@ defaults
|
||||
retries 3
|
||||
timeout queue 1000
|
||||
timeout connect 1000
|
||||
{% if haproxy_client_timeout -%}
|
||||
timeout client {{ haproxy_client_timeout }}
|
||||
{% else -%}
|
||||
timeout client 30000
|
||||
{% endif -%}
|
||||
|
||||
{% if haproxy_server_timeout -%}
|
||||
timeout server {{ haproxy_server_timeout }}
|
||||
{% else -%}
|
||||
timeout server 30000
|
||||
{% endif -%}
|
||||
|
||||
listen stats {{ stat_port }}
|
||||
mode http
|
||||
|
@ -4,6 +4,7 @@
|
||||
from collections import OrderedDict
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
@ -13,7 +14,9 @@ from charmhelpers.core.hookenv import (
|
||||
log as juju_log,
|
||||
charm_dir,
|
||||
ERROR,
|
||||
INFO
|
||||
INFO,
|
||||
relation_ids,
|
||||
relation_set
|
||||
)
|
||||
|
||||
from charmhelpers.contrib.storage.linux.lvm import (
|
||||
@ -22,6 +25,10 @@ from charmhelpers.contrib.storage.linux.lvm import (
|
||||
remove_lvm_physical_volume,
|
||||
)
|
||||
|
||||
from charmhelpers.contrib.network.ip import (
|
||||
get_ipv6_addr
|
||||
)
|
||||
|
||||
from charmhelpers.core.host import lsb_release, mounts, umount
|
||||
from charmhelpers.fetch import apt_install, apt_cache
|
||||
from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk
|
||||
@ -457,3 +464,21 @@ def get_hostname(address, fqdn=True):
|
||||
return result
|
||||
else:
|
||||
return result.split('.')[0]
|
||||
|
||||
|
||||
def sync_db_with_multi_ipv6_addresses(database, database_user,
|
||||
relation_prefix=None):
|
||||
hosts = get_ipv6_addr(dynamic_only=False)
|
||||
|
||||
kwargs = {'database': database,
|
||||
'username': database_user,
|
||||
'hostname': json.dumps(hosts)}
|
||||
|
||||
if relation_prefix:
|
||||
keys = kwargs.keys()
|
||||
for key in keys:
|
||||
kwargs["%s_%s" % (relation_prefix, key)] = kwargs[key]
|
||||
del kwargs[key]
|
||||
|
||||
for rid in relation_ids('shared-db'):
|
||||
relation_set(relation_id=rid, **kwargs)
|
||||
|
@ -3,7 +3,7 @@ from charmhelpers.core.hookenv import (
|
||||
relation_ids,
|
||||
service_name,
|
||||
related_units,
|
||||
relation_get,
|
||||
relation_get
|
||||
)
|
||||
|
||||
from charmhelpers.contrib.openstack.context import (
|
||||
|
@ -20,7 +20,8 @@ from cinder_utils import (
|
||||
CLUSTER_RES,
|
||||
CINDER_CONF,
|
||||
CINDER_API_CONF,
|
||||
ceph_config_file
|
||||
ceph_config_file,
|
||||
setup_ipv6
|
||||
)
|
||||
|
||||
from charmhelpers.core.hookenv import (
|
||||
@ -38,11 +39,17 @@ from charmhelpers.core.hookenv import (
|
||||
ERROR,
|
||||
)
|
||||
|
||||
from charmhelpers.fetch import apt_install, apt_update
|
||||
from charmhelpers.fetch import (
|
||||
apt_install,
|
||||
apt_update
|
||||
)
|
||||
|
||||
from charmhelpers.core.host import lsb_release, restart_on_change
|
||||
|
||||
from charmhelpers.contrib.openstack.utils import (
|
||||
configure_installation_source, openstack_upgrade_available)
|
||||
configure_installation_source,
|
||||
openstack_upgrade_available,
|
||||
sync_db_with_multi_ipv6_addresses)
|
||||
|
||||
from charmhelpers.contrib.storage.linux.ceph import ensure_ceph_keyring
|
||||
|
||||
@ -55,7 +62,9 @@ from charmhelpers.payload.execd import execd_preinstall
|
||||
from charmhelpers.contrib.network.ip import (
|
||||
get_iface_for_address,
|
||||
get_netmask_for_address,
|
||||
get_address_in_network
|
||||
get_address_in_network,
|
||||
get_ipv6_addr,
|
||||
is_ipv6
|
||||
)
|
||||
from charmhelpers.contrib.openstack.ip import (
|
||||
canonical_url,
|
||||
@ -85,6 +94,12 @@ def install():
|
||||
@restart_on_change(restart_map(), stopstart=True)
|
||||
def config_changed():
|
||||
conf = config()
|
||||
|
||||
if conf['prefer-ipv6']:
|
||||
setup_ipv6()
|
||||
sync_db_with_multi_ipv6_addresses(config('database'),
|
||||
config('database-user'))
|
||||
|
||||
if (service_enabled('volume') and
|
||||
conf['block-device'] not in [None, 'None', 'none']):
|
||||
block_devices = conf['block-device'].split()
|
||||
@ -115,9 +130,15 @@ def db_joined():
|
||||
log(e, level=ERROR)
|
||||
raise Exception(e)
|
||||
|
||||
conf = config()
|
||||
relation_set(database=conf['database'], username=conf['database-user'],
|
||||
hostname=unit_get('private-address'))
|
||||
if config('prefer-ipv6'):
|
||||
sync_db_with_multi_ipv6_addresses(config('database'),
|
||||
config('database-user'))
|
||||
else:
|
||||
host = unit_get('private-address')
|
||||
conf = config()
|
||||
relation_set(database=conf['database'],
|
||||
username=conf['database-user'],
|
||||
hostname=host)
|
||||
|
||||
|
||||
@hooks.hook('pgsql-db-relation-joined')
|
||||
@ -262,6 +283,10 @@ def cluster_joined(relation_id=None):
|
||||
relation_id=relation_id,
|
||||
relation_settings={'{}-address'.format(addr_type): address}
|
||||
)
|
||||
if config('prefer-ipv6'):
|
||||
private_addr = get_ipv6_addr(exc_list=[config('vip')])[0]
|
||||
relation_set(relation_id=relation_id,
|
||||
relation_settings={'private-address': address})
|
||||
|
||||
|
||||
@hooks.hook('cluster-relation-changed',
|
||||
@ -273,7 +298,7 @@ def cluster_changed():
|
||||
|
||||
@hooks.hook('ha-relation-joined')
|
||||
def ha_joined():
|
||||
config = get_hacluster_config()
|
||||
cluster_config = get_hacluster_config()
|
||||
|
||||
resources = {
|
||||
'res_cinder_haproxy': 'lsb:haproxy'
|
||||
@ -284,14 +309,22 @@ def ha_joined():
|
||||
}
|
||||
|
||||
vip_group = []
|
||||
for vip in config['vip'].split():
|
||||
for vip in cluster_config['vip'].split():
|
||||
if is_ipv6(vip):
|
||||
res_cinder_vip = 'ocf:heartbeat:IPv6addr'
|
||||
vip_params = 'ipv6addr'
|
||||
else:
|
||||
res_cinder_vip = 'ocf:heartbeat:IPaddr2'
|
||||
vip_params = 'ip'
|
||||
|
||||
iface = get_iface_for_address(vip)
|
||||
if iface is not None:
|
||||
vip_key = 'res_cinder_{}_vip'.format(iface)
|
||||
resources[vip_key] = 'ocf:heartbeat:IPaddr2'
|
||||
resources[vip_key] = res_cinder_vip
|
||||
resource_params[vip_key] = (
|
||||
'params ip="{vip}" cidr_netmask="{netmask}"'
|
||||
' nic="{iface}"'.format(vip=vip,
|
||||
'params {ip}="{vip}" cidr_netmask="{netmask}"'
|
||||
' nic="{iface}"'.format(ip=vip_params,
|
||||
vip=vip,
|
||||
iface=iface,
|
||||
netmask=get_netmask_for_address(vip))
|
||||
)
|
||||
@ -307,8 +340,8 @@ def ha_joined():
|
||||
'cl_cinder_haproxy': 'res_cinder_haproxy'
|
||||
}
|
||||
relation_set(init_services=init_services,
|
||||
corosync_bindiface=config['ha-bindiface'],
|
||||
corosync_mcastport=config['ha-mcastport'],
|
||||
corosync_bindiface=cluster_config['ha-bindiface'],
|
||||
corosync_mcastport=cluster_config['ha-mcastport'],
|
||||
resources=resources,
|
||||
resource_params=resource_params,
|
||||
clones=clones)
|
||||
|
@ -14,7 +14,8 @@ from charmhelpers.core.hookenv import (
|
||||
from charmhelpers.fetch import (
|
||||
apt_upgrade,
|
||||
apt_update,
|
||||
apt_install
|
||||
apt_install,
|
||||
add_source
|
||||
)
|
||||
|
||||
from charmhelpers.core.host import (
|
||||
@ -22,7 +23,8 @@ from charmhelpers.core.host import (
|
||||
umount,
|
||||
service_stop,
|
||||
service_start,
|
||||
mkdir
|
||||
mkdir,
|
||||
lsb_release
|
||||
)
|
||||
|
||||
from charmhelpers.contrib.storage.linux.ceph import (
|
||||
@ -128,7 +130,8 @@ CONFIG_FILES = OrderedDict([
|
||||
config_file=CINDER_CONF),
|
||||
cinder_contexts.StorageBackendContext(),
|
||||
cinder_contexts.LoggingConfigContext(),
|
||||
context.IdentityServiceContext()],
|
||||
context.IdentityServiceContext(),
|
||||
context.BindHostContext()],
|
||||
'services': ['cinder-api', 'cinder-volume',
|
||||
'cinder-scheduler', 'haproxy']
|
||||
}),
|
||||
@ -425,3 +428,19 @@ def do_openstack_upgrade(configs):
|
||||
if eligible_leader(CLUSTER_RES):
|
||||
migrate_database()
|
||||
[service_start(s) for s in services()]
|
||||
|
||||
|
||||
def setup_ipv6():
|
||||
ubuntu_rel = lsb_release()['DISTRIB_CODENAME'].lower()
|
||||
if ubuntu_rel < "trusty":
|
||||
raise Exception("IPv6 is not supported in the charms for Ubuntu "
|
||||
"versions less than Trusty 14.04")
|
||||
|
||||
# NOTE(xianghui): Need to install haproxy(1.5.3) from trusty-backports
|
||||
# to support ipv6 address, so check is required to make sure not
|
||||
# breaking other versions, IPv6 only support for >= Trusty
|
||||
if ubuntu_rel == 'trusty':
|
||||
add_source('deb http://archive.ubuntu.com/ubuntu trusty-backports'
|
||||
' main')
|
||||
apt_update()
|
||||
apt_install('haproxy/trusty-backports', fatal=True)
|
||||
|
@ -34,6 +34,7 @@ host = {{ host }}
|
||||
rbd_user = {{ rbd_user }}
|
||||
{% endif -%}
|
||||
|
||||
osapi_volume_listen = {{ bind_host }}
|
||||
{% if osapi_volume_listen_port -%}
|
||||
osapi_volume_listen_port = {{ osapi_volume_listen_port }}
|
||||
{% endif -%}
|
||||
|
@ -95,19 +95,25 @@ class TestCinderContext(CharmTestCase):
|
||||
self.assertEquals(contexts.StorageBackendContext()(),
|
||||
{'backends': 'cinder-ceph,cinder-vmware'})
|
||||
|
||||
@patch('charmhelpers.contrib.openstack.context.config')
|
||||
@patch('charmhelpers.contrib.openstack.context.is_clustered')
|
||||
@patch('charmhelpers.contrib.openstack.context.determine_apache_port')
|
||||
@patch('charmhelpers.contrib.openstack.context.determine_api_port')
|
||||
@patch('charmhelpers.contrib.openstack.context.unit_get')
|
||||
@patch('charmhelpers.contrib.openstack.context.https')
|
||||
mod_ch_context = 'charmhelpers.contrib.openstack.context'
|
||||
|
||||
@patch('%s.ApacheSSLContext.canonical_names' % (mod_ch_context))
|
||||
@patch('%s.ApacheSSLContext.configure_ca' % (mod_ch_context))
|
||||
@patch('%s.config' % (mod_ch_context))
|
||||
@patch('%s.is_clustered' % (mod_ch_context))
|
||||
@patch('%s.determine_apache_port' % (mod_ch_context))
|
||||
@patch('%s.determine_api_port' % (mod_ch_context))
|
||||
@patch('%s.unit_get' % (mod_ch_context))
|
||||
@patch('%s.https' % (mod_ch_context))
|
||||
@patch.object(utils, 'service_enabled')
|
||||
def test_apache_ssl_context_service_enabled(self, service_enabled,
|
||||
mock_https, mock_unit_get,
|
||||
mock_determine_api_port,
|
||||
mock_determine_apache_port,
|
||||
mock_is_clustered,
|
||||
mock_config):
|
||||
mock_hookenv,
|
||||
mock_configure_ca,
|
||||
mock_cfg_canonical_names):
|
||||
mock_https.return_value = True
|
||||
mock_unit_get.return_value = '1.2.3.4'
|
||||
mock_determine_api_port.return_value = '12'
|
||||
@ -122,7 +128,6 @@ class TestCinderContext(CharmTestCase):
|
||||
service_enabled.return_value = False
|
||||
self.assertEquals(ctxt(), {})
|
||||
self.assertFalse(mock_https.called)
|
||||
|
||||
service_enabled.return_value = True
|
||||
self.assertEquals(ctxt(), {'endpoints': [('1.2.3.4', '1.2.3.4',
|
||||
34, 12)],
|
||||
|
@ -16,6 +16,7 @@ utils.restart_map.return_value = RESTART_MAP
|
||||
utils.register_configs = MagicMock()
|
||||
|
||||
import cinder_hooks as hooks
|
||||
hooks.hooks._config_save = False
|
||||
|
||||
hooks.hooks._config_save = False
|
||||
|
||||
@ -62,6 +63,8 @@ TO_PATCH = [
|
||||
'eligible_leader',
|
||||
'get_hacluster_config',
|
||||
'execd_preinstall',
|
||||
'get_ipv6_addr',
|
||||
'sync_db_with_multi_ipv6_addresses'
|
||||
]
|
||||
|
||||
|
||||
@ -261,7 +264,7 @@ class TestJoinedHooks(CharmTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestJoinedHooks, self).setUp(hooks, TO_PATCH)
|
||||
self.config.side_effect = self.test_config.get_all
|
||||
self.config.side_effect = self.test_config.get
|
||||
|
||||
def test_db_joined(self):
|
||||
'It properly requests access to a shared-db service'
|
||||
@ -272,6 +275,18 @@ class TestJoinedHooks(CharmTestCase):
|
||||
'hostname': 'cindernode1', 'database': 'cinder'}
|
||||
self.relation_set.assert_called_with(**expected)
|
||||
|
||||
def test_db_joined_with_ipv6(self):
|
||||
'It properly requests access to a shared-db service'
|
||||
self.unit_get.return_value = 'cindernode1'
|
||||
self.get_ipv6_addr.return_value = ['2001:db8:1::1']
|
||||
self.sync_db_with_multi_ipv6_addresses.return_value = MagicMock()
|
||||
self.is_relation_made.return_value = False
|
||||
self.test_config.set('prefer-ipv6', True)
|
||||
self.test_config.set('vip', 'dummy_vip')
|
||||
hooks.hooks.execute(['hooks/shared-db-relation-joined'])
|
||||
self.sync_db_with_multi_ipv6_addresses.assert_called_with_once(
|
||||
'cinder', 'cinder')
|
||||
|
||||
def test_db_joined_with_postgresql(self):
|
||||
self.is_relation_made.return_value = True
|
||||
|
||||
|
@ -12,6 +12,7 @@ utils.register_configs = MagicMock()
|
||||
utils.service_enabled = MagicMock()
|
||||
|
||||
import cinder_hooks as hooks
|
||||
hooks.hooks._config_save = False
|
||||
|
||||
# Unpatch it now that its loaded.
|
||||
utils.register_configs = _register_configs
|
||||
@ -62,7 +63,7 @@ class TestClusterHooks(CharmTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestClusterHooks, self).setUp(hooks, TO_PATCH)
|
||||
self.config.side_effect = self.test_config.get_all
|
||||
self.config.side_effect = self.test_config.get
|
||||
|
||||
@patch('charmhelpers.core.host.service')
|
||||
@patch('charmhelpers.core.host.file_hash')
|
||||
@ -121,6 +122,8 @@ class TestClusterHooks(CharmTestCase):
|
||||
'vip_iface': 'eth101',
|
||||
'vip_cidr': '19',
|
||||
}
|
||||
|
||||
self.test_config.set('prefer-ipv6', 'False')
|
||||
self.get_hacluster_config.return_value = conf
|
||||
self.get_iface_for_address.return_value = 'eth101'
|
||||
self.get_netmask_for_address.return_value = '255.255.224.0'
|
||||
@ -146,6 +149,40 @@ class TestClusterHooks(CharmTestCase):
|
||||
call(**ex_args)
|
||||
])
|
||||
|
||||
def test_ha_joined_complete_config_with_ipv6(self):
|
||||
'Ensure hacluster subordinate receives all relevant config'
|
||||
conf = {
|
||||
'ha-bindiface': 'eth100',
|
||||
'ha-mcastport': '37373',
|
||||
'vip': '2001:db8:1::1',
|
||||
'vip_iface': 'eth101',
|
||||
'vip_cidr': '64',
|
||||
}
|
||||
|
||||
self.test_config.set('prefer-ipv6', 'True')
|
||||
self.get_hacluster_config.return_value = conf
|
||||
self.get_iface_for_address.return_value = 'eth101'
|
||||
self.get_netmask_for_address.return_value = 'ffff.ffff.ffff.ffff'
|
||||
hooks.hooks.execute(['hooks/ha-relation-joined'])
|
||||
ex_args = {
|
||||
'corosync_mcastport': '37373',
|
||||
'init_services': {'res_cinder_haproxy': 'haproxy'},
|
||||
'resource_params': {
|
||||
'res_cinder_eth101_vip':
|
||||
'params ipv6addr="2001:db8:1::1" '
|
||||
'cidr_netmask="ffff.ffff.ffff.ffff" '
|
||||
'nic="eth101"',
|
||||
'res_cinder_haproxy': 'op monitor interval="5s"'
|
||||
},
|
||||
'corosync_bindiface': 'eth100',
|
||||
'clones': {'cl_cinder_haproxy': 'res_cinder_haproxy'},
|
||||
'resources': {
|
||||
'res_cinder_eth101_vip': 'ocf:heartbeat:IPv6addr',
|
||||
'res_cinder_haproxy': 'lsb:haproxy'
|
||||
}
|
||||
}
|
||||
self.relation_set.assert_called_with(**ex_args)
|
||||
|
||||
@patch.object(hooks, 'identity_joined')
|
||||
def test_ha_changed_clustered(self, joined):
|
||||
self.relation_get.return_value = True
|
||||
|
@ -64,6 +64,7 @@ class CharmTestCase(unittest.TestCase):
|
||||
self.test_relation = TestRelation()
|
||||
self.patch_all()
|
||||
|
||||
# FIXME: This is bad. These mocks will persist across all tests.
|
||||
def patch(self, method):
|
||||
_m = patch.object(self.obj, method)
|
||||
mock = _m.start()
|
||||
@ -80,7 +81,9 @@ class TestConfig(object):
|
||||
def __init__(self):
|
||||
self.config = get_default_config()
|
||||
|
||||
def get(self, attr):
|
||||
def get(self, attr=None):
|
||||
if not attr:
|
||||
return self.config
|
||||
try:
|
||||
return self.config[attr]
|
||||
except KeyError:
|
||||
|
Loading…
x
Reference in New Issue
Block a user