Merge lp:~openstack-charmers/charm-helpers/active-active into lp:charm-helpers

Proposed by James Page
Status: Merged
Merged at revision: 132
Proposed branch: lp:~openstack-charmers/charm-helpers/active-active
Merge into: lp:charm-helpers
Diff against target: 272 lines (+180/-23)
6 files modified
charmhelpers/contrib/openstack/context.py (+9/-6)
charmhelpers/contrib/openstack/utils.py (+13/-13)
charmhelpers/contrib/peerstorage/__init__.py (+83/-0)
tests/contrib/openstack/test_openstack_utils.py (+6/-3)
tests/contrib/openstack/test_os_contexts.py (+0/-1)
tests/contrib/peerstorage/test_peerstorage.py (+69/-0)
To merge this branch: bzr merge lp:~openstack-charmers/charm-helpers/active-active
Reviewer Review Type Date Requested Status
Marco Ceppi Approve
Review via email: mp+211285@code.launchpad.net

Description of the change

Tweaks to AMQP context for HA changes

New peerstorage helper which is useful for storing and replicating passwords etc.. across peers in a cluster.

Fix to get_hostname to make fqdn flag work properly.

To post a comment you must log in.
Revision history for this message
Marco Ceppi (marcoceppi) wrote :

LGTM

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'charmhelpers/contrib/openstack/context.py'
--- charmhelpers/contrib/openstack/context.py 2014-02-27 15:50:39 +0000
+++ charmhelpers/contrib/openstack/context.py 2014-03-17 11:41:06 +0000
@@ -199,6 +199,7 @@
199199
200 ctxt = {}200 ctxt = {}
201 for rid in relation_ids('amqp'):201 for rid in relation_ids('amqp'):
202 ha_vip_only = False
202 for unit in related_units(rid):203 for unit in related_units(rid):
203 if relation_get('clustered', rid=rid, unit=unit):204 if relation_get('clustered', rid=rid, unit=unit):
204 ctxt['clustered'] = True205 ctxt['clustered'] = True
@@ -213,16 +214,18 @@
213 unit=unit),214 unit=unit),
214 'rabbitmq_virtual_host': vhost,215 'rabbitmq_virtual_host': vhost,
215 })216 })
217 if relation_get('ha_queues', rid=rid, unit=unit) is not None:
218 ctxt['rabbitmq_ha_queues'] = True
219
220 ha_vip_only = relation_get('ha-vip-only',
221 rid=rid, unit=unit) is not None
222
216 if context_complete(ctxt):223 if context_complete(ctxt):
217 # Sufficient information found = break out!224 # Sufficient information found = break out!
218 break225 break
219 # Used for active/active rabbitmq >= grizzly226 # Used for active/active rabbitmq >= grizzly
220 if ('clustered' not in ctxt or relation_get('ha-vip-only') == 'True') and \227 if ('clustered' not in ctxt or ha_vip_only) \
221 len(related_units(rid)) > 1:228 and len(related_units(rid)) > 1:
222 if relation_get('ha_queues'):
223 ctxt['rabbitmq_ha_queues'] = relation_get('ha_queues')
224 else:
225 ctxt['rabbitmq_ha_queues'] = False
226 rabbitmq_hosts = []229 rabbitmq_hosts = []
227 for unit in related_units(rid):230 for unit in related_units(rid):
228 rabbitmq_hosts.append(relation_get('private-address',231 rabbitmq_hosts.append(relation_get('private-address',
229232
=== modified file 'charmhelpers/contrib/openstack/utils.py'
--- charmhelpers/contrib/openstack/utils.py 2014-01-14 12:14:15 +0000
+++ charmhelpers/contrib/openstack/utils.py 2014-03-17 11:41:06 +0000
@@ -420,19 +420,19 @@
420 Resolves hostname for given IP, or returns the input420 Resolves hostname for given IP, or returns the input
421 if it is already a hostname.421 if it is already a hostname.
422 """422 """
423 if not is_ip(address):423 if is_ip(address):
424 return address424 try:
425425 import dns.reversename
426 try:426 except ImportError:
427 import dns.reversename427 apt_install('python-dnspython')
428 except ImportError:428 import dns.reversename
429 apt_install('python-dnspython')429
430 import dns.reversename430 rev = dns.reversename.from_address(address)
431431 result = ns_query(rev)
432 rev = dns.reversename.from_address(address)432 if not result:
433 result = ns_query(rev)433 return None
434 if not result:434 else:
435 return None435 result = address
436436
437 if fqdn:437 if fqdn:
438 # strip trailing .438 # strip trailing .
439439
=== added directory 'charmhelpers/contrib/peerstorage'
=== added file 'charmhelpers/contrib/peerstorage/__init__.py'
--- charmhelpers/contrib/peerstorage/__init__.py 1970-01-01 00:00:00 +0000
+++ charmhelpers/contrib/peerstorage/__init__.py 2014-03-17 11:41:06 +0000
@@ -0,0 +1,83 @@
1from charmhelpers.core.hookenv import (
2 relation_ids,
3 relation_get,
4 local_unit,
5 relation_set,
6)
7
8"""
9This helper provides functions to support use of a peer relation
10for basic key/value storage, with the added benefit that all storage
11can be replicated across peer units, so this is really useful for
12services that issue usernames/passwords to remote services.
13
14def shared_db_changed()
15 # Only the lead unit should create passwords
16 if not is_leader():
17 return
18 username = relation_get('username')
19 key = '{}.password'.format(username)
20 # Attempt to retrieve any existing password for this user
21 password = peer_retrieve(key)
22 if password is None:
23 # New user, create password and store
24 password = pwgen(length=64)
25 peer_store(key, password)
26 create_access(username, password)
27 relation_set(password=password)
28
29
30def cluster_changed()
31 # Echo any relation data other that *-address
32 # back onto the peer relation so all units have
33 # all *.password keys stored on their local relation
34 # for later retrieval.
35 peer_echo()
36
37"""
38
39
40def peer_retrieve(key, relation_name='cluster'):
41 """ Retrieve a named key from peer relation relation_name """
42 cluster_rels = relation_ids(relation_name)
43 if len(cluster_rels) > 0:
44 cluster_rid = cluster_rels[0]
45 return relation_get(attribute=key, rid=cluster_rid,
46 unit=local_unit())
47 else:
48 raise ValueError('Unable to detect'
49 'peer relation {}'.format(relation_name))
50
51
52def peer_store(key, value, relation_name='cluster'):
53 """ Store the key/value pair on the named peer relation relation_name """
54 cluster_rels = relation_ids(relation_name)
55 if len(cluster_rels) > 0:
56 cluster_rid = cluster_rels[0]
57 relation_set(relation_id=cluster_rid,
58 relation_settings={key: value})
59 else:
60 raise ValueError('Unable to detect '
61 'peer relation {}'.format(relation_name))
62
63
64def peer_echo(includes=None):
65 """Echo filtered attributes back onto the same relation for storage
66
67 Note that this helper must only be called within a peer relation
68 changed hook
69 """
70 rdata = relation_get()
71 echo_data = {}
72 if includes is None:
73 echo_data = rdata.copy()
74 for ex in ['private-address', 'public-address']:
75 if ex in echo_data:
76 echo_data.pop(ex)
77 else:
78 for attribute, value in rdata.iteritems():
79 for include in includes:
80 if include in attribute:
81 echo_data[attribute] = value
82 if len(echo_data) > 0:
83 relation_set(relation_settings=echo_data)
084
=== modified file 'tests/contrib/openstack/test_openstack_utils.py'
--- tests/contrib/openstack/test_openstack_utils.py 2014-01-13 10:31:33 +0000
+++ tests/contrib/openstack/test_openstack_utils.py 2014-03-17 11:41:06 +0000
@@ -599,11 +599,14 @@
599599
600 @patch.object(openstack, 'apt_install')600 @patch.object(openstack, 'apt_install')
601 def test_get_hostname_with_hostname(self, apt_install):601 def test_get_hostname_with_hostname(self, apt_install):
602 fake_dns = FakeDNS('5.5.5.5')602 hn = openstack.get_hostname('www.ubuntu.com')
603 with patch('__builtin__.__import__', side_effect=[fake_dns]):
604 hn = openstack.get_hostname('www.ubuntu.com')
605 self.assertEquals(hn, 'www.ubuntu.com')603 self.assertEquals(hn, 'www.ubuntu.com')
606604
605 @patch.object(openstack, 'apt_install')
606 def test_get_hostname_with_hostname_not_fqdn(self, apt_install):
607 hn = openstack.get_hostname('packages.ubuntu.com', fqdn=False)
608 self.assertEquals(hn, 'packages')
609
607610
608if __name__ == '__main__':611if __name__ == '__main__':
609 unittest.main()612 unittest.main()
610613
=== modified file 'tests/contrib/openstack/test_os_contexts.py'
--- tests/contrib/openstack/test_os_contexts.py 2014-02-27 16:20:51 +0000
+++ tests/contrib/openstack/test_os_contexts.py 2014-03-17 11:41:06 +0000
@@ -337,7 +337,6 @@
337 'rabbitmq_user': 'adam',337 'rabbitmq_user': 'adam',
338 'rabbitmq_virtual_host': 'foo',338 'rabbitmq_virtual_host': 'foo',
339 'rabbitmq_hosts': 'rabbithost2,rabbithost1',339 'rabbitmq_hosts': 'rabbithost2,rabbithost1',
340 'rabbitmq_ha_queues': False
341 }340 }
342 self.assertEquals(result, expected)341 self.assertEquals(result, expected)
343342
344343
=== added directory 'tests/contrib/peerstorage'
=== added file 'tests/contrib/peerstorage/__init__.py'
=== added file 'tests/contrib/peerstorage/test_peerstorage.py'
--- tests/contrib/peerstorage/test_peerstorage.py 1970-01-01 00:00:00 +0000
+++ tests/contrib/peerstorage/test_peerstorage.py 2014-03-17 11:41:06 +0000
@@ -0,0 +1,69 @@
1from tests.helpers import FakeRelation
2from testtools import TestCase
3from mock import patch
4from charmhelpers.contrib import peerstorage
5
6
7TO_PATCH = ['relation_ids', 'relation_set', 'relation_get', 'local_unit']
8FAKE_RELATION_NAME = 'cluster'
9FAKE_RELATION = {
10 'cluster:0': {
11 'cluster/0': {
12 },
13 'cluster/1': {
14 },
15 'cluster/2': {
16 },
17 },
18
19}
20FAKE_RELATION_IDS = ['cluster:0']
21FAKE_LOCAL_UNIT = 'test_host'
22
23
24class TestPeerStorage(TestCase):
25 def setUp(self):
26 super(TestPeerStorage, self).setUp()
27 for m in TO_PATCH:
28 setattr(self, m, self._patch(m))
29 self.fake_relation_name = FAKE_RELATION_NAME
30 self.fake_relation = FakeRelation(FAKE_RELATION)
31 self.local_unit.return_value = FAKE_LOCAL_UNIT
32 self.relation_get.return_value = {'key1': 'value1',
33 'key2': 'value2',
34 'private-address': '127.0.0.1',
35 'public-address': '91.189.90.159'}
36
37 def _patch(self, method):
38 _m = patch('charmhelpers.contrib.peerstorage.' + method)
39 mock = _m.start()
40 self.addCleanup(_m.stop)
41 return mock
42
43 def test_peer_retrieve_no_relation(self):
44 self.relation_ids.return_value = []
45 self.assertRaises(ValueError, peerstorage.peer_retrieve, 'key', relation_name=self.fake_relation_name)
46
47 def test_peer_retrieve_with_relation(self):
48 self.relation_ids.return_value = FAKE_RELATION_IDS
49 peerstorage.peer_retrieve('key', self.fake_relation_name)
50 self.relation_get.assert_called_with(attribute='key', rid=FAKE_RELATION_IDS[0], unit=FAKE_LOCAL_UNIT)
51
52 def test_peer_store_no_relation(self):
53 self.relation_ids.return_value = []
54 self.assertRaises(ValueError, peerstorage.peer_store, 'key', 'value', relation_name=self.fake_relation_name)
55
56 def test_peer_store_with_relation(self):
57 self.relation_ids.return_value = FAKE_RELATION_IDS
58 peerstorage.peer_store('key', 'value', self.fake_relation_name)
59 self.relation_set.assert_called_with(relation_id=FAKE_RELATION_IDS[0],
60 relation_settings={'key': 'value'})
61
62 def test_peer_echo_no_includes(self):
63 peerstorage.peer_echo()
64 self.relation_set.assert_called_with(relation_settings={'key1': 'value1',
65 'key2': 'value2'})
66
67 def test_peer_echo_includes(self):
68 peerstorage.peer_echo(['key1'])
69 self.relation_set.assert_called_with(relation_settings={'key1': 'value1'})

Subscribers

People subscribed via source and target branches