Merge "Dual Stack VIPs"

This commit is contained in:
Jenkins 2017-08-16 15:05:01 +00:00 committed by Gerrit Code Review
commit 04b1059ca6
10 changed files with 127 additions and 48 deletions

View File

@ -41,9 +41,9 @@ from charmhelpers.core.hookenv import (
charm_name, charm_name,
DEBUG, DEBUG,
INFO, INFO,
WARNING,
ERROR, ERROR,
status_set, status_set,
network_get_primary_address
) )
from charmhelpers.core.sysctl import create as sysctl_create from charmhelpers.core.sysctl import create as sysctl_create
@ -80,6 +80,9 @@ from charmhelpers.contrib.openstack.neutron import (
from charmhelpers.contrib.openstack.ip import ( from charmhelpers.contrib.openstack.ip import (
resolve_address, resolve_address,
INTERNAL, INTERNAL,
ADMIN,
PUBLIC,
ADDRESS_MAP,
) )
from charmhelpers.contrib.network.ip import ( from charmhelpers.contrib.network.ip import (
get_address_in_network, get_address_in_network,
@ -87,7 +90,6 @@ from charmhelpers.contrib.network.ip import (
get_ipv6_addr, get_ipv6_addr,
get_netmask_for_address, get_netmask_for_address,
format_ipv6_addr, format_ipv6_addr,
is_address_in_network,
is_bridge_member, is_bridge_member,
is_ipv6_disabled, is_ipv6_disabled,
) )
@ -620,7 +622,6 @@ class HAProxyContext(OSContextGenerator):
ctxt['haproxy_connect_timeout'] = config('haproxy-connect-timeout') ctxt['haproxy_connect_timeout'] = config('haproxy-connect-timeout')
if config('prefer-ipv6'): if config('prefer-ipv6'):
ctxt['ipv6'] = True
ctxt['local_host'] = 'ip6-localhost' ctxt['local_host'] = 'ip6-localhost'
ctxt['haproxy_host'] = '::' ctxt['haproxy_host'] = '::'
else: else:
@ -736,11 +737,17 @@ class ApacheSSLContext(OSContextGenerator):
return sorted(list(set(cns))) return sorted(list(set(cns)))
def get_network_addresses(self): def get_network_addresses(self):
"""For each network configured, return corresponding address and vip """For each network configured, return corresponding address and
(if available). hostnamr or vip (if available).
Returns a list of tuples of the form: Returns a list of tuples of the form:
[(address_in_net_a, hostname_in_net_a),
(address_in_net_b, hostname_in_net_b),
...]
or, if no hostnames(s) available:
[(address_in_net_a, vip_in_net_a), [(address_in_net_a, vip_in_net_a),
(address_in_net_b, vip_in_net_b), (address_in_net_b, vip_in_net_b),
...] ...]
@ -752,32 +759,27 @@ class ApacheSSLContext(OSContextGenerator):
...] ...]
""" """
addresses = [] addresses = []
if config('vip'): for net_type in [INTERNAL, ADMIN, PUBLIC]:
vips = config('vip').split() net_config = config(ADDRESS_MAP[net_type]['config'])
else: # NOTE(jamespage): Fallback must always be private address
vips = [] # as this is used to bind services on the
# local unit.
for net_type in ['os-internal-network', 'os-admin-network', fallback = unit_get("private-address")
'os-public-network']: if net_config:
addr = get_address_in_network(config(net_type), addr = get_address_in_network(net_config,
unit_get('private-address')) fallback)
if len(vips) > 1 and is_clustered():
if not config(net_type):
log("Multiple networks configured but net_type "
"is None (%s)." % net_type, level=WARNING)
continue
for vip in vips:
if is_address_in_network(config(net_type), vip):
addresses.append((addr, vip))
break
elif is_clustered() and config('vip'):
addresses.append((addr, config('vip')))
else: else:
addresses.append((addr, addr)) try:
addr = network_get_primary_address(
ADDRESS_MAP[net_type]['binding']
)
except NotImplementedError:
addr = fallback
return sorted(addresses) endpoint = resolve_address(net_type)
addresses.append((addr, endpoint))
return sorted(set(addresses))
def __call__(self): def __call__(self):
if isinstance(self.external_ports, six.string_types): if isinstance(self.external_ports, six.string_types):
@ -804,7 +806,7 @@ class ApacheSSLContext(OSContextGenerator):
self.configure_cert(cn) self.configure_cert(cn)
addresses = self.get_network_addresses() addresses = self.get_network_addresses()
for address, endpoint in sorted(set(addresses)): for address, endpoint in addresses:
for api_port in self.external_ports: for api_port in self.external_ports:
ext_port = determine_apache_port(api_port, ext_port = determine_apache_port(api_port,
singlenode_mode=True) singlenode_mode=True)
@ -1419,14 +1421,26 @@ class NeutronAPIContext(OSContextGenerator):
'rel_key': 'report-interval', 'rel_key': 'report-interval',
'default': 30, 'default': 30,
}, },
'enable_qos': {
'rel_key': 'enable-qos',
'default': False,
},
} }
ctxt = self.get_neutron_options({}) ctxt = self.get_neutron_options({})
for rid in relation_ids('neutron-plugin-api'): for rid in relation_ids('neutron-plugin-api'):
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)
# The l2-population key is used by the context as a way of
# checking if the api service on the other end is sending data
# in a recent format.
if 'l2-population' in rdata: if 'l2-population' in rdata:
ctxt.update(self.get_neutron_options(rdata)) ctxt.update(self.get_neutron_options(rdata))
if ctxt['enable_qos']:
ctxt['extension_drivers'] = 'qos'
else:
ctxt['extension_drivers'] = ''
return ctxt return ctxt
def get_neutron_options(self, rdata): def get_neutron_options(self, rdata):

