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

Proposed by James Page
Status: Merged
Merged at revision: 133
Proposed branch: lp:~openstack-charmers/charm-helpers/icehouse
Merge into: lp:charm-helpers
Diff against target: 428 lines (+264/-27)
9 files modified
charmhelpers/contrib/openstack/context.py (+9/-6)
charmhelpers/contrib/openstack/neutron.py (+28/-4)
charmhelpers/contrib/openstack/utils.py (+16/-13)
charmhelpers/contrib/peerstorage/__init__.py (+83/-0)
charmhelpers/fetch/__init__.py (+23/-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)
tests/fetch/test_fetch.py (+30/-0)
To merge this branch: bzr merge lp:~openstack-charmers/charm-helpers/icehouse
Reviewer Review Type Date Requested Status
Marco Ceppi Approve
Review via email: mp+211934@code.launchpad.net

Description of the change

WIP for icehouse release of openstack

To post a comment you must log in.
Revision history for this message
James Page (james-page) wrote :

Note that this proposal also includes the active-active branch separately proposed.

Revision history for this message
Marco Ceppi (marcoceppi) wrote :

LGTM, following conflict was resolved:

+<<<<<<< TREE
             'packages': [[headers_package(), 'openvswitch-datapath-dkms'],
+=======
+ 'packages': [[headers_package()] + determine_dkms_package(),
+>>>>>>> MERGE-SOURCE

With MERGE-SOURCE being used

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmhelpers/contrib/openstack/context.py'
2--- charmhelpers/contrib/openstack/context.py 2014-02-27 15:50:39 +0000
3+++ charmhelpers/contrib/openstack/context.py 2014-03-25 10:15:30 +0000
4@@ -199,6 +199,7 @@
5
6 ctxt = {}
7 for rid in relation_ids('amqp'):
8+ ha_vip_only = False
9 for unit in related_units(rid):
10 if relation_get('clustered', rid=rid, unit=unit):
11 ctxt['clustered'] = True
12@@ -213,16 +214,18 @@
13 unit=unit),
14 'rabbitmq_virtual_host': vhost,
15 })
16+ if relation_get('ha_queues', rid=rid, unit=unit) is not None:
17+ ctxt['rabbitmq_ha_queues'] = True
18+
19+ ha_vip_only = relation_get('ha-vip-only',
20+ rid=rid, unit=unit) is not None
21+
22 if context_complete(ctxt):
23 # Sufficient information found = break out!
24 break
25 # Used for active/active rabbitmq >= grizzly
26- if ('clustered' not in ctxt or relation_get('ha-vip-only') == 'True') and \
27- len(related_units(rid)) > 1:
28- if relation_get('ha_queues'):
29- ctxt['rabbitmq_ha_queues'] = relation_get('ha_queues')
30- else:
31- ctxt['rabbitmq_ha_queues'] = False
32+ if ('clustered' not in ctxt or ha_vip_only) \
33+ and len(related_units(rid)) > 1:
34 rabbitmq_hosts = []
35 for unit in related_units(rid):
36 rabbitmq_hosts.append(relation_get('private-address',
37
38=== modified file 'charmhelpers/contrib/openstack/neutron.py'
39--- charmhelpers/contrib/openstack/neutron.py 2013-10-16 10:45:22 +0000
40+++ charmhelpers/contrib/openstack/neutron.py 2014-03-25 10:15:30 +0000
41@@ -18,6 +18,22 @@
42 return 'linux-headers-%s' % kver
43
44
45+def kernel_version():
46+ """ Retrieve the current major kernel version as a tuple e.g. (3, 13) """
47+ kver = check_output(['uname', '-r']).strip()
48+ kver = kver.split('.')
49+ return (int(kver[0]), int(kver[1]))
50+
51+
52+def determine_dkms_package():
53+ """ Determine which DKMS package should be used based on kernel version """
54+ # NOTE: 3.13 kernels have support for GRE and VXLAN native
55+ if kernel_version() >= (3, 13):
56+ return []
57+ else:
58+ return ['openvswitch-datapath-dkms']
59+
60+
61 # legacy
62 def quantum_plugins():
63 from charmhelpers.contrib.openstack import context
64@@ -32,7 +48,7 @@
65 database=config('neutron-database'),
66 relation_prefix='neutron')],
67 'services': ['quantum-plugin-openvswitch-agent'],
68- 'packages': [[headers_package(), 'openvswitch-datapath-dkms'],
69+ 'packages': [[headers_package()] + determine_dkms_package(),
70 ['quantum-plugin-openvswitch-agent']],
71 'server_packages': ['quantum-server',
72 'quantum-plugin-openvswitch'],
73@@ -57,7 +73,8 @@
74
75 def neutron_plugins():
76 from charmhelpers.contrib.openstack import context
77- return {
78+ release = os_release('nova-common')
79+ plugins = {
80 'ovs': {
81 'config': '/etc/neutron/plugins/openvswitch/'
82 'ovs_neutron_plugin.ini',
83@@ -68,8 +85,8 @@
84 database=config('neutron-database'),
85 relation_prefix='neutron')],
86 'services': ['neutron-plugin-openvswitch-agent'],
87- 'packages': [[headers_package(), 'openvswitch-datapath-dkms'],
88- ['quantum-plugin-openvswitch-agent']],
89+ 'packages': [[headers_package()] + determine_dkms_package(),
90+ ['neutron-plugin-openvswitch-agent']],
91 'server_packages': ['neutron-server',
92 'neutron-plugin-openvswitch'],
93 'server_services': ['neutron-server']
94@@ -89,6 +106,13 @@
95 'server_services': ['neutron-server']
96 }
97 }
98+ # NOTE: patch in ml2 plugin for icehouse onwards
99+ if release >= 'icehouse':
100+ plugins['ovs']['config'] = '/etc/neutron/plugins/ml2/ml2_conf.ini'
101+ plugins['ovs']['driver'] = 'neutron.plugins.ml2.plugin.Ml2Plugin'
102+ plugins['ovs']['server_packages'] = ['neutron-server',
103+ 'neutron-plugin-ml2']
104+ return plugins
105
106
107 def neutron_plugin_attribute(plugin, attr, net_manager=None):
108
109=== modified file 'charmhelpers/contrib/openstack/utils.py'
110--- charmhelpers/contrib/openstack/utils.py 2014-01-14 12:14:15 +0000
111+++ charmhelpers/contrib/openstack/utils.py 2014-03-25 10:15:30 +0000
112@@ -65,6 +65,9 @@
113 ('1.10.0', 'havana'),
114 ('1.9.1', 'havana'),
115 ('1.9.0', 'havana'),
116+ ('1.13.0', 'icehouse'),
117+ ('1.12.0', 'icehouse'),
118+ ('1.11.0', 'icehouse'),
119 ])
120
121 DEFAULT_LOOPBACK_SIZE = '5G'
122@@ -420,19 +423,19 @@
123 Resolves hostname for given IP, or returns the input
124 if it is already a hostname.
125 """
126- if not is_ip(address):
127- return address
128-
129- try:
130- import dns.reversename
131- except ImportError:
132- apt_install('python-dnspython')
133- import dns.reversename
134-
135- rev = dns.reversename.from_address(address)
136- result = ns_query(rev)
137- if not result:
138- return None
139+ if is_ip(address):
140+ try:
141+ import dns.reversename
142+ except ImportError:
143+ apt_install('python-dnspython')
144+ import dns.reversename
145+
146+ rev = dns.reversename.from_address(address)
147+ result = ns_query(rev)
148+ if not result:
149+ return None
150+ else:
151+ result = address
152
153 if fqdn:
154 # strip trailing .
155
156=== added directory 'charmhelpers/contrib/peerstorage'
157=== added file 'charmhelpers/contrib/peerstorage/__init__.py'
158--- charmhelpers/contrib/peerstorage/__init__.py 1970-01-01 00:00:00 +0000
159+++ charmhelpers/contrib/peerstorage/__init__.py 2014-03-25 10:15:30 +0000
160@@ -0,0 +1,83 @@
161+from charmhelpers.core.hookenv import (
162+ relation_ids,
163+ relation_get,
164+ local_unit,
165+ relation_set,
166+)
167+
168+"""
169+This helper provides functions to support use of a peer relation
170+for basic key/value storage, with the added benefit that all storage
171+can be replicated across peer units, so this is really useful for
172+services that issue usernames/passwords to remote services.
173+
174+def shared_db_changed()
175+ # Only the lead unit should create passwords
176+ if not is_leader():
177+ return
178+ username = relation_get('username')
179+ key = '{}.password'.format(username)
180+ # Attempt to retrieve any existing password for this user
181+ password = peer_retrieve(key)
182+ if password is None:
183+ # New user, create password and store
184+ password = pwgen(length=64)
185+ peer_store(key, password)
186+ create_access(username, password)
187+ relation_set(password=password)
188+
189+
190+def cluster_changed()
191+ # Echo any relation data other that *-address
192+ # back onto the peer relation so all units have
193+ # all *.password keys stored on their local relation
194+ # for later retrieval.
195+ peer_echo()
196+
197+"""
198+
199+
200+def peer_retrieve(key, relation_name='cluster'):
201+ """ Retrieve a named key from peer relation relation_name """
202+ cluster_rels = relation_ids(relation_name)
203+ if len(cluster_rels) > 0:
204+ cluster_rid = cluster_rels[0]
205+ return relation_get(attribute=key, rid=cluster_rid,
206+ unit=local_unit())
207+ else:
208+ raise ValueError('Unable to detect'
209+ 'peer relation {}'.format(relation_name))
210+
211+
212+def peer_store(key, value, relation_name='cluster'):
213+ """ Store the key/value pair on the named peer relation relation_name """
214+ cluster_rels = relation_ids(relation_name)
215+ if len(cluster_rels) > 0:
216+ cluster_rid = cluster_rels[0]
217+ relation_set(relation_id=cluster_rid,
218+ relation_settings={key: value})
219+ else:
220+ raise ValueError('Unable to detect '
221+ 'peer relation {}'.format(relation_name))
222+
223+
224+def peer_echo(includes=None):
225+ """Echo filtered attributes back onto the same relation for storage
226+
227+ Note that this helper must only be called within a peer relation
228+ changed hook
229+ """
230+ rdata = relation_get()
231+ echo_data = {}
232+ if includes is None:
233+ echo_data = rdata.copy()
234+ for ex in ['private-address', 'public-address']:
235+ if ex in echo_data:
236+ echo_data.pop(ex)
237+ else:
238+ for attribute, value in rdata.iteritems():
239+ for include in includes:
240+ if include in attribute:
241+ echo_data[attribute] = value
242+ if len(echo_data) > 0:
243+ relation_set(relation_settings=echo_data)
244
245=== modified file 'charmhelpers/fetch/__init__.py'
246--- charmhelpers/fetch/__init__.py 2014-03-03 12:30:23 +0000
247+++ charmhelpers/fetch/__init__.py 2014-03-25 10:15:30 +0000
248@@ -97,6 +97,29 @@
249 subprocess.call(cmd, env=env)
250
251
252+def apt_upgrade(options=None, fatal=False, dist=False):
253+ """Upgrade all packages"""
254+ if options is None:
255+ options = ['--option=Dpkg::Options::=--force-confold']
256+
257+ cmd = ['apt-get', '--assume-yes']
258+ cmd.extend(options)
259+ if dist:
260+ cmd.append('dist-upgrade')
261+ else:
262+ cmd.append('upgrade')
263+ log("Upgrading with options: {}".format(options))
264+
265+ env = os.environ.copy()
266+ if 'DEBIAN_FRONTEND' not in env:
267+ env['DEBIAN_FRONTEND'] = 'noninteractive'
268+
269+ if fatal:
270+ subprocess.check_call(cmd, env=env)
271+ else:
272+ subprocess.call(cmd, env=env)
273+
274+
275 def apt_update(fatal=False):
276 """Update local apt cache"""
277 cmd = ['apt-get', 'update']
278
279=== modified file 'tests/contrib/openstack/test_openstack_utils.py'
280--- tests/contrib/openstack/test_openstack_utils.py 2014-01-13 10:31:33 +0000
281+++ tests/contrib/openstack/test_openstack_utils.py 2014-03-25 10:15:30 +0000
282@@ -599,11 +599,14 @@
283
284 @patch.object(openstack, 'apt_install')
285 def test_get_hostname_with_hostname(self, apt_install):
286- fake_dns = FakeDNS('5.5.5.5')
287- with patch('__builtin__.__import__', side_effect=[fake_dns]):
288- hn = openstack.get_hostname('www.ubuntu.com')
289+ hn = openstack.get_hostname('www.ubuntu.com')
290 self.assertEquals(hn, 'www.ubuntu.com')
291
292+ @patch.object(openstack, 'apt_install')
293+ def test_get_hostname_with_hostname_not_fqdn(self, apt_install):
294+ hn = openstack.get_hostname('packages.ubuntu.com', fqdn=False)
295+ self.assertEquals(hn, 'packages')
296+
297
298 if __name__ == '__main__':
299 unittest.main()
300
301=== modified file 'tests/contrib/openstack/test_os_contexts.py'
302--- tests/contrib/openstack/test_os_contexts.py 2014-02-27 16:20:51 +0000
303+++ tests/contrib/openstack/test_os_contexts.py 2014-03-25 10:15:30 +0000
304@@ -337,7 +337,6 @@
305 'rabbitmq_user': 'adam',
306 'rabbitmq_virtual_host': 'foo',
307 'rabbitmq_hosts': 'rabbithost2,rabbithost1',
308- 'rabbitmq_ha_queues': False
309 }
310 self.assertEquals(result, expected)
311
312
313=== added directory 'tests/contrib/peerstorage'
314=== added file 'tests/contrib/peerstorage/__init__.py'
315=== added file 'tests/contrib/peerstorage/test_peerstorage.py'
316--- tests/contrib/peerstorage/test_peerstorage.py 1970-01-01 00:00:00 +0000
317+++ tests/contrib/peerstorage/test_peerstorage.py 2014-03-25 10:15:30 +0000
318@@ -0,0 +1,69 @@
319+from tests.helpers import FakeRelation
320+from testtools import TestCase
321+from mock import patch
322+from charmhelpers.contrib import peerstorage
323+
324+
325+TO_PATCH = ['relation_ids', 'relation_set', 'relation_get', 'local_unit']
326+FAKE_RELATION_NAME = 'cluster'
327+FAKE_RELATION = {
328+ 'cluster:0': {
329+ 'cluster/0': {
330+ },
331+ 'cluster/1': {
332+ },
333+ 'cluster/2': {
334+ },
335+ },
336+
337+}
338+FAKE_RELATION_IDS = ['cluster:0']
339+FAKE_LOCAL_UNIT = 'test_host'
340+
341+
342+class TestPeerStorage(TestCase):
343+ def setUp(self):
344+ super(TestPeerStorage, self).setUp()
345+ for m in TO_PATCH:
346+ setattr(self, m, self._patch(m))
347+ self.fake_relation_name = FAKE_RELATION_NAME
348+ self.fake_relation = FakeRelation(FAKE_RELATION)
349+ self.local_unit.return_value = FAKE_LOCAL_UNIT
350+ self.relation_get.return_value = {'key1': 'value1',
351+ 'key2': 'value2',
352+ 'private-address': '127.0.0.1',
353+ 'public-address': '91.189.90.159'}
354+
355+ def _patch(self, method):
356+ _m = patch('charmhelpers.contrib.peerstorage.' + method)
357+ mock = _m.start()
358+ self.addCleanup(_m.stop)
359+ return mock
360+
361+ def test_peer_retrieve_no_relation(self):
362+ self.relation_ids.return_value = []
363+ self.assertRaises(ValueError, peerstorage.peer_retrieve, 'key', relation_name=self.fake_relation_name)
364+
365+ def test_peer_retrieve_with_relation(self):
366+ self.relation_ids.return_value = FAKE_RELATION_IDS
367+ peerstorage.peer_retrieve('key', self.fake_relation_name)
368+ self.relation_get.assert_called_with(attribute='key', rid=FAKE_RELATION_IDS[0], unit=FAKE_LOCAL_UNIT)
369+
370+ def test_peer_store_no_relation(self):
371+ self.relation_ids.return_value = []
372+ self.assertRaises(ValueError, peerstorage.peer_store, 'key', 'value', relation_name=self.fake_relation_name)
373+
374+ def test_peer_store_with_relation(self):
375+ self.relation_ids.return_value = FAKE_RELATION_IDS
376+ peerstorage.peer_store('key', 'value', self.fake_relation_name)
377+ self.relation_set.assert_called_with(relation_id=FAKE_RELATION_IDS[0],
378+ relation_settings={'key': 'value'})
379+
380+ def test_peer_echo_no_includes(self):
381+ peerstorage.peer_echo()
382+ self.relation_set.assert_called_with(relation_settings={'key1': 'value1',
383+ 'key2': 'value2'})
384+
385+ def test_peer_echo_includes(self):
386+ peerstorage.peer_echo(['key1'])
387+ self.relation_set.assert_called_with(relation_settings={'key1': 'value1'})
388
389=== modified file 'tests/fetch/test_fetch.py'
390--- tests/fetch/test_fetch.py 2014-03-05 10:37:16 +0000
391+++ tests/fetch/test_fetch.py 2014-03-25 10:15:30 +0000
392@@ -391,6 +391,36 @@
393
394 @patch('subprocess.call')
395 @patch.object(fetch, 'log')
396+ def test_apt_upgrade_non_fatal(self, log, mock_call):
397+ options = ['--foo', '--bar']
398+ fetch.apt_upgrade(options)
399+
400+ mock_call.assert_called_with(['apt-get', '--assume-yes',
401+ '--foo', '--bar', 'upgrade'],
402+ env=getenv({'DEBIAN_FRONTEND': 'noninteractive'}))
403+
404+ @patch('subprocess.check_call')
405+ @patch.object(fetch, 'log')
406+ def test_apt_upgrade_fatal(self, log, mock_call):
407+ options = ['--foo', '--bar']
408+ fetch.apt_upgrade(options, fatal=True)
409+
410+ mock_call.assert_called_with(['apt-get', '--assume-yes',
411+ '--foo', '--bar', 'upgrade'],
412+ env=getenv({'DEBIAN_FRONTEND': 'noninteractive'}))
413+
414+ @patch('subprocess.check_call')
415+ @patch.object(fetch, 'log')
416+ def test_apt_dist_upgrade_fatal(self, log, mock_call):
417+ options = ['--foo', '--bar']
418+ fetch.apt_upgrade(options, fatal=True, dist=True)
419+
420+ mock_call.assert_called_with(['apt-get', '--assume-yes',
421+ '--foo', '--bar', 'dist-upgrade'],
422+ env=getenv({'DEBIAN_FRONTEND': 'noninteractive'}))
423+
424+ @patch('subprocess.call')
425+ @patch.object(fetch, 'log')
426 def test_installs_apt_packages(self, log, mock_call):
427 packages = ['foo', 'bar']
428 options = ['--foo', '--bar']

Subscribers

People subscribed via source and target branches