fix for hosts update issue + unit tests

This commit is contained in:
Edward Hope-Morley 2014-10-02 17:12:44 +01:00
parent 7ede70871c
commit 8eaea905f7
4 changed files with 146 additions and 21 deletions

View File

@ -1,10 +1,14 @@
#!/usr/bin/make
PYTHON := /usr/bin/env python
export PYTHONPATH := hooks
lint:
@flake8 --exclude hooks/charmhelpers hooks
@charm proof
unit_test:
@$(PYTHON) /usr/bin/nosetests --nologcapture unit_tests
bin/charm_helpers_sync.py:
@mkdir -p bin
@bzr cat lp:charm-helpers/tools/charm_helpers_sync/charm_helpers_sync.py \

View File

@ -1,7 +1,9 @@
''' General utilities for percona '''
import re
import subprocess
from subprocess import Popen, PIPE
import socket
import tempfile
import os
from charmhelpers.core.host import (
lsb_release
@ -13,7 +15,8 @@ from charmhelpers.core.hookenv import (
relation_get,
relation_set,
local_unit,
config
config,
log
)
from charmhelpers.fetch import (
apt_install,
@ -49,6 +52,7 @@ REPO = """deb http://repo.percona.com/apt {release} main
deb-src http://repo.percona.com/apt {release} main"""
MY_CNF = "/etc/mysql/my.cnf"
SEEDED_MARKER = "/var/lib/mysql/seeded"
HOSTS_FILE = '/etc/hosts'
def seeded():
@ -78,15 +82,10 @@ def render_template(template_name, context, template_dir=TEMPLATES_DIR):
return template.render(context)
# TODO: goto charm-helpers (I use this everywhere)
def get_host_ip(hostname=None):
if config('prefer-ipv6'):
private_address = get_ipv6_addr(exc_list=[config('vip')])[0]
hostname = socket.gethostname()
host_map = {}
host_map[private_address] = hostname
render_hosts(host_map)
return hostname
get_ipv6_addr(exc_list=[config('vip')])[0]
return socket.gethostname()
hostname = hostname or unit_get('private-address')
try:
@ -102,22 +101,25 @@ def get_host_ip(hostname=None):
def get_cluster_hosts():
hosts = [get_host_ip()]
hosts = []
hosts_map = {}
for relid in relation_ids('cluster'):
for unit in related_units(relid):
private_address = relation_get('private-address', unit, relid)
if config('prefer-ipv6'):
hosts.append(get_host_ip())
hostname = relation_get('hostname', unit, relid)
if not hostname or hostname in hosts:
log("hostname '%s' provided by cluster relation" % (hostname))
if not hostname:
continue
hosts_map[private_address] = hostname
hosts.append(hostname)
else:
hosts.append(get_host_ip(private_address))
render_hosts(hosts_map)
update_hosts_file(hosts_map)
return hosts
SQL_SST_USER_SETUP = "GRANT RELOAD, LOCK TABLES, REPLICATION CLIENT ON *.*" \
@ -159,23 +161,39 @@ def relation_clear(r_id=None):
**settings)
def render_hosts(map):
FILE = '/etc/hosts'
print "render_hosts"
with open(FILE, 'r') as hosts:
def update_hosts_file(map):
"""Percona does not currently like ipv6 addresses so we need to use dns
names instead. In order to make them resolvable we ensure they are in
/etc/hosts.
See https://bugs.launchpad.net/galera/+bug/1130595 for some more info.
"""
with open(HOSTS_FILE, 'r') as hosts:
lines = hosts.readlines()
key = re.compile("^(.+?)\s(.+)")
newlines = []
for ip, hostname in map.items():
if not ip or not hostname:
continue
for line in lines:
if line.startswith(ip) or hostname in line:
lines.remove(line)
lines.append(ip + ' ' + hostname + '\n')
with open(FILE, 'w') as hosts:
for line in lines:
hosts.write(line)
match = re.match(key, line)
if match:
if ((match.group(1) != ip and
hostname not in match.group(2).split()) and
line not in newlines):
# keep the line
newlines.append(line)
newlines.append("%s %s\n" % (ip, hostname))
with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
with open(tmpfile.name, 'w') as hosts:
for line in newlines:
hosts.write(line)
os.rename(tmpfile.name, HOSTS_FILE)
def assert_charm_supports_ipv6():

0
unit_tests/__init__.py Normal file
View File

View File

@ -0,0 +1,103 @@
import mock
import os
import unittest
import tempfile
import sys
sys.modules['MySQLdb'] = mock.Mock()
import percona_utils
class UtilsTests(unittest.TestCase):
def setUp(self):
super(UtilsTests, self).setUp()
def test_update_empty_hosts_file(self):
map = {'1.2.3.4': 'my-host'}
with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
percona_utils.HOSTS_FILE = tmpfile.name
percona_utils.HOSTS_FILE = tmpfile.name
percona_utils.update_hosts_file(map)
with open(tmpfile.name, 'r') as fd:
lines = fd.readlines()
os.remove(tmpfile.name)
self.assertEqual(len(lines), 1)
self.assertEqual(lines[0], "%s %s\n" % (map.items()[0]))
def test_update_hosts_file_w_dup(self):
map = {'1.2.3.4': 'my-host'}
with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
percona_utils.HOSTS_FILE = tmpfile.name
with open(tmpfile.name, 'w') as fd:
fd.write("%s %s\n" % (map.items()[0]))
percona_utils.update_hosts_file(map)
with open(tmpfile.name, 'r') as fd:
lines = fd.readlines()
os.remove(tmpfile.name)
self.assertEqual(len(lines), 1)
self.assertEqual(lines[0], "%s %s\n" % (map.items()[0]))
def test_update_hosts_file_entry(self):
altmap = {'2.4.6.8': 'alt-host'}
map = {'1.2.3.4': 'my-host'}
with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
percona_utils.HOSTS_FILE = tmpfile.name
with open(tmpfile.name, 'w') as fd:
fd.write("%s %s\n" % (altmap.items()[0]))
percona_utils.update_hosts_file(map)
with open(tmpfile.name, 'r') as fd:
lines = fd.readlines()
os.remove(tmpfile.name)
self.assertEqual(len(lines), 2)
self.assertEqual(lines[0], "%s %s\n" % (altmap.items()[0]))
self.assertEqual(lines[1], "%s %s\n" % (map.items()[0]))
@mock.patch("percona_utils.log")
@mock.patch("percona_utils.config")
@mock.patch("percona_utils.update_hosts_file")
@mock.patch("percona_utils.get_host_ip")
@mock.patch("percona_utils.relation_get")
@mock.patch("percona_utils.related_units")
@mock.patch("percona_utils.relation_ids")
def test_get_cluster_hosts(self, mock_rel_ids, mock_rel_units,
mock_rel_get, mock_get_host_ip,
mock_update_hosts_file, mock_config,
mock_log):
mock_rel_ids.return_value = [1,2]
mock_rel_units.return_value = [3,4]
mock_rel_get.return_value = '0.0.0.0'
mock_config.side_effect = lambda k: False
percona_utils.get_cluster_hosts()
mock_rel_get.assert_called_with('private-address', 4, 2)
@mock.patch("percona_utils.log")
@mock.patch("percona_utils.config")
@mock.patch("percona_utils.update_hosts_file")
@mock.patch("percona_utils.get_host_ip")
@mock.patch("percona_utils.relation_get")
@mock.patch("percona_utils.related_units")
@mock.patch("percona_utils.relation_ids")
def test_get_cluster_hosts_ipv6(self, mock_rel_ids, mock_rel_units,
mock_rel_get, mock_get_host_ip,
mock_update_hosts_file, mock_config,
mock_log):
mock_rel_ids.return_value = [1,2]
mock_rel_units.return_value = [3,4]
mock_rel_get.return_value = '0.0.0.0'
mock_config.side_effect = lambda k: True
percona_utils.get_cluster_hosts()
mock_rel_get.assert_called_with('hostname', 4, 2)