View File

@ -48,9 +48,7 @@ listen stats
{% for service, ports in service_ports.items() -%} {% for service, ports in service_ports.items() -%}
frontend tcp-in_{{ service }} frontend tcp-in_{{ service }}
bind *:{{ ports[0] }} bind *:{{ ports[0] }}
{% if ipv6 -%}
bind :::{{ ports[0] }} bind :::{{ ports[0] }}
{% endif -%}
{% for frontend in frontends -%} {% for frontend in frontends -%}
acl net_{{ frontend }} dst {{ frontends[frontend]['network'] }} acl net_{{ frontend }} dst {{ frontends[frontend]['network'] }}
use_backend {{ service }}_{{ frontend }} if net_{{ frontend }} use_backend {{ service }}_{{ frontend }} if net_{{ frontend }}

View File

@ -20,7 +20,8 @@ from charmhelpers.fetch import apt_install, apt_update
from charmhelpers.core.hookenv import ( from charmhelpers.core.hookenv import (
log, log,
ERROR, ERROR,
INFO INFO,
TRACE
) )
from charmhelpers.contrib.openstack.utils import OPENSTACK_CODENAMES from charmhelpers.contrib.openstack.utils import OPENSTACK_CODENAMES
@ -80,8 +81,10 @@ def get_loader(templates_dir, os_release):
loaders.insert(0, FileSystemLoader(tmpl_dir)) loaders.insert(0, FileSystemLoader(tmpl_dir))
if rel == os_release: if rel == os_release:
break break
# demote this log to the lowest level; we don't really need to see these
# lots in production even when debugging.
log('Creating choice loader with dirs: %s' % log('Creating choice loader with dirs: %s' %
[l.searchpath for l in loaders], level=INFO) [l.searchpath for l in loaders], level=TRACE)
return ChoiceLoader(loaders) return ChoiceLoader(loaders)

View File

