Merge lp:~openstack-charmers/charm-helpers/icehouse into lp:charm-helpers
- icehouse
- Merge into devel
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Marco Ceppi | Approve | ||
Review via email: mp+211934@code.launchpad.net |
Commit message
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 : | # |
Revision history for this message
Marco Ceppi (marcoceppi) wrote : | # |
LGTM, following conflict was resolved:
+<<<<<<< TREE
+=======
+ 'packages': [[headers_
+>>>>>>> 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'] |
Note that this proposal also includes the active-active branch separately proposed.