Merge lp:~hopem/charms/trusty/percona-cluster/next-fix-lp1425999 into lp:~openstack-charmers-archive/charms/trusty/percona-cluster/next

Proposed by Edward Hope-Morley
Status: Merged
Approved by: Billy Olsen
Approved revision: 51
Merged at revision: 51
Proposed branch: lp:~hopem/charms/trusty/percona-cluster/next-fix-lp1425999
Merge into: lp:~openstack-charmers-archive/charms/trusty/percona-cluster/next
Diff against target: 316 lines (+138/-64)
3 files modified
hooks/charmhelpers/contrib/database/mysql.py (+42/-59)
hooks/charmhelpers/contrib/network/ip.py (+84/-1)
hooks/charmhelpers/core/services/helpers.py (+12/-4)
To merge this branch: bzr merge lp:~hopem/charms/trusty/percona-cluster/next-fix-lp1425999
Reviewer Review Type Date Requested Status
Billy Olsen Approve
Jorge Niedbalski (community) Approve
Review via email: mp+251237@code.launchpad.net

This proposal supersedes a proposal from 2015-02-26.

To post a comment you must log in.
Revision history for this message
Billy Olsen (billy-olsen) wrote : Posted in a previous version of this proposal

LGTM, Approved. Everything looks good to in my testing.

review: Approve
Revision history for this message
Edward Hope-Morley (hopem) wrote :

Reverted and resynced to incorporate get_host_ip() from charmhelpers

Revision history for this message
Jorge Niedbalski (niedbalski) wrote :

Thanks for the re-sync Ed.

LGTM +1

review: Approve
Revision history for this message
Billy Olsen (billy-olsen) wrote :