@ -186,7 +186,7 @@ SWIFT_CODENAMES = OrderedDict([
('ocata', ('ocata',
['2.11.0', '2.12.0', '2.13.0']), ['2.11.0', '2.12.0', '2.13.0']),
('pike', ('pike',
['2.13.0']), ['2.13.0', '2.15.0']),
]) ])
# >= Liberty version->codename mapping # >= Liberty version->codename mapping

View File

@ -43,6 +43,7 @@ ERROR = "ERROR"
WARNING = "WARNING" WARNING = "WARNING"
INFO = "INFO" INFO = "INFO"
DEBUG = "DEBUG" DEBUG = "DEBUG"
TRACE = "TRACE"
MARKER = object() MARKER = object()
cache = {} cache = {}

View File

@ -34,7 +34,7 @@ import six
from contextlib import contextmanager from contextlib import contextmanager
from collections import OrderedDict from collections import OrderedDict
from .hookenv import log from .hookenv import log, DEBUG
from .fstab import Fstab from .fstab import Fstab
from charmhelpers.osplatform import get_platform from charmhelpers.osplatform import get_platform
@ -487,13 +487,37 @@ def mkdir(path, owner='root', group='root', perms=0o555, force=False):
def write_file(path, content, owner='root', group='root', perms=0o444): def write_file(path, content, owner='root', group='root', perms=0o444):
"""Create or overwrite a file with the contents of a byte string.""" """Create or overwrite a file with the contents of a byte string."""
log("Writing file {} {}:{} {:o}".format(path, owner, group, perms))
uid = pwd.getpwnam(owner).pw_uid uid = pwd.getpwnam(owner).pw_uid
gid = grp.getgrnam(group).gr_gid gid = grp.getgrnam(group).gr_gid
with open(path, 'wb') as target: # lets see if we can grab the file and compare the context, to avoid doing
os.fchown(target.fileno(), uid, gid) # a write.
os.fchmod(target.fileno(), perms) existing_content = None
target.write(content) existing_uid, existing_gid = None, None
try:
with open(path, 'rb') as target:
existing_content = target.read()
stat = os.stat(path)
existing_uid, existing_gid = stat.st_uid, stat.st_gid
except:
pass
if content != existing_content:
log("Writing file {} {}:{} {:o}".format(path, owner, group, perms),
level=DEBUG)
with open(path, 'wb') as target:
os.fchown(target.fileno(), uid, gid)
os.fchmod(target.fileno(), perms)
target.write(content)
return
# the contents were the same, but we might still need to change the
# ownership.
if existing_uid != uid:
log("Changing uid on already existing content: {} -> {}"
.format(existing_uid, uid), level=DEBUG)
os.chown(path, uid, -1)
if existing_gid != gid:
log("Changing gid on already existing content: {} -> {}"
.format(existing_gid, gid), level=DEBUG)
os.chown(path, -1, gid)
def fstab_remove(mp): def fstab_remove(mp):

View File

