Avoid shared-db change when using access-network

When the percona-cluster charm sets an access-network but the default
unit-get address is not on that network extra shared-db relations get
executed. This is specifically a problem when running upgrades and
trying to avoid API downtime.

The root cause is that the access-network is not checked until the
SharedDBContext is consulted. But then db_joined function will
change it back to the wrong ip on subsequent runs.

This change adds a check for access-network on the relation during
the db_joined function and pushes IP selection off to
get_relation_ip.

Charm helpers sync to pull in changes to get_relation_ip.

Change-Id: Ifc22d61f1de1092306b98d30fbea01fead855d14
Partial-bug: #1677647
This commit is contained in:
David Ames 2017-04-26 13:26:08 -07:00
parent bc777bb70a
commit e7317bf571
3 changed files with 59 additions and 66 deletions

View File

@ -1232,31 +1232,50 @@ MAX_DEFAULT_WORKERS = 4
DEFAULT_MULTIPLIER = 2
def _calculate_workers():
'''
Determine the number of worker processes based on the CPU
count of the unit containing the application.
Workers will be limited to MAX_DEFAULT_WORKERS in
container environments where no worker-multipler configuration
option been set.
@returns int: number of worker processes to use
'''
multiplier = config('worker-multiplier') or DEFAULT_MULTIPLIER
count = int(_num_cpus() * multiplier)
if multiplier > 0 and count == 0:
count = 1
if config('worker-multiplier') is None and is_container():
# NOTE(jamespage): Limit unconfigured worker-multiplier
# to MAX_DEFAULT_WORKERS to avoid insane
# worker configuration in LXD containers
# on large servers
# Reference: https://pad.lv/1665270
count = min(count, MAX_DEFAULT_WORKERS)
return count
def _num_cpus():
'''
Compatibility wrapper for calculating the number of CPU's
a unit has.
@returns: int: number of CPU cores detected
'''
try:
return psutil.cpu_count()
except AttributeError:
return psutil.NUM_CPUS
class WorkerConfigContext(OSContextGenerator):
@property
def num_cpus(self):
# NOTE: use cpu_count if present (16.04 support)
if hasattr(psutil, 'cpu_count'):
return psutil.cpu_count()
else:
return psutil.NUM_CPUS
def __call__(self):
multiplier = config('worker-multiplier') or DEFAULT_MULTIPLIER
count = int(self.num_cpus * multiplier)
if multiplier > 0 and count == 0:
count = 1
if config('worker-multiplier') is None and is_container():
# NOTE(jamespage): Limit unconfigured worker-multiplier
# to MAX_DEFAULT_WORKERS to avoid insane
# worker configuration in LXD containers
# on large servers
# Reference: https://pad.lv/1665270
count = min(count, MAX_DEFAULT_WORKERS)
ctxt = {"workers": count}
ctxt = {"workers": _calculate_workers()}
return ctxt
@ -1264,7 +1283,7 @@ class WSGIWorkerConfigContext(WorkerConfigContext):
def __init__(self, name=None, script=None, admin_script=None,
public_script=None, process_weight=1.00,
admin_process_weight=0.75, public_process_weight=0.25):
admin_process_weight=0.25, public_process_weight=0.75):
self.service_name = name
self.user = name
self.group = name
@ -1276,8 +1295,7 @@ class WSGIWorkerConfigContext(WorkerConfigContext):
self.public_process_weight = public_process_weight
def __call__(self):
multiplier = config('worker-multiplier') or 1
total_processes = self.num_cpus * multiplier
total_processes = _calculate_workers()
ctxt = {
"service_name": self.service_name,
"user": self.user,

View File

@ -53,11 +53,10 @@ from charmhelpers.core.hookenv import (
relation_get,
relation_set,
relation_ids,
related_units,
service_name,
unit_get,
UnregisteredHookError,
status_set,
network_get_primary_address,
)
from charmhelpers.core.host import (
@ -105,7 +104,8 @@ from charmhelpers.contrib.network.ip import (
get_netmask_for_address,
get_iface_for_address,
get_ipv6_addr,
is_ipv6
is_ipv6,
get_relation_ip,
)
from charmhelpers.contrib.openstack.ip import (
canonical_url,
@ -158,13 +158,14 @@ def db_joined():
sync_db_with_multi_ipv6_addresses(config('database'),
config('database-user'))
else:
host = None
try:
# NOTE: try to use network spaces
host = network_get_primary_address('shared-db')
except NotImplementedError:
# NOTE: fallback to private-address
host = unit_get('private-address')
# Avoid churn check for access-network early
access_network = None
for unit in related_units():
access_network = relation_get(unit=unit,
attribute='access-network')
if access_network:
break
host = get_relation_ip('shared-db', cidr_network=access_network)
relation_set(database=config('database'),
username=config('database-user'),

View File

@ -59,9 +59,8 @@ TO_PATCH = [
'relation_ids',
'relation_set',
'relation_get',
'related_units',
'service_name',
'unit_get',
'network_get_primary_address',
# charmhelpers.core.host
'apt_install',
'apt_update',
@ -99,6 +98,7 @@ TO_PATCH = [
'get_ipv6_addr',
'sync_db_with_multi_ipv6_addresses',
'delete_keyring',
'get_relation_ip',
]
@ -107,7 +107,6 @@ class GlanceRelationTests(CharmTestCase):
def setUp(self):
super(GlanceRelationTests, self).setUp(relations, TO_PATCH)
self.config.side_effect = self.test_config.get
self.network_get_primary_address.side_effect = NotImplementedError
@patch.object(utils, 'config')
@patch.object(utils, 'token_cache_pkgs')
@ -178,40 +177,15 @@ class GlanceRelationTests(CharmTestCase):
self.git_install.assert_called_with(projects_yaml)
def test_db_joined(self):
self.unit_get.return_value = 'glance.foohost.com'
self.get_relation_ip.return_value = '10.0.0.1'
self.is_relation_made.return_value = False
relations.db_joined()
self.relation_set.assert_called_with(database='glance',
username='glance',
hostname='glance.foohost.com')
self.unit_get.assert_called_with('private-address')
def test_db_joined_network_spaces(self):
self.network_get_primary_address.side_effect = None
self.network_get_primary_address.return_value = '192.168.20.1'
self.unit_get.return_value = 'glance.foohost.com'
self.is_relation_made.return_value = False
relations.db_joined()
self.relation_set.assert_called_with(database='glance',
username='glance',
hostname='192.168.20.1')
self.assertFalse(self.unit_get.called)
def test_db_joined_with_ipv6(self):
self.test_config.set('prefer-ipv6', True)
self.is_relation_made.return_value = False
relations.db_joined()
relation_data = {
'database': 'glance',
'username': 'glance',
}
relation_data['hostname'] = '2001:db8:1::1'
self.sync_db_with_multi_ipv6_addresses.assert_called_with(
'glance', 'glance')
hostname='10.0.0.1')
self.get_relation_ip.assert_called_with('shared-db', cidr_network=None)
def test_postgresql_db_joined(self):
self.unit_get.return_value = 'glance.foohost.com'
self.is_relation_made.return_value = False
relations.pgsql_db_joined()
self.relation_set.assert_called_with(database='glance'),