LGTM +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'hooks/charmhelpers/contrib/database/mysql.py'
--- hooks/charmhelpers/contrib/database/mysql.py 2015-02-27 09:43:12 +0000
+++ hooks/charmhelpers/contrib/database/mysql.py 2015-02-27 10:21:53 +0000
@@ -15,14 +15,15 @@
15 write_file15 write_file
16)16)
17from charmhelpers.core.hookenv import (17from charmhelpers.core.hookenv import (
18 config as config_get,
18 relation_get,19 relation_get,
19 related_units,20 related_units,
20 unit_get,21 unit_get,
21 log,22 log,
22 DEBUG,23 DEBUG,
23 INFO,24 INFO,
25 WARNING,
24)26)
25from charmhelpers.core.hookenv import config as config_get
26from charmhelpers.fetch import (27from charmhelpers.fetch import (
27 apt_install,28 apt_install,
28 apt_update,29 apt_update,
@@ -32,6 +33,7 @@
32 peer_store,33 peer_store,
33 peer_retrieve,34 peer_retrieve,
34)35)
36from charmhelpers.contrib.network.ip import get_host_ip
3537
36try:38try:
37 import MySQLdb39 import MySQLdb
@@ -220,6 +222,18 @@
220 """Retrieve or generate mysql root password for service units."""222 """Retrieve or generate mysql root password for service units."""
221 return self.get_mysql_password(username=None, password=password)223 return self.get_mysql_password(username=None, password=password)
222224
225 def normalize_address(self, hostname):
226 """Ensure that address returned is an IP address (i.e. not fqdn)"""
227 if config_get('prefer-ipv6'):
228 # TODO: add support for ipv6 dns
229 return hostname
230
231 if hostname != unit_get('private-address'):
232 return get_host_ip(hostname, fallback=hostname)
233
234 # Otherwise assume localhost
235 return '127.0.0.1'
236
223 def get_allowed_units(self, database, username, relation_id=None):237 def get_allowed_units(self, database, username, relation_id=None):
224 """Get list of units with access grants for database with username.238 """Get list of units with access grants for database with username.
225239
@@ -247,6 +261,7 @@
247261
248 if hosts:262 if hosts:
249 for host in hosts:263 for host in hosts:
264 host = self.normalize_address(host)
250 if self.grant_exists(database, username, host):265 if self.grant_exists(database, username, host):
251 log("Grant exists for host '%s' on db '%s'" %266 log("Grant exists for host '%s' on db '%s'" %
252 (host, database), level=DEBUG)267 (host, database), level=DEBUG)
@@ -262,21 +277,11 @@
262277
263 def configure_db(self, hostname, database, username, admin=False):278 def configure_db(self, hostname, database, username, admin=False):
264 """Configure access to database for username from hostname."""279 """Configure access to database for username from hostname."""
265 if config_get('prefer-ipv6'):
266 remote_ip = hostname
267 elif hostname != unit_get('private-address'):
268 try:
269 remote_ip = socket.gethostbyname(hostname)
270 except Exception:
271 # socket.gethostbyname doesn't support ipv6
272 remote_ip = hostname
273 else:
274 remote_ip = '127.0.0.1'
275
276 self.connect(password=self.get_mysql_root_password())280 self.connect(password=self.get_mysql_root_password())
277 if not self.database_exists(database):281 if not self.database_exists(database):
278 self.create_database(database)282 self.create_database(database)
279283
284 remote_ip = self.normalize_address(hostname)
280 password = self.get_mysql_password(username)285 password = self.get_mysql_password(username)
281 if not self.grant_exists(database, username, remote_ip):286 if not self.grant_exists(database, username, remote_ip):
282 if not admin:287 if not admin:
@@ -289,9 +294,11 @@
289294
290class PerconaClusterHelper(object):295class PerconaClusterHelper(object):
291296
292 # Going for the biggest page size to avoid wasted bytes. InnoDB page size is297 # Going for the biggest page size to avoid wasted bytes.
293 # 16MB298 # InnoDB page size is 16MB
299
294 DEFAULT_PAGE_SIZE = 16 * 1024 * 1024300 DEFAULT_PAGE_SIZE = 16 * 1024 * 1024
301 DEFAULT_INNODB_BUFFER_FACTOR = 0.50
295302
296 def human_to_bytes(self, human):303 def human_to_bytes(self, human):
297 """Convert human readable configuration options to bytes."""304 """Convert human readable configuration options to bytes."""
@@ -352,51 +359,27 @@
352 if 'max-connections' in config:359 if 'max-connections' in config:
353 mysql_config['max_connections'] = config['max-connections']360 mysql_config['max_connections'] = config['max-connections']
354361
355 # Total memory available for dataset
356 dataset_bytes = self.human_to_bytes(config['dataset-size'])
357 mysql_config['dataset_bytes'] = dataset_bytes
358
359 if 'query-cache-type' in config:
360 # Query Cache Configuration
361 mysql_config['query_cache_size'] = config['query-cache-size']
362 if (config['query-cache-size'] == -1 and
363 config['query-cache-type'] in ['ON', 'DEMAND']):
364 # Calculate the query cache size automatically
365 qcache_bytes = (dataset_bytes * 0.20)
366 qcache_bytes = int(qcache_bytes -
367 (qcache_bytes % self.DEFAULT_PAGE_SIZE))
368 mysql_config['query_cache_size'] = qcache_bytes
369 dataset_bytes -= qcache_bytes
370
371 # 5.5 allows the words, but not 5.1
372 if config['query-cache-type'] == 'ON':
373 mysql_config['query_cache_type'] = 1
374 elif config['query-cache-type'] == 'DEMAND':
375 mysql_config['query_cache_type'] = 2
376 else:
377 mysql_config['query_cache_type'] = 0
378
379 # Set a sane default key_buffer size362 # Set a sane default key_buffer size
380 mysql_config['key_buffer'] = self.human_to_bytes('32M')363 mysql_config['key_buffer'] = self.human_to_bytes('32M')
381364 total_memory = self.human_to_bytes(self.get_mem_total())
382 if 'preferred-storage-engine' in config:365
383 # Storage engine configuration366 log("Option 'dataset-size' has been deprecated, instead by default %d%% of system \
384 preferred_engines = config['preferred-storage-engine'].split(',')367 available RAM will be used for innodb_buffer_pool_size allocation" %
385 chunk_size = int(dataset_bytes / len(preferred_engines))368 (self.DEFAULT_INNODB_BUFFER_FACTOR * 100), level="WARN")
386 mysql_config['innodb_flush_log_at_trx_commit'] = 1369
387 mysql_config['sync_binlog'] = 1370 innodb_buffer_pool_size = config.get('innodb-buffer-pool-size', None)
388 if 'InnoDB' in preferred_engines:371
389 mysql_config['innodb_buffer_pool_size'] = chunk_size372 if innodb_buffer_pool_size:
390 if config['tuning-level'] == 'fast':373 innodb_buffer_pool_size = self.human_to_bytes(
391 mysql_config['innodb_flush_log_at_trx_commit'] = 2374 innodb_buffer_pool_size)
392 else:375
393 mysql_config['innodb_buffer_pool_size'] = 0376 if innodb_buffer_pool_size > total_memory:
394377 log("innodb_buffer_pool_size; {} is greater than system available memory:{}".format(
395 mysql_config['default_storage_engine'] = preferred_engines[0]378 innodb_buffer_pool_size,
396 if 'MyISAM' in preferred_engines:379 total_memory), level='WARN')
397 mysql_config['key_buffer'] = chunk_size380 else:
398381 innodb_buffer_pool_size = int(
399 if config['tuning-level'] == 'fast':382 total_memory * self.DEFAULT_INNODB_BUFFER_FACTOR)
400 mysql_config['sync_binlog'] = 0383
401384 mysql_config['innodb_buffer_pool_size'] = innodb_buffer_pool_size
402 return mysql_config385 return mysql_config
403386
=== modified file 'hooks/charmhelpers/contrib/network/ip.py'
--- hooks/charmhelpers/contrib/network/ip.py 2015-02-04 18:56:00 +0000
+++ hooks/charmhelpers/contrib/network/ip.py 2015-02-27 10:21:53 +0000
@@ -17,13 +17,16 @@
17import glob17import glob
18import re18import re
19import subprocess19import subprocess
20import six
21import socket
2022
21from functools import partial23from functools import partial
2224
23from charmhelpers.core.hookenv import unit_get25from charmhelpers.core.hookenv import unit_get
24from charmhelpers.fetch import apt_install26from charmhelpers.fetch import apt_install
25from charmhelpers.core.hookenv import (27from charmhelpers.core.hookenv import (
26 log28 log,
29 WARNING,
27)30)
2831
29try:32try:
@@ -365,3 +368,83 @@
365 return True368 return True
366369
367 return False370 return False
371
372
373def is_ip(address):
374 """
375 Returns True if address is a valid IP address.
376 """
377 try:
378 # Test to see if already an IPv4 address
379 socket.inet_aton(address)
380 return True
381 except socket.error:
382 return False
383
384
385def ns_query(address):
386 try:
387 import dns.resolver
388 except ImportError:
389 apt_install('python-dnspython')
390 import dns.resolver
391
392 if isinstance(address, dns.name.Name):
393 rtype = 'PTR'
394 elif isinstance(address, six.string_types):
395 rtype = 'A'
396 else:
397 return None
398
399 answers = dns.resolver.query(address, rtype)
400 if answers:
401 return str(answers[0])
402 return None
403
404
405def get_host_ip(hostname, fallback=None):
406 """
407 Resolves the IP for a given hostname, or returns
408 the input if it is already an IP.
409 """
410 if is_ip(hostname):
411 return hostname
412
413 ip_addr = ns_query(hostname)
414 if not ip_addr:
415 try:
416 ip_addr = socket.gethostbyname(hostname)
417 except:
418 log("Failed to resolve hostname '%s'" % (hostname),
419 level=WARNING)
420 return fallback
421 return ip_addr
422
423
424def get_hostname(address, fqdn=True):
425 """
426 Resolves hostname for given IP, or returns the input
427 if it is already a hostname.
428 """
429 if is_ip(address):
430 try:
431 import dns.reversename
432 except ImportError:
433 apt_install("python-dnspython")
434 import dns.reversename
435
436 rev = dns.reversename.from_address(address)
437 result = ns_query(rev)
438 if not result:
439 return None
440 else:
441 result = address
442
443 if fqdn:
444 # strip trailing .
445 if result.endswith('.'):
446 return result[:-1]
447 else:
448 return result
449 else:
450 return result.split('.')[0]
368451
=== modified file 'hooks/charmhelpers/core/services/helpers.py'
--- hooks/charmhelpers/core/services/helpers.py 2015-02-04 18:56:00 +0000
+++ hooks/charmhelpers/core/services/helpers.py 2015-02-27 10:21:53 +0000
@@ -45,12 +45,14 @@
45 """45 """
46 name = None46 name = None
47 interface = None47 interface = None
48 required_keys = []
4948
50 def __init__(self, name=None, additional_required_keys=None):49 def __init__(self, name=None, additional_required_keys=None):
50 if not hasattr(self, 'required_keys'):
51 self.required_keys = []
52
51 if name is not None:53 if name is not None:
52 self.name = name54 self.name = name
53 if additional_required_keys is not None:55 if additional_required_keys:
54 self.required_keys.extend(additional_required_keys)56 self.required_keys.extend(additional_required_keys)
55 self.get_data()57 self.get_data()
5658
@@ -134,7 +136,10 @@
134 """136 """
135 name = 'db'137 name = 'db'
136 interface = 'mysql'138 interface = 'mysql'
137 required_keys = ['host', 'user', 'password', 'database']139
140 def __init__(self, *args, **kwargs):
141 self.required_keys = ['host', 'user', 'password', 'database']
142 super(HttpRelation).__init__(self, *args, **kwargs)
138143
139144
140class HttpRelation(RelationContext):145class HttpRelation(RelationContext):
@@ -146,7 +151,10 @@
146 """151 """
147 name = 'website'152 name = 'website'
148 interface = 'http'153 interface = 'http'
149 required_keys = ['host', 'port']154
155 def __init__(self, *args, **kwargs):
156 self.required_keys = ['host', 'port']
157 super(HttpRelation).__init__(self, *args, **kwargs)
150158
151 def provide_data(self):159 def provide_data(self):
152 return {160 return {

Subscribers

People subscribed via source and target branches