@ -60,6 +60,7 @@ from charmhelpers.core.hookenv import (
service_name, service_name,
log, log,
ERROR, ERROR,
WARNING,
status_set, status_set,
open_port, open_port,
) )
@ -510,6 +511,14 @@ def ha_joined(relation_id=None):
if iface is not None: if iface is not None:
vip_key = 'res_cinder_{}_vip'.format(iface) vip_key = 'res_cinder_{}_vip'.format(iface)
if vip_key in vip_group:
if vip not in resource_params[vip_key]:
vip_key = '{}_{}'.format(vip_key, vip_params)
else:
log("Resource '%s' (vip='%s') already exists in "
"vip group - skipping" % (vip_key, vip), WARNING)
continue
resources[vip_key] = res_cinder_vip resources[vip_key] = res_cinder_vip
resource_params[vip_key] = ( resource_params[vip_key] = (
'params {ip}="{vip}" cidr_netmask="{netmask}"' 'params {ip}="{vip}" cidr_netmask="{netmask}"'

View File

@ -43,6 +43,7 @@ ERROR = "ERROR"
WARNING = "WARNING" WARNING = "WARNING"
INFO = "INFO" INFO = "INFO"
DEBUG = "DEBUG" DEBUG = "DEBUG"
TRACE = "TRACE"
MARKER = object() MARKER = object()
cache = {} cache = {}

View File

@ -34,7 +34,7 @@ import six
from contextlib import contextmanager from contextlib import contextmanager
from collections import OrderedDict from collections import OrderedDict
from .hookenv import log from .hookenv import log, DEBUG
from .fstab import Fstab from .fstab import Fstab
from charmhelpers.osplatform import get_platform from charmhelpers.osplatform import get_platform
@ -487,13 +487,37 @@ def mkdir(path, owner='root', group='root', perms=0o555, force=False):
def write_file(path, content, owner='root', group='root', perms=0o444): def write_file(path, content, owner='root', group='root', perms=0o444):
"""Create or overwrite a file with the contents of a byte string.""" """Create or overwrite a file with the contents of a byte string."""
log("Writing file {} {}:{} {:o}".format(path, owner, group, perms))
uid = pwd.getpwnam(owner).pw_uid uid = pwd.getpwnam(owner).pw_uid
gid = grp.getgrnam(group).gr_gid gid = grp.getgrnam(group).gr_gid
with open(path, 'wb') as target: # lets see if we can grab the file and compare the context, to avoid doing
os.fchown(target.fileno(), uid, gid) # a write.
os.fchmod(target.fileno(), perms) existing_content = None
target.write(content) existing_uid, existing_gid = None, None
try:
with open(path, 'rb') as target:
existing_content = target.read()
stat = os.stat(path)
existing_uid, existing_gid = stat.st_uid, stat.st_gid
except:
pass
if content != existing_content:
log("Writing file {} {}:{} {:o}".format(path, owner, group, perms),
level=DEBUG)
with open(path, 'wb') as target:
os.fchown(target.fileno(), uid, gid)
os.fchmod(target.fileno(), perms)
target.write(content)
return
# the contents were the same, but we might still need to change the
# ownership.
if existing_uid != uid:
log("Changing uid on already existing content: {} -> {}"
.format(existing_uid, uid), level=DEBUG)
os.chown(path, uid, -1)
if existing_gid != gid:
log("Changing gid on already existing content: {} -> {}"
.format(existing_gid, gid), level=DEBUG)
os.chown(path, -1, gid)
def fstab_remove(mp): def fstab_remove(mp):

View File

@ -145,6 +145,8 @@ class TestCinderContext(CharmTestCase):
mod_ch_context = 'charmhelpers.contrib.openstack.context' mod_ch_context = 'charmhelpers.contrib.openstack.context'
@patch('charmhelpers.contrib.openstack.context.resolve_address')
@patch('charmhelpers.contrib.openstack.ip.config')
@patch('%s.ApacheSSLContext.canonical_names' % (mod_ch_context)) @patch('%s.ApacheSSLContext.canonical_names' % (mod_ch_context))
@patch('%s.ApacheSSLContext.configure_ca' % (mod_ch_context)) @patch('%s.ApacheSSLContext.configure_ca' % (mod_ch_context))
@patch('%s.config' % (mod_ch_context)) @patch('%s.config' % (mod_ch_context))
@ -161,9 +163,12 @@ class TestCinderContext(CharmTestCase):
mock_is_clustered, mock_is_clustered,
mock_hookenv, mock_hookenv,
mock_configure_ca, mock_configure_ca,
mock_cfg_canonical_names): mock_cfg_canonical_names,
mock_ip_config,
mock_ip_network_get):
mock_https.return_value = True mock_https.return_value = True
mock_unit_get.return_value = '1.2.3.4' mock_unit_get.return_value = '1.2.3.4'
mock_ip_network_get.return_value = '1.2.3.4'
mock_determine_api_port.return_value = '12' mock_determine_api_port.return_value = '12'
mock_determine_apache_port.return_value = '34' mock_determine_apache_port.return_value = '34'
mock_is_clustered.return_value = False mock_is_clustered.return_value = False