Merge lp:~gnuoy/charm-helpers/stable-1453940 into lp:~openstack-charmers/charm-helpers/stable-1604
- stable-1453940
- Merge into stable-1604
Proposed by
Liam Young
Status: | Merged |
---|---|
Merged at revision: | 371 |
Proposed branch: | lp:~gnuoy/charm-helpers/stable-1453940 |
Merge into: | lp:~openstack-charmers/charm-helpers/stable-1604 |
Diff against target: |
812 lines (+612/-30) 5 files modified
charmhelpers/contrib/openstack/context.py (+8/-9) charmhelpers/contrib/storage/linux/ceph.py (+224/-2) tests/contrib/openstack/test_os_contexts.py (+36/-4) tests/contrib/storage/test_linux_ceph.py (+337/-8) tests/helpers.py (+7/-7) |
To merge this branch: | bzr merge lp:~gnuoy/charm-helpers/stable-1453940 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Chris Glass (community) | Approve | ||
Review via email: mp+271258@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
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 2015-08-10 16:39:41 +0000 |
3 | +++ charmhelpers/contrib/openstack/context.py 2015-09-16 09:01:05 +0000 |
4 | @@ -483,13 +483,15 @@ |
5 | |
6 | log('Generating template context for ceph', level=DEBUG) |
7 | mon_hosts = [] |
8 | - auth = None |
9 | - key = None |
10 | - use_syslog = str(config('use-syslog')).lower() |
11 | + ctxt = { |
12 | + 'use_syslog': str(config('use-syslog')).lower() |
13 | + } |
14 | for rid in relation_ids('ceph'): |
15 | for unit in related_units(rid): |
16 | - auth = relation_get('auth', rid=rid, unit=unit) |
17 | - key = relation_get('key', rid=rid, unit=unit) |
18 | + if not ctxt.get('auth'): |
19 | + ctxt['auth'] = relation_get('auth', rid=rid, unit=unit) |
20 | + if not ctxt.get('key'): |
21 | + ctxt['key'] = relation_get('key', rid=rid, unit=unit) |
22 | ceph_pub_addr = relation_get('ceph-public-address', rid=rid, |
23 | unit=unit) |
24 | unit_priv_addr = relation_get('private-address', rid=rid, |
25 | @@ -498,10 +500,7 @@ |
26 | ceph_addr = format_ipv6_addr(ceph_addr) or ceph_addr |
27 | mon_hosts.append(ceph_addr) |
28 | |
29 | - ctxt = {'mon_hosts': ' '.join(sorted(mon_hosts)), |
30 | - 'auth': auth, |
31 | - 'key': key, |
32 | - 'use_syslog': use_syslog} |
33 | + ctxt['mon_hosts'] = ' '.join(sorted(mon_hosts)) |
34 | |
35 | if not os.path.isdir('/etc/ceph'): |
36 | os.mkdir('/etc/ceph') |
37 | |
38 | === modified file 'charmhelpers/contrib/storage/linux/ceph.py' |
39 | --- charmhelpers/contrib/storage/linux/ceph.py 2015-08-10 16:39:41 +0000 |
40 | +++ charmhelpers/contrib/storage/linux/ceph.py 2015-09-16 09:01:05 +0000 |
41 | @@ -28,6 +28,7 @@ |
42 | import shutil |
43 | import json |
44 | import time |
45 | +import uuid |
46 | |
47 | from subprocess import ( |
48 | check_call, |
49 | @@ -35,8 +36,10 @@ |
50 | CalledProcessError, |
51 | ) |
52 | from charmhelpers.core.hookenv import ( |
53 | + local_unit, |
54 | relation_get, |
55 | relation_ids, |
56 | + relation_set, |
57 | related_units, |
58 | log, |
59 | DEBUG, |
60 | @@ -411,17 +414,52 @@ |
61 | |
62 | The API is versioned and defaults to version 1. |
63 | """ |
64 | - def __init__(self, api_version=1): |
65 | + def __init__(self, api_version=1, request_id=None): |
66 | self.api_version = api_version |
67 | + if request_id: |
68 | + self.request_id = request_id |
69 | + else: |
70 | + self.request_id = str(uuid.uuid1()) |
71 | self.ops = [] |
72 | |
73 | def add_op_create_pool(self, name, replica_count=3): |
74 | self.ops.append({'op': 'create-pool', 'name': name, |
75 | 'replicas': replica_count}) |
76 | |
77 | + def set_ops(self, ops): |
78 | + """Set request ops to provided value. |
79 | + |
80 | + Useful for injecting ops that come from a previous request |
81 | + to allow comparisons to ensure validity. |
82 | + """ |
83 | + self.ops = ops |
84 | + |
85 | @property |
86 | def request(self): |
87 | - return json.dumps({'api-version': self.api_version, 'ops': self.ops}) |
88 | + return json.dumps({'api-version': self.api_version, 'ops': self.ops, |
89 | + 'request-id': self.request_id}) |
90 | + |
91 | + def _ops_equal(self, other): |
92 | + if len(self.ops) == len(other.ops): |
93 | + for req_no in range(0, len(self.ops)): |
94 | + for key in ['replicas', 'name', 'op']: |
95 | + if self.ops[req_no][key] != other.ops[req_no][key]: |
96 | + return False |
97 | + else: |
98 | + return False |
99 | + return True |
100 | + |
101 | + def __eq__(self, other): |
102 | + if not isinstance(other, self.__class__): |
103 | + return False |
104 | + if self.api_version == other.api_version and \ |
105 | + self._ops_equal(other): |
106 | + return True |
107 | + else: |
108 | + return False |
109 | + |
110 | + def __ne__(self, other): |
111 | + return not self.__eq__(other) |
112 | |
113 | |
114 | class CephBrokerRsp(object): |
115 | @@ -431,14 +469,198 @@ |
116 | |
117 | The API is versioned and defaults to version 1. |
118 | """ |
119 | + |
120 | def __init__(self, encoded_rsp): |
121 | self.api_version = None |
122 | self.rsp = json.loads(encoded_rsp) |
123 | |
124 | @property |
125 | + def request_id(self): |
126 | + return self.rsp.get('request-id') |
127 | + |
128 | + @property |
129 | def exit_code(self): |
130 | return self.rsp.get('exit-code') |
131 | |
132 | @property |
133 | def exit_msg(self): |
134 | return self.rsp.get('stderr') |
135 | + |
136 | + |
137 | +# Ceph Broker Conversation: |
138 | +# If a charm needs an action to be taken by ceph it can create a CephBrokerRq |
139 | +# and send that request to ceph via the ceph relation. The CephBrokerRq has a |
140 | +# unique id so that the client can identity which CephBrokerRsp is associated |
141 | +# with the request. Ceph will also respond to each client unit individually |
142 | +# creating a response key per client unit eg glance/0 will get a CephBrokerRsp |
143 | +# via key broker-rsp-glance-0 |
144 | +# |
145 | +# To use this the charm can just do something like: |
146 | +# |
147 | +# from charmhelpers.contrib.storage.linux.ceph import ( |
148 | +# send_request_if_needed, |
149 | +# is_request_complete, |
150 | +# CephBrokerRq, |
151 | +# ) |
152 | +# |
153 | +# @hooks.hook('ceph-relation-changed') |
154 | +# def ceph_changed(): |
155 | +# rq = CephBrokerRq() |
156 | +# rq.add_op_create_pool(name='poolname', replica_count=3) |
157 | +# |
158 | +# if is_request_complete(rq): |
159 | +# <Request complete actions> |
160 | +# else: |
161 | +# send_request_if_needed(get_ceph_request()) |
162 | +# |
163 | +# CephBrokerRq and CephBrokerRsp are serialized into JSON. Below is an example |
164 | +# of glance having sent a request to ceph which ceph has successfully processed |
165 | +# 'ceph:8': { |
166 | +# 'ceph/0': { |
167 | +# 'auth': 'cephx', |
168 | +# 'broker-rsp-glance-0': '{"request-id": "0bc7dc54", "exit-code": 0}', |
169 | +# 'broker_rsp': '{"request-id": "0da543b8", "exit-code": 0}', |
170 | +# 'ceph-public-address': '10.5.44.103', |
171 | +# 'key': 'AQCLDttVuHXINhAAvI144CB09dYchhHyTUY9BQ==', |
172 | +# 'private-address': '10.5.44.103', |
173 | +# }, |
174 | +# 'glance/0': { |
175 | +# 'broker_req': ('{"api-version": 1, "request-id": "0bc7dc54", ' |
176 | +# '"ops": [{"replicas": 3, "name": "glance", ' |
177 | +# '"op": "create-pool"}]}'), |
178 | +# 'private-address': '10.5.44.109', |
179 | +# }, |
180 | +# } |
181 | + |
182 | +def get_previous_request(rid): |
183 | + """Return the last ceph broker request sent on a given relation |
184 | + |
185 | + @param rid: Relation id to query for request |
186 | + """ |
187 | + request = None |
188 | + broker_req = relation_get(attribute='broker_req', rid=rid, |
189 | + unit=local_unit()) |
190 | + if broker_req: |
191 | + request_data = json.loads(broker_req) |
192 | + request = CephBrokerRq(api_version=request_data['api-version'], |
193 | + request_id=request_data['request-id']) |
194 | + request.set_ops(request_data['ops']) |
195 | + |
196 | + return request |
197 | + |
198 | + |
199 | +def get_request_states(request): |
200 | + """Return a dict of requests per relation id with their corresponding |
201 | + completion state. |
202 | + |
203 | + This allows a charm, which has a request for ceph, to see whether there is |
204 | + an equivalent request already being processed and if so what state that |
205 | + request is in. |
206 | + |
207 | + @param request: A CephBrokerRq object |
208 | + """ |
209 | + complete = [] |
210 | + requests = {} |
211 | + for rid in relation_ids('ceph'): |
212 | + complete = False |
213 | + previous_request = get_previous_request(rid) |
214 | + if request == previous_request: |
215 | + sent = True |
216 | + complete = is_request_complete_for_rid(previous_request, rid) |
217 | + else: |
218 | + sent = False |
219 | + complete = False |
220 | + |
221 | + requests[rid] = { |
222 | + 'sent': sent, |
223 | + 'complete': complete, |
224 | + } |
225 | + |
226 | + return requests |
227 | + |
228 | + |
229 | +def is_request_sent(request): |
230 | + """Check to see if a functionally equivalent request has already been sent |
231 | + |
232 | + Returns True if a similair request has been sent |
233 | + |
234 | + @param request: A CephBrokerRq object |
235 | + """ |
236 | + states = get_request_states(request) |
237 | + for rid in states.keys(): |
238 | + if not states[rid]['sent']: |
239 | + return False |
240 | + |
241 | + return True |
242 | + |
243 | + |
244 | +def is_request_complete(request): |
245 | + """Check to see if a functionally equivalent request has already been |
246 | + completed |
247 | + |
248 | + Returns True if a similair request has been completed |
249 | + |
250 | + @param request: A CephBrokerRq object |
251 | + """ |
252 | + states = get_request_states(request) |
253 | + for rid in states.keys(): |
254 | + if not states[rid]['complete']: |
255 | + return False |
256 | + |
257 | + return True |
258 | + |
259 | + |
260 | +def is_request_complete_for_rid(request, rid): |
261 | + """Check if a given request has been completed on the given relation |
262 | + |
263 | + @param request: A CephBrokerRq object |
264 | + @param rid: Relation ID |
265 | + """ |
266 | + broker_key = get_broker_rsp_key() |
267 | + for unit in related_units(rid): |
268 | + rdata = relation_get(rid=rid, unit=unit) |
269 | + if rdata.get(broker_key): |
270 | + rsp = CephBrokerRsp(rdata.get(broker_key)) |
271 | + if rsp.request_id == request.request_id: |
272 | + if not rsp.exit_code: |
273 | + return True |
274 | + else: |
275 | + # The remote unit sent no reply targeted at this unit so either the |
276 | + # remote ceph cluster does not support unit targeted replies or it |
277 | + # has not processed our request yet. |
278 | + if rdata.get('broker_rsp'): |
279 | + request_data = json.loads(rdata['broker_rsp']) |
280 | + if request_data.get('request-id'): |
281 | + log('Ignoring legacy broker_rsp without unit key as remote ' |
282 | + 'service supports unit specific replies', level=DEBUG) |
283 | + else: |
284 | + log('Using legacy broker_rsp as remote service does not ' |
285 | + 'supports unit specific replies', level=DEBUG) |
286 | + rsp = CephBrokerRsp(rdata['broker_rsp']) |
287 | + if not rsp.exit_code: |
288 | + return True |
289 | + |
290 | + return False |
291 | + |
292 | + |
293 | +def get_broker_rsp_key(): |
294 | + """Return broker response key for this unit |
295 | + |
296 | + This is the key that ceph is going to use to pass request status |
297 | + information back to this unit |
298 | + """ |
299 | + return 'broker-rsp-' + local_unit().replace('/', '-') |
300 | + |
301 | + |
302 | +def send_request_if_needed(request): |
303 | + """Send broker request if an equivalent request has not already been sent |
304 | + |
305 | + @param request: A CephBrokerRq object |
306 | + """ |
307 | + if is_request_sent(request): |
308 | + log('Request already sent but not complete, not sending new request', |
309 | + level=DEBUG) |
310 | + else: |
311 | + for rid in relation_ids('ceph'): |
312 | + log('Sending request {}'.format(request.request_id), level=DEBUG) |
313 | + relation_set(relation_id=rid, broker_req=request.request) |
314 | |
315 | === modified file 'tests/contrib/openstack/test_os_contexts.py' |
316 | --- tests/contrib/openstack/test_os_contexts.py 2015-08-10 16:39:41 +0000 |
317 | +++ tests/contrib/openstack/test_os_contexts.py 2015-09-16 09:01:05 +0000 |
318 | @@ -74,7 +74,7 @@ |
319 | |
320 | def relation_ids(self, relation): |
321 | rids = [] |
322 | - for rid in self.relation_data.keys(): |
323 | + for rid in sorted(self.relation_data.keys()): |
324 | if relation + ':' in rid: |
325 | rids.append(rid) |
326 | return rids |
327 | @@ -82,7 +82,7 @@ |
328 | def relation_units(self, relation_id): |
329 | if relation_id not in self.relation_data: |
330 | return None |
331 | - return self.relation_data[relation_id].keys() |
332 | + return sorted(self.relation_data[relation_id].keys()) |
333 | |
334 | SHARED_DB_RELATION = { |
335 | 'db_host': 'dbserver.local', |
336 | @@ -1029,7 +1029,7 @@ |
337 | def test_ceph_context_with_data(self, ensure_packages, mkdir, isdir, |
338 | config): |
339 | '''Test ceph context with all relation data''' |
340 | - config.return_value = True |
341 | + config.side_effect = fake_config({'use-syslog': 'True'}) |
342 | isdir.return_value = False |
343 | relation = FakeRelation(relation_data=CEPH_RELATION) |
344 | self.relation_get.side_effect = relation.get |
345 | @@ -1051,7 +1051,7 @@ |
346 | @patch.object(context, 'ensure_packages') |
347 | def test_ceph_context_with_missing_data(self, ensure_packages, mkdir): |
348 | '''Test ceph context with missing relation data''' |
349 | - relation = copy(CEPH_RELATION) |
350 | + relation = deepcopy(CEPH_RELATION) |
351 | for k, v in six.iteritems(relation): |
352 | for u in six.iterkeys(v): |
353 | del relation[k][u]['auth'] |
354 | @@ -1068,6 +1068,38 @@ |
355 | @patch('os.path.isdir') |
356 | @patch('os.mkdir') |
357 | @patch.object(context, 'ensure_packages') |
358 | + def test_ceph_context_partial_missing_data(self, ensure_packages, mkdir, |
359 | + isdir, config): |
360 | + '''Test ceph context last unit missing data |
361 | + |
362 | + Tests a fix to a previously bug which meant only the config from |
363 | + last unit was returned so if a valid value was supplied from an |
364 | + earlier unit it would be ignored''' |
365 | + config.side_effect = fake_config({'use-syslog': 'True'}) |
366 | + relation = deepcopy(CEPH_RELATION) |
367 | + for k, v in six.iteritems(relation): |
368 | + last_unit = sorted(six.iterkeys(v))[-1] |
369 | + unit_data = relation[k][last_unit] |
370 | + del unit_data['auth'] |
371 | + relation[k][last_unit] = unit_data |
372 | + relation = FakeRelation(relation_data=relation) |
373 | + self.relation_get.side_effect = relation.get |
374 | + self.relation_ids.side_effect = relation.relation_ids |
375 | + self.related_units.side_effect = relation.relation_units |
376 | + ceph = context.CephContext() |
377 | + result = ceph() |
378 | + expected = { |
379 | + 'mon_hosts': 'ceph_node1 ceph_node2', |
380 | + 'auth': 'foo', |
381 | + 'key': 'bar', |
382 | + 'use_syslog': 'true' |
383 | + } |
384 | + self.assertEquals(result, expected) |
385 | + |
386 | + @patch.object(context, 'config') |
387 | + @patch('os.path.isdir') |
388 | + @patch('os.mkdir') |
389 | + @patch.object(context, 'ensure_packages') |
390 | def test_ceph_context_with_public_addr( |
391 | self, ensure_packages, mkdir, isdir, config): |
392 | '''Test ceph context in host with multiple networks with all |
393 | |
394 | === modified file 'tests/contrib/storage/test_linux_ceph.py' |
395 | --- tests/contrib/storage/test_linux_ceph.py 2015-08-10 16:39:41 +0000 |
396 | +++ tests/contrib/storage/test_linux_ceph.py 2015-09-16 09:01:05 +0000 |
397 | @@ -5,10 +5,11 @@ |
398 | from threading import Timer |
399 | from testtools import TestCase |
400 | import json |
401 | +import copy |
402 | |
403 | import charmhelpers.contrib.storage.linux.ceph as ceph_utils |
404 | from subprocess import CalledProcessError |
405 | -from tests.helpers import patch_open |
406 | +from tests.helpers import patch_open, FakeRelation |
407 | import nose.plugins.attrib |
408 | import os |
409 | import time |
410 | @@ -31,6 +32,46 @@ |
411 | baz |
412 | """ |
413 | |
414 | +CEPH_CLIENT_RELATION = { |
415 | + 'ceph:8': { |
416 | + 'ceph/0': { |
417 | + 'auth': 'cephx', |
418 | + 'broker-rsp-glance-0': '{"request-id": "0bc7dc54", "exit-code": 0}', |
419 | + 'broker-rsp-glance-1': '{"request-id": "0880e22a", "exit-code": 0}', |
420 | + 'broker-rsp-glance-2': '{"request-id": "0da543b8", "exit-code": 0}', |
421 | + 'broker_rsp': '{"request-id": "0da543b8", "exit-code": 0}', |
422 | + 'ceph-public-address': '10.5.44.103', |
423 | + 'key': 'AQCLDttVuHXINhAAvI144CB09dYchhHyTUY9BQ==', |
424 | + 'private-address': '10.5.44.103', |
425 | + }, |
426 | + 'ceph/1': { |
427 | + 'auth': 'cephx', |
428 | + 'ceph-public-address': '10.5.44.104', |
429 | + 'key': 'AQCLDttVuHXINhAAvI144CB09dYchhHyTUY9BQ==', |
430 | + 'private-address': '10.5.44.104', |
431 | + }, |
432 | + 'ceph/2': { |
433 | + 'auth': 'cephx', |
434 | + 'ceph-public-address': '10.5.44.105', |
435 | + 'key': 'AQCLDttVuHXINhAAvI144CB09dYchhHyTUY9BQ==', |
436 | + 'private-address': '10.5.44.105', |
437 | + }, |
438 | + 'glance/0': { |
439 | + 'broker_req': '{"api-version": 1, "request-id": "0bc7dc54", "ops": [{"replicas": 3, "name": "glance", "op": "create-pool"}]}', |
440 | + 'private-address': '10.5.44.109', |
441 | + }, |
442 | + } |
443 | +} |
444 | + |
445 | +CEPH_CLIENT_RELATION_LEGACY = copy.deepcopy(CEPH_CLIENT_RELATION) |
446 | +CEPH_CLIENT_RELATION_LEGACY['ceph:8']['ceph/0'] = { |
447 | + 'auth': 'cephx', |
448 | + 'broker_rsp': '{"exit-code": 0}', |
449 | + 'ceph-public-address': '10.5.44.103', |
450 | + 'key': 'AQCLDttVuHXINhAAvI144CB09dYchhHyTUY9BQ==', |
451 | + 'private-address': '10.5.44.103', |
452 | +} |
453 | + |
454 | |
455 | class CephUtilsTests(TestCase): |
456 | def setUp(self): |
457 | @@ -38,6 +79,10 @@ |
458 | [self._patch(m) for m in [ |
459 | 'check_call', |
460 | 'check_output', |
461 | + 'relation_get', |
462 | + 'related_units', |
463 | + 'relation_ids', |
464 | + 'relation_set', |
465 | 'log', |
466 | ]] |
467 | |
468 | @@ -563,19 +608,303 @@ |
469 | b'ceph version 0.67.4 (ad85b8bfafea6232d64cb7ba76a8b6e8252fa0c7)' |
470 | self.assertEquals(ceph_utils.ceph_version(), '0.67.4') |
471 | |
472 | - def test_ceph_broker_rq_class(self): |
473 | + @patch.object(ceph_utils, 'uuid') |
474 | + def test_ceph_broker_rq_class(self, uuid): |
475 | + uuid.uuid1.return_value = 'uuid' |
476 | rq = ceph_utils.CephBrokerRq() |
477 | rq.add_op_create_pool('pool1', replica_count=1) |
478 | rq.add_op_create_pool('pool2') |
479 | - expected = json.dumps({'api-version': 1, |
480 | - 'ops': [{'op': 'create-pool', 'name': 'pool1', |
481 | - 'replicas': 1}, |
482 | - {'op': 'create-pool', 'name': 'pool2', |
483 | - 'replicas': 3}]}) |
484 | - self.assertEqual(rq.request, expected) |
485 | + expected = { |
486 | + 'api-version': 1, |
487 | + 'request-id': 'uuid', |
488 | + 'ops': [{'op': 'create-pool', 'name': 'pool1', 'replicas': 1}, |
489 | + {'op': 'create-pool', 'name': 'pool2', 'replicas': 3}] |
490 | + } |
491 | + request_dict = json.loads(rq.request) |
492 | + for key in ['api-version', 'request-id']: |
493 | + self.assertEqual(request_dict[key], expected[key]) |
494 | + for key in ['op', 'name', 'replicas']: |
495 | + self.assertEqual(request_dict['ops'][0][key], expected['ops'][0][key]) |
496 | + self.assertEqual(request_dict['ops'][1][key], expected['ops'][1][key]) |
497 | |
498 | def test_ceph_broker_rsp_class(self): |
499 | rsp = ceph_utils.CephBrokerRsp(json.dumps({'exit-code': 0, |
500 | 'stderr': "Success"})) |
501 | self.assertEqual(rsp.exit_code, 0) |
502 | self.assertEqual(rsp.exit_msg, "Success") |
503 | + self.assertEqual(rsp.request_id, None) |
504 | + |
505 | + def test_ceph_broker_rsp_class_rqid(self): |
506 | + rsp = ceph_utils.CephBrokerRsp(json.dumps({'exit-code': 0, |
507 | + 'stderr': "Success", |
508 | + 'request-id': 'reqid1'})) |
509 | + self.assertEqual(rsp.exit_code, 0) |
510 | + self.assertEqual(rsp.exit_msg, 'Success') |
511 | + self.assertEqual(rsp.request_id, 'reqid1') |
512 | + |
513 | + def setup_client_relation(self, relation): |
514 | + relation = FakeRelation(relation) |
515 | + self.relation_get.side_effect = relation.get |
516 | + self.relation_ids.side_effect = relation.relation_ids |
517 | + self.related_units.side_effect = relation.related_units |
518 | + |
519 | +# @patch.object(ceph_utils, 'uuid') |
520 | +# @patch.object(ceph_utils, 'local_unit') |
521 | +# def test_get_request_states(self, mlocal_unit, muuid): |
522 | +# muuid.uuid1.return_value = '0bc7dc54' |
523 | + @patch.object(ceph_utils, 'local_unit') |
524 | + def test_get_request_states(self, mlocal_unit): |
525 | + mlocal_unit.return_value = 'glance/0' |
526 | + self.setup_client_relation(CEPH_CLIENT_RELATION) |
527 | + rq = ceph_utils.CephBrokerRq() |
528 | + rq.add_op_create_pool(name='glance', replica_count=3) |
529 | + expect = {'ceph:8': {'complete': True, 'sent': True}} |
530 | + self.assertEqual(ceph_utils.get_request_states(rq), expect) |
531 | + |
532 | + @patch.object(ceph_utils, 'local_unit') |
533 | + def test_get_request_states_newrq(self, mlocal_unit): |
534 | + mlocal_unit.return_value = 'glance/0' |
535 | + self.setup_client_relation(CEPH_CLIENT_RELATION) |
536 | + rq = ceph_utils.CephBrokerRq() |
537 | + rq.add_op_create_pool(name='glance', replica_count=4) |
538 | + expect = {'ceph:8': {'complete': False, 'sent': False}} |
539 | + self.assertEqual(ceph_utils.get_request_states(rq), expect) |
540 | + |
541 | + @patch.object(ceph_utils, 'local_unit') |
542 | + def test_get_request_states_pendingrq(self, mlocal_unit): |
543 | + mlocal_unit.return_value = 'glance/0' |
544 | + rel = copy.deepcopy(CEPH_CLIENT_RELATION) |
545 | + del rel['ceph:8']['ceph/0']['broker-rsp-glance-0'] |
546 | + self.setup_client_relation(rel) |
547 | + rq = ceph_utils.CephBrokerRq() |
548 | + rq.add_op_create_pool(name='glance', replica_count=3) |
549 | + expect = {'ceph:8': {'complete': False, 'sent': True}} |
550 | + self.assertEqual(ceph_utils.get_request_states(rq), expect) |
551 | + |
552 | + @patch.object(ceph_utils, 'local_unit') |
553 | + def test_get_request_states_failedrq(self, mlocal_unit): |
554 | + mlocal_unit.return_value = 'glance/0' |
555 | + rel = copy.deepcopy(CEPH_CLIENT_RELATION) |
556 | + rel['ceph:8']['ceph/0']['broker-rsp-glance-0'] = '{"request-id": "0bc7dc54", "exit-code": 1}' |
557 | + self.setup_client_relation(rel) |
558 | + rq = ceph_utils.CephBrokerRq() |
559 | + rq.add_op_create_pool(name='glance', replica_count=3) |
560 | + expect = {'ceph:8': {'complete': False, 'sent': True}} |
561 | + self.assertEqual(ceph_utils.get_request_states(rq), expect) |
562 | + |
563 | + @patch.object(ceph_utils, 'local_unit') |
564 | + def test_is_request_sent(self, mlocal_unit): |
565 | + mlocal_unit.return_value = 'glance/0' |
566 | + self.setup_client_relation(CEPH_CLIENT_RELATION) |
567 | + rq = ceph_utils.CephBrokerRq() |
568 | + rq.add_op_create_pool(name='glance', replica_count=3) |
569 | + self.assertTrue(ceph_utils.is_request_sent(rq)) |
570 | + |
571 | + @patch.object(ceph_utils, 'local_unit') |
572 | + def test_is_request_sent_newrq(self, mlocal_unit): |
573 | + mlocal_unit.return_value = 'glance/0' |
574 | + self.setup_client_relation(CEPH_CLIENT_RELATION) |
575 | + rq = ceph_utils.CephBrokerRq() |
576 | + rq.add_op_create_pool(name='glance', replica_count=4) |
577 | + self.assertFalse(ceph_utils.is_request_sent(rq)) |
578 | + |
579 | + @patch.object(ceph_utils, 'local_unit') |
580 | + def test_is_request_sent_pending(self, mlocal_unit): |
581 | + mlocal_unit.return_value = 'glance/0' |
582 | + rel = copy.deepcopy(CEPH_CLIENT_RELATION) |
583 | + del rel['ceph:8']['ceph/0']['broker-rsp-glance-0'] |
584 | + self.setup_client_relation(rel) |
585 | + rq = ceph_utils.CephBrokerRq() |
586 | + rq.add_op_create_pool(name='glance', replica_count=3) |
587 | + self.assertTrue(ceph_utils.is_request_sent(rq)) |
588 | + |
589 | + @patch.object(ceph_utils, 'local_unit') |
590 | + def test_is_request_sent_legacy(self, mlocal_unit): |
591 | + mlocal_unit.return_value = 'glance/0' |
592 | + self.setup_client_relation(CEPH_CLIENT_RELATION_LEGACY) |
593 | + rq = ceph_utils.CephBrokerRq() |
594 | + rq.add_op_create_pool(name='glance', replica_count=3) |
595 | + self.assertTrue(ceph_utils.is_request_sent(rq)) |
596 | + |
597 | + @patch.object(ceph_utils, 'local_unit') |
598 | + def test_is_request_sent_legacy_newrq(self, mlocal_unit): |
599 | + mlocal_unit.return_value = 'glance/0' |
600 | + self.setup_client_relation(CEPH_CLIENT_RELATION_LEGACY) |
601 | + rq = ceph_utils.CephBrokerRq() |
602 | + rq.add_op_create_pool(name='glance', replica_count=4) |
603 | + self.assertFalse(ceph_utils.is_request_sent(rq)) |
604 | + |
605 | + @patch.object(ceph_utils, 'local_unit') |
606 | + def test_is_request_sent_legacy_pending(self, mlocal_unit): |
607 | + mlocal_unit.return_value = 'glance/0' |
608 | + rel = copy.deepcopy(CEPH_CLIENT_RELATION_LEGACY) |
609 | + del rel['ceph:8']['ceph/0']['broker_rsp'] |
610 | + rq = ceph_utils.CephBrokerRq() |
611 | + rq.add_op_create_pool(name='glance', replica_count=3) |
612 | + self.assertTrue(ceph_utils.is_request_sent(rq)) |
613 | + |
614 | + @patch.object(ceph_utils, 'uuid') |
615 | + @patch.object(ceph_utils, 'local_unit') |
616 | + def test_is_request_complete(self, mlocal_unit, muuid): |
617 | + muuid.uuid1.return_value = '0bc7dc54' |
618 | + mlocal_unit.return_value = 'glance/0' |
619 | + self.setup_client_relation(CEPH_CLIENT_RELATION) |
620 | + rq = ceph_utils.CephBrokerRq() |
621 | + rq.add_op_create_pool(name='glance', replica_count=3) |
622 | + self.assertTrue(ceph_utils.is_request_complete(rq)) |
623 | + |
624 | + @patch.object(ceph_utils, 'local_unit') |
625 | + def test_is_request_complete_newrq(self, mlocal_unit): |
626 | + mlocal_unit.return_value = 'glance/0' |
627 | + self.setup_client_relation(CEPH_CLIENT_RELATION) |
628 | + rq = ceph_utils.CephBrokerRq() |
629 | + rq.add_op_create_pool(name='glance', replica_count=4) |
630 | + self.assertFalse(ceph_utils.is_request_complete(rq)) |
631 | + |
632 | + @patch.object(ceph_utils, 'local_unit') |
633 | + def test_is_request_complete_pending(self, mlocal_unit): |
634 | + mlocal_unit.return_value = 'glance/0' |
635 | + rel = copy.deepcopy(CEPH_CLIENT_RELATION) |
636 | + del rel['ceph:8']['ceph/0']['broker-rsp-glance-0'] |
637 | + self.setup_client_relation(rel) |
638 | + rq = ceph_utils.CephBrokerRq() |
639 | + rq.add_op_create_pool(name='glance', replica_count=3) |
640 | + self.assertFalse(ceph_utils.is_request_complete(rq)) |
641 | + |
642 | + @patch.object(ceph_utils, 'local_unit') |
643 | + def test_is_request_complete_legacy(self, mlocal_unit): |
644 | + mlocal_unit.return_value = 'glance/0' |
645 | + self.setup_client_relation(CEPH_CLIENT_RELATION_LEGACY) |
646 | + rq = ceph_utils.CephBrokerRq() |
647 | + rq.add_op_create_pool(name='glance', replica_count=3) |
648 | + self.assertTrue(ceph_utils.is_request_complete(rq)) |
649 | + |
650 | + @patch.object(ceph_utils, 'local_unit') |
651 | + def test_is_request_complete_legacy_newrq(self, mlocal_unit): |
652 | + mlocal_unit.return_value = 'glance/0' |
653 | + self.setup_client_relation(CEPH_CLIENT_RELATION_LEGACY) |
654 | + rq = ceph_utils.CephBrokerRq() |
655 | + rq.add_op_create_pool(name='glance', replica_count=4) |
656 | + self.assertFalse(ceph_utils.is_request_complete(rq)) |
657 | + |
658 | + @patch.object(ceph_utils, 'local_unit') |
659 | + def test_is_request_complete_legacy_pending(self, mlocal_unit): |
660 | + mlocal_unit.return_value = 'glance/0' |
661 | + rel = copy.deepcopy(CEPH_CLIENT_RELATION_LEGACY) |
662 | + del rel['ceph:8']['ceph/0']['broker_rsp'] |
663 | + self.setup_client_relation(rel) |
664 | + rq = ceph_utils.CephBrokerRq() |
665 | + rq.add_op_create_pool(name='glance', replica_count=3) |
666 | + self.assertFalse(ceph_utils.is_request_complete(rq)) |
667 | + |
668 | + def test_equivalent_broker_requests(self): |
669 | + rq1 = ceph_utils.CephBrokerRq() |
670 | + rq1.add_op_create_pool(name='glance', replica_count=4) |
671 | + rq2 = ceph_utils.CephBrokerRq() |
672 | + rq2.add_op_create_pool(name='glance', replica_count=4) |
673 | + self.assertTrue(rq1 == rq2) |
674 | + |
675 | + def test_equivalent_broker_requests_diff1(self): |
676 | + rq1 = ceph_utils.CephBrokerRq() |
677 | + rq1.add_op_create_pool(name='glance', replica_count=3) |
678 | + rq2 = ceph_utils.CephBrokerRq() |
679 | + rq2.add_op_create_pool(name='glance', replica_count=4) |
680 | + self.assertFalse(rq1 == rq2) |
681 | + |
682 | + def test_equivalent_broker_requests_diff2(self): |
683 | + rq1 = ceph_utils.CephBrokerRq() |
684 | + rq1.add_op_create_pool(name='glance', replica_count=3) |
685 | + rq2 = ceph_utils.CephBrokerRq() |
686 | + rq2.add_op_create_pool(name='cinder', replica_count=3) |
687 | + self.assertFalse(rq1 == rq2) |
688 | + |
689 | + def test_equivalent_broker_requests_diff3(self): |
690 | + rq1 = ceph_utils.CephBrokerRq() |
691 | + rq1.add_op_create_pool(name='glance', replica_count=3) |
692 | + rq2 = ceph_utils.CephBrokerRq(api_version=2) |
693 | + rq2.add_op_create_pool(name='glance', replica_count=3) |
694 | + self.assertFalse(rq1 == rq2) |
695 | + |
696 | + @patch.object(ceph_utils, 'uuid') |
697 | + @patch.object(ceph_utils, 'local_unit') |
698 | + def test_is_request_complete_for_rid(self, mlocal_unit, muuid): |
699 | + muuid.uuid1.return_value = '0bc7dc54' |
700 | + req = ceph_utils.CephBrokerRq() |
701 | + req.add_op_create_pool(name='glance', replica_count=3) |
702 | + mlocal_unit.return_value = 'glance/0' |
703 | + self.setup_client_relation(CEPH_CLIENT_RELATION) |
704 | + self.assertTrue(ceph_utils.is_request_complete_for_rid(req, 'ceph:8')) |
705 | + |
706 | + @patch.object(ceph_utils, 'uuid') |
707 | + @patch.object(ceph_utils, 'local_unit') |
708 | + def test_is_request_complete_for_rid_newrq(self, mlocal_unit, muuid): |
709 | + muuid.uuid1.return_value = 'a44c0fa6' |
710 | + req = ceph_utils.CephBrokerRq() |
711 | + req.add_op_create_pool(name='glance', replica_count=4) |
712 | + mlocal_unit.return_value = 'glance/0' |
713 | + self.setup_client_relation(CEPH_CLIENT_RELATION) |
714 | + self.assertFalse(ceph_utils.is_request_complete_for_rid(req, 'ceph:8')) |
715 | + |
716 | + @patch.object(ceph_utils, 'uuid') |
717 | + @patch.object(ceph_utils, 'local_unit') |
718 | + def test_is_request_complete_for_rid_failed(self, mlocal_unit, muuid): |
719 | + muuid.uuid1.return_value = '0bc7dc54' |
720 | + req = ceph_utils.CephBrokerRq() |
721 | + req.add_op_create_pool(name='glance', replica_count=4) |
722 | + mlocal_unit.return_value = 'glance/0' |
723 | + rel = copy.deepcopy(CEPH_CLIENT_RELATION) |
724 | + rel['ceph:8']['ceph/0']['broker-rsp-glance-0'] = '{"request-id": "0bc7dc54", "exit-code": 1}' |
725 | + self.setup_client_relation(rel) |
726 | + self.assertFalse(ceph_utils.is_request_complete_for_rid(req, 'ceph:8')) |
727 | + |
728 | + @patch.object(ceph_utils, 'uuid') |
729 | + @patch.object(ceph_utils, 'local_unit') |
730 | + def test_is_request_complete_for_rid_pending(self, mlocal_unit, muuid): |
731 | + muuid.uuid1.return_value = '0bc7dc54' |
732 | + req = ceph_utils.CephBrokerRq() |
733 | + req.add_op_create_pool(name='glance', replica_count=4) |
734 | + mlocal_unit.return_value = 'glance/0' |
735 | + rel = copy.deepcopy(CEPH_CLIENT_RELATION) |
736 | + del rel['ceph:8']['ceph/0']['broker-rsp-glance-0'] |
737 | + self.setup_client_relation(rel) |
738 | + self.assertFalse(ceph_utils.is_request_complete_for_rid(req, 'ceph:8')) |
739 | + |
740 | + @patch.object(ceph_utils, 'uuid') |
741 | + @patch.object(ceph_utils, 'local_unit') |
742 | + def test_is_request_complete_for_rid_legacy(self, mlocal_unit, muuid): |
743 | + muuid.uuid1.return_value = '0bc7dc54' |
744 | + req = ceph_utils.CephBrokerRq() |
745 | + req.add_op_create_pool(name='glance', replica_count=3) |
746 | + mlocal_unit.return_value = 'glance/0' |
747 | + self.setup_client_relation(CEPH_CLIENT_RELATION_LEGACY) |
748 | + self.assertTrue(ceph_utils.is_request_complete_for_rid(req, 'ceph:8')) |
749 | + |
750 | + @patch.object(ceph_utils, 'local_unit') |
751 | + def test_get_broker_rsp_key(self, mlocal_unit): |
752 | + mlocal_unit.return_value = 'glance/0' |
753 | + self.assertEqual(ceph_utils.get_broker_rsp_key(), 'broker-rsp-glance-0') |
754 | + |
755 | + @patch.object(ceph_utils, 'local_unit') |
756 | + def test_send_request_if_needed(self, mlocal_unit): |
757 | + mlocal_unit.return_value = 'glance/0' |
758 | + self.setup_client_relation(CEPH_CLIENT_RELATION) |
759 | + rq = ceph_utils.CephBrokerRq() |
760 | + rq.add_op_create_pool(name='glance', replica_count=3) |
761 | + ceph_utils.send_request_if_needed(rq) |
762 | + self.relation_set.assert_has_calls([]) |
763 | + |
764 | + @patch.object(ceph_utils, 'uuid') |
765 | + @patch.object(ceph_utils, 'local_unit') |
766 | + def test_send_request_if_needed_newrq(self, mlocal_unit, muuid): |
767 | + muuid.uuid1.return_value = 'de67511e' |
768 | + mlocal_unit.return_value = 'glance/0' |
769 | + self.setup_client_relation(CEPH_CLIENT_RELATION) |
770 | + rq = ceph_utils.CephBrokerRq() |
771 | + rq.add_op_create_pool(name='glance', replica_count=4) |
772 | + ceph_utils.send_request_if_needed(rq) |
773 | + actual = json.loads(self.relation_set.call_args_list[0][1]['broker_req']) |
774 | + self.assertEqual(actual['api-version'], 1) |
775 | + self.assertEqual(actual['request-id'], 'de67511e') |
776 | + self.assertEqual(actual['ops'][0]['replicas'], 4) |
777 | + self.assertEqual(actual['ops'][0]['op'], 'create-pool') |
778 | + self.assertEqual(actual['ops'][0]['name'], 'glance') |
779 | |
780 | === modified file 'tests/helpers.py' |
781 | --- tests/helpers.py 2014-11-25 15:07:02 +0000 |
782 | +++ tests/helpers.py 2015-09-16 09:01:05 +0000 |
783 | @@ -74,12 +74,12 @@ |
784 | def __init__(self, relation_data): |
785 | self.relation_data = relation_data |
786 | |
787 | - def get(self, attr=None, unit=None, rid=None): |
788 | + def get(self, attribute=None, unit=None, rid=None): |
789 | if not rid or rid == 'foo:0': |
790 | - if attr is None: |
791 | + if attribute is None: |
792 | return self.relation_data |
793 | - elif attr in self.relation_data: |
794 | - return self.relation_data[attr] |
795 | + elif attribute in self.relation_data: |
796 | + return self.relation_data[attribute] |
797 | return None |
798 | else: |
799 | if rid not in self.relation_data: |
800 | @@ -88,9 +88,9 @@ |
801 | relation = self.relation_data[rid][unit] |
802 | except KeyError: |
803 | return None |
804 | - if attr in relation: |
805 | - return relation[attr] |
806 | - return None |
807 | + if attribute and attribute in relation: |
808 | + return relation[attribute] |
809 | + return relation |
810 | |
811 | def relation_ids(self, relation=None): |
812 | return self.relation_data.keys() |
Looks good. Same diff as for trunk. +1