Merge lp:~james-page/charms/trusty/rabbitmq-server/network-splits into lp:charms/trusty/rabbitmq-server
- Trusty Tahr (14.04)
- network-splits
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~james-page/charms/trusty/rabbitmq-server/network-splits |
Merge into: | lp:charms/trusty/rabbitmq-server |
Diff against target: |
325 lines (+114/-35) 4 files modified
.coveragerc (+6/-0) hooks/rabbit_utils.py (+38/-0) hooks/rabbitmq_server_relations.py (+64/-33) unit_tests/test_rabbitmq_server_relations.py (+6/-2) |
To merge this branch: | bzr merge lp:~james-page/charms/trusty/rabbitmq-server/network-splits |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Cory Johns (community) | Needs Fixing | ||
Review Queue (community) | automated testing | Needs Fixing | |
James Page | Needs Resubmitting | ||
Tim Van Steenburgh (community) | Needs Fixing | ||
Review via email: mp+228152@code.launchpad.net |
This proposal has been superseded by a proposal from 2015-01-23.
Commit message
Description of the change
Add support for separate 'access-network' configuration.
Tim Van Steenburgh (tvansteenburgh) wrote : | # |
James Page (james-page) wrote : | # |
Hi Tim
https:/
RE the charm proof warning; not touching those - not having a default key is intentional 'unset' being a valid state. The code works on the fact that a config-get on unset keys returns None in JSON.
Cory Johns (johnsca) wrote : | # |
James,
I believe that Tim's point was that the policy was that you still need to provide a default value, even if that value is an empty string. No warnings on `charm proof` is listed on the policy: https:/
Also, my understanding of the "Resubmit" status was that the review was rejected and would need to be resubmitted to be approved; is that not the case?
James Page (james-page) wrote : | # |
charm proof is wrong - sorry I've discussed this numerous times with Marco - not providing a default value is a perfectly acceptable situation and is used extensively in my charms - Marco did at least drop this to a warning rather than an error but I'm not re-writing all of my charms to comply with a policy that I believe is wrong in this case.
James Page (james-page) wrote : | # |
I've raised https:/
James Page (james-page) wrote : | # |
After some discussion with Marco on IRC, he's provided me with a way to have the key, but still leave the default unset; I've updated the charm to reflect this.
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #195 trusty-
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #224 trusty-
UNIT FAIL: unit-test missing
UNIT Results (max last 2 lines):
INFO:root:Search string not found in makefile target commands.
ERROR:root:No make target was executed.
Full unit test output: http://
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #244 trusty-
AMULET FAIL: amulet-test missing
AMULET Results (max last 2 lines):
INFO:root:Search string not found in makefile target commands.
ERROR:root:No make target was executed.
Full amulet test output: http://
Build: http://
Review Queue (review-queue) wrote : | # |
This items has failed automated testing! Results available here http://
Cory Johns (johnsca) wrote : | # |
Test failure is due to a merge conflict, mainly in charmhelpers sync, but there are a couple in the charm itself, as well. I tried my hand at resolving, but there was a bit more than I felt comfortable with making decisions on without deeper knowledge of the charm.
- 72. By James Page
-
Rebase on next branch
- 73. By James Page
-
Optimize calls
- 74. By James Page
-
Re-instate configuration
- 75. By James Page
-
Aligment on config
- 76. By James Page
-
Run flake8 from venv as well
Unmerged revisions
Preview Diff
1 | === added file '.coveragerc' |
2 | --- .coveragerc 1970-01-01 00:00:00 +0000 |
3 | +++ .coveragerc 2015-01-23 08:23:23 +0000 |
4 | @@ -0,0 +1,6 @@ |
5 | +[report] |
6 | +# Regexes for lines to exclude from consideration |
7 | +exclude_lines = |
8 | + if __name__ == .__main__.: |
9 | +include= |
10 | + hooks/rabbit* |
11 | |
12 | === modified file 'hooks/rabbit_utils.py' |
13 | --- hooks/rabbit_utils.py 2015-01-19 19:02:16 +0000 |
14 | +++ hooks/rabbit_utils.py 2015-01-23 08:23:23 +0000 |
15 | @@ -37,18 +37,39 @@ |
16 | peer_retrieve |
17 | ) |
18 | |
19 | +from collections import OrderedDict |
20 | + |
21 | PACKAGES = ['rabbitmq-server', 'python-amqplib'] |
22 | |
23 | RABBITMQ_CTL = '/usr/sbin/rabbitmqctl' |
24 | COOKIE_PATH = '/var/lib/rabbitmq/.erlang.cookie' |
25 | ENV_CONF = '/etc/rabbitmq/rabbitmq-env.conf' |
26 | RABBITMQ_CONF = '/etc/rabbitmq/rabbitmq.config' |
27 | +ENABLED_PLUGINS = '/etc/rabbitmq/enabled_plugins' |
28 | RABBIT_USER = 'rabbitmq' |
29 | LIB_PATH = '/var/lib/rabbitmq/' |
30 | HOSTS_FILE = '/etc/hosts' |
31 | |
32 | _named_passwd = '/var/lib/charm/{}/{}.passwd' |
33 | |
34 | +# hook_contexts are used as a convenient mechanism to render templates |
35 | +# logically, consider building a hook_context for template rendering so |
36 | +# the charm doesn't concern itself with template specifics etc. |
37 | +CONFIG_FILES = OrderedDict([ |
38 | + (RABBITMQ_CONF, { |
39 | + 'hook_contexts': None, |
40 | + 'services': ['rabbitmq-server'] |
41 | + }), |
42 | + (ENV_CONF, { |
43 | + 'hook_contexts': None, |
44 | + 'services': ['rabbitmq-server'] |
45 | + }), |
46 | + (ENABLED_PLUGINS, { |
47 | + 'hook_contexts': None, |
48 | + 'services': ['rabbitmq-server'] |
49 | + }), |
50 | +]) |
51 | + |
52 | |
53 | class RabbitmqError(Exception): |
54 | pass |
55 | @@ -532,3 +553,20 @@ |
56 | if lsb_release()['DISTRIB_CODENAME'].lower() < "trusty": |
57 | raise Exception("IPv6 is not supported in the charms for Ubuntu " |
58 | "versions less than Trusty 14.04") |
59 | + |
60 | + |
61 | +def restart_map(): |
62 | + '''Determine the correct resource map to be passed to |
63 | + charmhelpers.core.restart_on_change() based on the services configured. |
64 | + |
65 | + :returns: dict: A dictionary mapping config file to lists of services |
66 | + that should be restarted when file changes. |
67 | + ''' |
68 | + _map = [] |
69 | + for f, ctxt in CONFIG_FILES.iteritems(): |
70 | + svcs = [] |
71 | + for svc in ctxt['services']: |
72 | + svcs.append(svc) |
73 | + if svcs: |
74 | + _map.append((f, svcs)) |
75 | + return OrderedDict(_map) |
76 | |
77 | === modified file 'hooks/rabbitmq_server_relations.py' |
78 | --- hooks/rabbitmq_server_relations.py 2015-01-12 21:16:00 +0000 |
79 | +++ hooks/rabbitmq_server_relations.py 2015-01-23 08:23:23 +0000 |
80 | @@ -31,11 +31,15 @@ |
81 | from charmhelpers.fetch import ( |
82 | add_source, |
83 | apt_update, |
84 | - apt_install) |
85 | + apt_install, |
86 | +) |
87 | |
88 | from charmhelpers.core.hookenv import ( |
89 | - open_port, close_port, |
90 | - log, ERROR, |
91 | + open_port, |
92 | + close_port, |
93 | + log, |
94 | + ERROR, |
95 | + INFO, |
96 | relation_get, |
97 | relation_set, |
98 | relation_ids, |
99 | @@ -50,7 +54,11 @@ |
100 | UnregisteredHookError |
101 | ) |
102 | from charmhelpers.core.host import ( |
103 | - rsync, service_stop, service_restart, cmp_pkgrevno |
104 | + cmp_pkgrevno, |
105 | + restart_on_change, |
106 | + rsync, |
107 | + service_stop, |
108 | + service_restart, |
109 | ) |
110 | from charmhelpers.contrib.charmsupport.nrpe import NRPE |
111 | from charmhelpers.contrib.ssl.service import ServiceCA |
112 | @@ -63,6 +71,8 @@ |
113 | peer_retrieve_by_prefix, |
114 | ) |
115 | |
116 | +from charmhelpers.contrib.network.ip import get_address_in_network |
117 | + |
118 | hooks = Hooks() |
119 | |
120 | SERVICE_NAME = os.getenv('JUJU_UNIT_NAME').split('/')[0] |
121 | @@ -150,7 +160,16 @@ |
122 | if config('prefer-ipv6'): |
123 | relation_settings['private-address'] = get_ipv6_addr()[0] |
124 | else: |
125 | - relation_settings['hostname'] = unit_get('private-address') |
126 | + # NOTE(jamespage) |
127 | + # override private-address settings if access-network is |
128 | + # configured and an appropriate network interface is configured. |
129 | + relation_settings['hostname'] = \ |
130 | + get_address_in_network(config('access-network'), |
131 | + unit_get('private-address')) |
132 | + relation_settings['private-address'] = \ |
133 | + get_address_in_network(config('access-network'), |
134 | + unit_get('private-address')) |
135 | + |
136 | |
137 | configure_client_ssl(relation_settings) |
138 | |
139 | @@ -167,6 +186,7 @@ |
140 | # set if need HA queues or not |
141 | if cmp_pkgrevno('rabbitmq-server', '3.0.1') < 0: |
142 | relation_settings['ha_queues'] = True |
143 | + |
144 | peer_store_and_set(relation_id=relation_id, |
145 | relation_settings=relation_settings) |
146 | |
147 | @@ -200,9 +220,10 @@ |
148 | # then use the current hostname |
149 | nodename = socket.gethostname() |
150 | |
151 | - if nodename: |
152 | + if nodename and rabbit.get_node_name() != nodename: |
153 | log('forcing nodename=%s' % nodename) |
154 | - # need to stop it under current nodename |
155 | + # would like to have used the restart_on_change decorator, but |
156 | + # need to stop it under current nodename prior to updating env |
157 | service_stop('rabbitmq-server') |
158 | rabbit.update_rmq_env_conf(hostname='rabbit@%s' % nodename, |
159 | ipv6=config('prefer-ipv6')) |
160 | @@ -212,7 +233,6 @@ |
161 | log('cluster_joined: Relation greater.') |
162 | return |
163 | |
164 | - rabbit.COOKIE_PATH = '/var/lib/rabbitmq/.erlang.cookie' |
165 | if not os.path.isfile(rabbit.COOKIE_PATH): |
166 | log('erlang cookie missing from %s' % rabbit.COOKIE_PATH, |
167 | level=ERROR) |
168 | @@ -225,7 +245,7 @@ |
169 | def cluster_changed(): |
170 | rdata = relation_get() |
171 | if 'cookie' not in rdata: |
172 | - log('cluster_joined: cookie not yet set.') |
173 | + log('cluster_joined: cookie not yet set.', level=INFO) |
174 | return |
175 | |
176 | if config('prefer-ipv6') and rdata.get('hostname'): |
177 | @@ -239,21 +259,13 @@ |
178 | whitelist = [a for a in rdata.keys() if a not in blacklist] |
179 | peer_echo(includes=whitelist) |
180 | |
181 | - # sync cookie |
182 | - cookie = peer_retrieve('cookie') |
183 | - if open(rabbit.COOKIE_PATH, 'r').read().strip() == cookie: |
184 | - log('Cookie already synchronized with peer.') |
185 | - else: |
186 | - log('Synchronizing erlang cookie from peer.') |
187 | - rabbit.service('stop') |
188 | - with open(rabbit.COOKIE_PATH, 'wb') as out: |
189 | - out.write(cookie) |
190 | - rabbit.service('start') |
191 | + # sync the cookie with peers if necessary |
192 | + update_cookie() |
193 | |
194 | if is_relation_made('ha') and \ |
195 | config('ha-vip-only') is False: |
196 | log('hacluster relation is present, skipping native ' |
197 | - 'rabbitmq cluster config.') |
198 | + 'rabbitmq cluster config.', level=INFO) |
199 | return |
200 | |
201 | # cluster with node |
202 | @@ -268,6 +280,24 @@ |
203 | amqp_changed(relation_id=rid, remote_unit=unit) |
204 | |
205 | |
206 | +def update_cookie(): |
207 | + # sync cookie |
208 | + cookie = peer_retrieve('cookie') |
209 | + cookie_local = None |
210 | + with open(rabbit.COOKIE_PATH, 'r') as f: |
211 | + cookie_local = f.read().strip() |
212 | + |
213 | + if cookie_local == cookie: |
214 | + log('Cookie already synchronized with peer.') |
215 | + return |
216 | + |
217 | + log('Synchronizing erlang cookie from peer.', level=INFO) |
218 | + service_stop('rabbitmq-server') |
219 | + with open(rabbit.COOKIE_PATH, 'wb') as out: |
220 | + out.write(cookie) |
221 | + service_restart('rabbitmq-server') |
222 | + |
223 | + |
224 | @hooks.hook('cluster-relation-departed') |
225 | def cluster_departed(): |
226 | if is_relation_made('ha') and \ |
227 | @@ -407,13 +437,13 @@ |
228 | rbd_size = config('rbd-size') |
229 | sizemb = int(rbd_size.split('G')[0]) * 1024 |
230 | blk_device = '/dev/rbd/%s/%s' % (POOL_NAME, rbd_img) |
231 | - # rbd_pool_rep_count = config('ceph-osd-replication-count') |
232 | + ceph.create_pool(service=SERVICE_NAME, name=POOL_NAME, |
233 | + replicas=int(config('ceph-osd-replication-count'))) |
234 | ceph.ensure_ceph_storage(service=SERVICE_NAME, pool=POOL_NAME, |
235 | rbd_img=rbd_img, sizemb=sizemb, |
236 | fstype='ext4', mount_point=RABBIT_DIR, |
237 | blk_device=blk_device, |
238 | - system_services=['rabbitmq-server']) # , |
239 | - # rbd_pool_replicas=rbd_pool_rep_count) |
240 | + system_services=['rabbitmq-server']) |
241 | subprocess.check_call(['chown', '-R', '%s:%s' % |
242 | (RABBIT_USER, RABBIT_GROUP), RABBIT_DIR]) |
243 | else: |
244 | @@ -595,12 +625,8 @@ |
245 | open_port(ssl_port) |
246 | |
247 | |
248 | -def restart_rabbit_update_nrpe(): |
249 | - service_restart('rabbitmq-server') |
250 | - update_nrpe_checks() |
251 | - |
252 | - |
253 | @hooks.hook('config-changed') |
254 | +@restart_on_change(rabbit.restart_map()) |
255 | def config_changed(): |
256 | if config('prefer-ipv6'): |
257 | rabbit.assert_charm_supports_ipv6() |
258 | @@ -622,9 +648,7 @@ |
259 | chmod(RABBIT_DIR, 0o775) |
260 | |
261 | if config('prefer-ipv6'): |
262 | - service_stop('rabbitmq-server') |
263 | rabbit.update_rmq_env_conf(ipv6=config('prefer-ipv6')) |
264 | - service_restart('rabbitmq-server') |
265 | |
266 | if config('management_plugin') is True: |
267 | rabbit.enable_plugin(MAN_PLUGIN) |
268 | @@ -641,15 +665,22 @@ |
269 | ha_is_active_active = config("ha-vip-only") |
270 | |
271 | if ha_is_active_active: |
272 | - restart_rabbit_update_nrpe() |
273 | + update_nrpe_checks() |
274 | else: |
275 | if is_elected_leader('res_rabbitmq_vip'): |
276 | - restart_rabbit_update_nrpe() |
277 | + update_nrpe_checks() |
278 | else: |
279 | log("hacluster relation is present but this node is not active" |
280 | " skipping update nrpe checks") |
281 | else: |
282 | - restart_rabbit_update_nrpe() |
283 | + update_nrpe_checks() |
284 | + |
285 | + # NOTE(jamespage) |
286 | + # trigger amqp_changed to pickup and changes to network |
287 | + # configuration via the access-network config option. |
288 | + for rid in relation_ids('amqp'): |
289 | + for unit in related_units(rid): |
290 | + amqp_changed(relation_id=rid, remote_unit=unit) |
291 | |
292 | |
293 | def pre_install_hooks(): |
294 | |
295 | === modified file 'unit_tests/test_rabbitmq_server_relations.py' |
296 | --- unit_tests/test_rabbitmq_server_relations.py 2014-11-27 10:52:50 +0000 |
297 | +++ unit_tests/test_rabbitmq_server_relations.py 2015-01-23 08:23:23 +0000 |
298 | @@ -69,14 +69,16 @@ |
299 | self.fake_repo = {'rabbitmq-server': {'pkg_vers': '3.0'}} |
300 | rabbitmq_server_relations.amqp_changed(None, None) |
301 | mock_peer_store_and_set.assert_called_with( |
302 | - relation_settings={'hostname': host_addr, |
303 | + relation_settings={'private-address': '10.1.2.3', |
304 | + 'hostname': host_addr, |
305 | 'ha_queues': True}, |
306 | relation_id=None) |
307 | |
308 | self.fake_repo = {'rabbitmq-server': {'pkg_vers': '3.0.2'}} |
309 | rabbitmq_server_relations.amqp_changed(None, None) |
310 | mock_peer_store_and_set.assert_called_with( |
311 | - relation_settings={'hostname': host_addr}, |
312 | + relation_settings={'private-address': '10.1.2.3', |
313 | + 'hostname': host_addr}, |
314 | relation_id=None) |
315 | |
316 | @patch('rabbitmq_server_relations.peer_store_and_set') |
317 | @@ -108,6 +110,8 @@ |
318 | mock_config.side_effect = config |
319 | ipv6_addr = "2001:db8:1:0:f816:3eff:fed6:c140" |
320 | mock_get_ipv6_addr.return_value = [ipv6_addr] |
321 | + host_addr = "10.1.2.3" |
322 | + unit_get.return_value = host_addr |
323 | is_elected_leader.return_value = True |
324 | relation_get.return_value = {} |
325 | is_clustered.return_value = False |
Hi James,
Thanks for this. Code review looks good, but I'm unsure how to do a functional test of this, can you provide instructions?
Also, there are some charm-proof warnings to clean up:
W: config.yaml: option access-network does not have the keys: default
W: config.yaml: option key does not have the keys: default
W: config.yaml: option source does not have the keys: default
Thanks!