Merge lp:~gnuoy/charms/trusty/ceph-radosgw/workloadstatus into lp:~openstack-charmers-archive/charms/trusty/ceph-radosgw/next

Proposed by Liam Young
Status: Merged
Merged at revision: 47
Proposed branch: lp:~gnuoy/charms/trusty/ceph-radosgw/workloadstatus
Merge into: lp:~openstack-charmers-archive/charms/trusty/ceph-radosgw/next
Diff against target: 830 lines (+362/-207)
6 files modified
hooks/ceph_radosgw_context.py (+92/-0)
hooks/hooks.py (+14/-78)
hooks/install (+1/-1)
hooks/utils.py (+45/-16)
unit_tests/test_ceph_radosgw_context.py (+202/-0)
unit_tests/test_hooks.py (+8/-112)
To merge this branch: bzr merge lp:~gnuoy/charms/trusty/ceph-radosgw/workloadstatus
Reviewer Review Type Date Requested Status
David Ames (community) Approve
Review via email: mp+273834@code.launchpad.net
To post a comment you must log in.
50. By Liam Young

Tidyup

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_unit_test #10702 ceph-radosgw-next for gnuoy mp273834
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/10702/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_lint_check #11511 ceph-radosgw-next for gnuoy mp273834
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/11511/

Revision history for this message
Liam Young (gnuoy) wrote :

This patch is a little larger than the other workload status ones because I've taken the opportunity to move the rendering of the ceph.conf over to us the templating and context helpers from charm helpers to bring it more inline with other openstack charms.

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_amulet_test #7214 ceph-radosgw-next for gnuoy mp273834
    AMULET OK: passed

Build: http://10.245.162.77:8080/job/charm_amulet_test/7214/

Revision history for this message
David Ames (thedac) wrote :

This is looking good. And it was useful for me to see the addition of a context object.

I have a couple of questions in line for the benefit of my own understanding.

review: Needs Information
51. By Liam Young

Identity relation should be an optional interface for status checks as not all versions support it. Catch inconsistent auth passed back by mons

52. By Liam Young

Fix lint and add unit test to test inconsistent auths

Revision history for this message
Liam Young (gnuoy) wrote :

Manually ran amulet:

juju-test INFO : Results: 6 passed, 0 failed, 0 errored

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_lint_check #11660 ceph-radosgw-next for gnuoy mp273834
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/11660/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_lint_check #11713 ceph-radosgw-next for gnuoy mp273834
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/11713/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_unit_test #10894 ceph-radosgw-next for gnuoy mp273834
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/10894/

Revision history for this message
David Ames (thedac) wrote :

Looks great, merging.

review: Approve
Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_amulet_test #7278 ceph-radosgw-next for gnuoy mp273834
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [functional_test] Error 1
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/12763689/
Build: http://10.245.162.77:8080/job/charm_amulet_test/7278/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'hooks/ceph_radosgw_context.py'
2--- hooks/ceph_radosgw_context.py 2015-01-14 16:48:07 +0000
3+++ hooks/ceph_radosgw_context.py 2015-10-12 10:56:13 +0000
4@@ -3,6 +3,18 @@
5 determine_api_port,
6 determine_apache_port,
7 )
8+from charmhelpers.core.host import cmp_pkgrevno
9+from charmhelpers.core.hookenv import (
10+ WARNING,
11+ config,
12+ log,
13+ relation_ids,
14+ related_units,
15+ relation_get,
16+ unit_get,
17+)
18+import socket
19+import dns.resolver
20
21
22 class HAProxyContext(context.HAProxyContext):
23@@ -27,3 +39,83 @@
24 # for haproxy.conf
25 ctxt['service_ports'] = port_mapping
26 return ctxt
27+
28+
29+class IdentityServiceContext(context.IdentityServiceContext):
30+ interfaces = ['identity-service']
31+
32+ def __call__(self):
33+ ctxt = super(IdentityServiceContext, self).__call__()
34+ if not ctxt:
35+ return
36+
37+ ctxt['admin_token'] = None
38+ for relid in relation_ids('identity-service'):
39+ for unit in related_units(relid):
40+ if not ctxt.get('admin_token'):
41+ ctxt['admin_token'] = \
42+ relation_get('admin_token', unit, relid)
43+
44+ ctxt['auth_type'] = 'keystone'
45+ ctxt['user_roles'] = config('operator-roles')
46+ ctxt['cache_size'] = config('cache-size')
47+ ctxt['revocation_check_interval'] = config('revocation-check-interval')
48+ if self.context_complete(ctxt):
49+ return ctxt
50+
51+ return {}
52+
53+
54+class MonContext(context.OSContextGenerator):
55+ interfaces = ['ceph-radosgw']
56+
57+ def __call__(self):
58+ if not relation_ids('mon'):
59+ return {}
60+ hosts = []
61+ auths = []
62+ for relid in relation_ids('mon'):
63+ for unit in related_units(relid):
64+ ceph_public_addr = relation_get('ceph-public-address', unit,
65+ relid)
66+ if ceph_public_addr:
67+ host_ip = self.get_host_ip(ceph_public_addr)
68+ hosts.append('{}:6789'.format(host_ip))
69+ _auth = relation_get('auth', unit, relid)
70+ if _auth:
71+ auths.append(_auth)
72+ if len(set(auths)) != 1:
73+ e = ("Inconsistent or absent auth returned by mon units. Setting "
74+ "auth_supported to 'none'")
75+ log(e, level=WARNING)
76+ auth = 'none'
77+ else:
78+ auth = auths[0]
79+ hosts.sort()
80+ ctxt = {
81+ 'auth_supported': auth,
82+ 'mon_hosts': ' '.join(hosts),
83+ 'hostname': socket.gethostname(),
84+ 'old_auth': cmp_pkgrevno('radosgw', "0.51") < 0,
85+ 'use_syslog': str(config('use-syslog')).lower(),
86+ 'embedded_webserver': config('use-embedded-webserver'),
87+ }
88+
89+ if self.context_complete(ctxt):
90+ return ctxt
91+
92+ return {}
93+
94+ def get_host_ip(self, hostname=None):
95+ try:
96+ if not hostname:
97+ hostname = unit_get('private-address')
98+ # Test to see if already an IPv4 address
99+ socket.inet_aton(hostname)
100+ return hostname
101+ except socket.error:
102+ # This may throw an NXDOMAIN exception; in which case
103+ # things are badly broken so just let it kill the hook
104+ answers = dns.resolver.query(hostname, 'A')
105+ if answers:
106+ return answers[0].address
107
108=== modified file 'hooks/hooks.py'
109--- hooks/hooks.py 2015-09-22 13:36:25 +0000
110+++ hooks/hooks.py 2015-10-12 10:56:13 +0000
111@@ -16,13 +16,13 @@
112 from charmhelpers.core.hookenv import (
113 relation_get,
114 relation_ids,
115- related_units,
116 config,
117 unit_get,
118 open_port,
119 relation_set,
120 log, ERROR,
121 Hooks, UnregisteredHookError,
122+ status_set,
123 )
124 from charmhelpers.fetch import (
125 apt_update,
126@@ -32,20 +32,20 @@
127 )
128 from charmhelpers.core.host import (
129 lsb_release,
130- restart_on_change
131+ restart_on_change,
132 )
133 from utils import (
134 render_template,
135- get_host_ip,
136 enable_pocket,
137 is_apache_24,
138 CEPHRG_HA_RES,
139 register_configs,
140+ REQUIRED_INTERFACES,
141+ check_optional_relations,
142 )
143
144 from charmhelpers.payload.execd import execd_preinstall
145 from charmhelpers.core.host import cmp_pkgrevno
146-from socket import gethostname as get_unit_hostname
147
148 from charmhelpers.contrib.network.ip import (
149 get_iface_for_address,
150@@ -55,7 +55,9 @@
151 canonical_url,
152 PUBLIC, INTERNAL, ADMIN,
153 )
154-
155+from charmhelpers.contrib.openstack.utils import (
156+ set_os_workload_status,
157+)
158 hooks = Hooks()
159 CONFIGS = register_configs()
160
161@@ -96,6 +98,7 @@
162
163
164 def install_packages():
165+ status_set('maintenance', 'Installing apt packages')
166 add_source(config('source'), config('key'))
167 if (config('use-ceph-optimised-packages') and
168 not config('use-embedded-webserver')):
169@@ -111,37 +114,14 @@
170
171 @hooks.hook('install.real')
172 def install():
173+ status_set('maintenance', 'Executing pre-install')
174 execd_preinstall()
175 enable_pocket('multiverse')
176 install_packages()
177 os.makedirs(NSS_DIR)
178-
179-
180-def emit_cephconf():
181- # Ensure ceph directory actually exists
182 if not os.path.exists('/etc/ceph'):
183 os.makedirs('/etc/ceph')
184
185- cephcontext = {
186- 'auth_supported': get_auth() or 'none',
187- 'mon_hosts': ' '.join(get_mon_hosts()),
188- 'hostname': get_unit_hostname(),
189- 'old_auth': cmp_pkgrevno('radosgw', "0.51") < 0,
190- 'use_syslog': str(config('use-syslog')).lower(),
191- 'embedded_webserver': config('use-embedded-webserver'),
192- }
193-
194- # Check to ensure that correct version of ceph is
195- # in use
196- if cmp_pkgrevno('radosgw', '0.55') >= 0:
197- # Add keystone configuration if found
198- ks_conf = get_keystone_conf()
199- if ks_conf:
200- cephcontext.update(ks_conf)
201-
202- with open('/etc/ceph/ceph.conf', 'w') as cephconf:
203- cephconf.write(render_template('ceph.conf', cephcontext))
204-
205
206 def emit_apacheconf():
207 apachecontext = {
208@@ -181,9 +161,9 @@
209 '/etc/haproxy/haproxy.cfg': ['haproxy']})
210 def config_changed():
211 install_packages()
212- emit_cephconf()
213 CONFIGS.write_all()
214 if not config('use-embedded-webserver'):
215+ status_set('maintenance', 'configuring apache')
216 emit_apacheconf()
217 install_www_scripts()
218 apache_sites()
219@@ -194,57 +174,11 @@
220 identity_joined(relid=r_id)
221
222
223-def get_mon_hosts():
224- hosts = []
225- for relid in relation_ids('mon'):
226- for unit in related_units(relid):
227- host_ip = get_host_ip(relation_get('ceph-public-address',
228- unit, relid))
229- hosts.append('{}:6789'.format(host_ip))
230-
231- hosts.sort()
232- return hosts
233-
234-
235-def get_auth():
236- return get_conf('auth')
237-
238-
239-def get_conf(name):
240- for relid in relation_ids('mon'):
241- for unit in related_units(relid):
242- conf = relation_get(name,
243- unit, relid)
244- if conf:
245- return conf
246- return None
247-
248-
249-def get_keystone_conf():
250- for relid in relation_ids('identity-service'):
251- for unit in related_units(relid):
252- ks_auth = {
253- 'auth_type': 'keystone',
254- 'auth_protocol':
255- relation_get('auth_protocol', unit, relid) or "http",
256- 'auth_host': relation_get('auth_host', unit, relid),
257- 'auth_port': relation_get('auth_port', unit, relid),
258- 'admin_token': relation_get('admin_token', unit, relid),
259- 'user_roles': config('operator-roles'),
260- 'cache_size': config('cache-size'),
261- 'revocation_check_interval':
262- config('revocation-check-interval')
263- }
264- if None not in ks_auth.itervalues():
265- return ks_auth
266- return None
267-
268-
269 @hooks.hook('mon-relation-departed',
270 'mon-relation-changed')
271 @restart_on_change({'/etc/ceph/ceph.conf': ['radosgw']})
272 def mon_relation():
273- emit_cephconf()
274+ CONFIGS.write_all()
275 key = relation_get('radosgw_key')
276 if key:
277 ceph.import_radosgw_key(key)
278@@ -295,7 +229,7 @@
279 @hooks.hook('identity-service-relation-changed')
280 @restart_on_change({'/etc/ceph/ceph.conf': ['radosgw']})
281 def identity_changed():
282- emit_cephconf()
283+ CONFIGS.write_all()
284 restart()
285
286
287@@ -377,3 +311,5 @@
288 hooks.execute(sys.argv)
289 except UnregisteredHookError as e:
290 log('Unknown hook {} - skipping.'.format(e))
291+ set_os_workload_status(CONFIGS, REQUIRED_INTERFACES,
292+ charm_func=check_optional_relations)
293
294=== modified file 'hooks/install'
295--- hooks/install 2015-09-22 13:36:25 +0000
296+++ hooks/install 2015-10-12 10:56:13 +0000
297@@ -2,7 +2,7 @@
298 # Wrapper to deal with newer Ubuntu versions that don't have py2 installed
299 # by default.
300
301-declare -a DEPS=('apt' 'netaddr' 'netifaces' 'pip' 'yaml')
302+declare -a DEPS=('apt' 'netaddr' 'netifaces' 'pip' 'yaml' 'jinja2' 'dnspython')
303
304 check_and_install() {
305 pkg="${1}-${2}"
306
307=== modified file 'hooks/utils.py'
308--- hooks/utils.py 2015-02-10 10:30:13 +0000
309+++ hooks/utils.py 2015-10-12 10:56:13 +0000
310@@ -1,4 +1,3 @@
311-
312 #
313 # Copyright 2012 Canonical Ltd.
314 #
315@@ -10,18 +9,29 @@
316 import socket
317 import re
318 import os
319+import dns.resolver
320+import jinja2
321 from copy import deepcopy
322 from collections import OrderedDict
323-from charmhelpers.core.hookenv import unit_get
324-from charmhelpers.fetch import apt_install
325+from charmhelpers.core.hookenv import unit_get, relation_ids, status_get
326 from charmhelpers.contrib.openstack import context, templating
327+from charmhelpers.contrib.openstack.utils import set_os_workload_status
328+from charmhelpers.contrib.hahelpers.cluster import get_hacluster_config
329+from charmhelpers.core.host import cmp_pkgrevno
330+from charmhelpers.fetch import filter_installed_packages
331
332 import ceph_radosgw_context
333
334+# The interface is said to be satisfied if anyone of the interfaces in the
335+# list has a complete context.
336+REQUIRED_INTERFACES = {
337+ 'mon': ['ceph-radosgw'],
338+}
339 CEPHRG_HA_RES = 'grp_cephrg_vips'
340 TEMPLATES_DIR = 'templates'
341 TEMPLATES = 'templates/'
342 HAPROXY_CONF = '/etc/haproxy/haproxy.cfg'
343+CEPH_CONF = '/etc/ceph/ceph.conf'
344
345 BASE_RESOURCE_MAP = OrderedDict([
346 (HAPROXY_CONF, {
347@@ -29,20 +39,12 @@
348 ceph_radosgw_context.HAProxyContext()],
349 'services': ['haproxy'],
350 }),
351+ (CEPH_CONF, {
352+ 'contexts': [ceph_radosgw_context.MonContext()],
353+ 'services': ['radosgw'],
354+ }),
355 ])
356
357-try:
358- import jinja2
359-except ImportError:
360- apt_install('python-jinja2', fatal=True)
361- import jinja2
362-
363-try:
364- import dns.resolver
365-except ImportError:
366- apt_install('python-dnspython', fatal=True)
367- import dns.resolver
368-
369
370 def resource_map():
371 '''
372@@ -58,7 +60,14 @@
373 def register_configs(release='icehouse'):
374 configs = templating.OSConfigRenderer(templates_dir=TEMPLATES,
375 openstack_release=release)
376- for cfg, rscs in resource_map().iteritems():
377+ CONFIGS = resource_map()
378+ pkg = 'radosgw'
379+ if not filter_installed_packages([pkg]) and cmp_pkgrevno(pkg, '0.55') >= 0:
380+ # Add keystone configuration if found
381+ CONFIGS[CEPH_CONF]['contexts'].append(
382+ ceph_radosgw_context.IdentityServiceContext()
383+ )
384+ for cfg, rscs in CONFIGS.iteritems():
385 configs.register(cfg, rscs['contexts'])
386 return configs
387
388@@ -103,3 +112,23 @@
389 return True
390 else:
391 return False
392+
393+
394+def check_optional_relations(configs):
395+ required_interfaces = {}
396+ if relation_ids('ha'):
397+ required_interfaces['ha'] = ['cluster']
398+ try:
399+ get_hacluster_config()
400+ except:
401+ return ('blocked',
402+ 'hacluster missing configuration: '
403+ 'vip, vip_iface, vip_cidr')
404+ if cmp_pkgrevno('radosgw', '0.55') >= 0 and \
405+ relation_ids('identity-service'):
406+ required_interfaces['identity'] = ['identity-service']
407+ if required_interfaces:
408+ set_os_workload_status(configs, required_interfaces)
409+ return status_get()
410+ else:
411+ return 'unknown', 'No optional relations'
412
413=== added file 'unit_tests/test_ceph_radosgw_context.py'
414--- unit_tests/test_ceph_radosgw_context.py 1970-01-01 00:00:00 +0000
415+++ unit_tests/test_ceph_radosgw_context.py 2015-10-12 10:56:13 +0000
416@@ -0,0 +1,202 @@
417+from mock import patch
418+
419+import ceph_radosgw_context as context
420+
421+from test_utils import CharmTestCase
422+import charmhelpers
423+
424+TO_PATCH = [
425+ 'config',
426+ 'log',
427+ 'relation_get',
428+ 'relation_ids',
429+ 'related_units',
430+ 'cmp_pkgrevno',
431+ 'socket',
432+]
433+
434+
435+class HAProxyContextTests(CharmTestCase):
436+ def setUp(self):
437+ super(HAProxyContextTests, self).setUp(context, TO_PATCH)
438+ self.relation_get.side_effect = self.test_relation.get
439+ self.config.side_effect = self.test_config.get
440+
441+ @patch('charmhelpers.contrib.openstack.context.unit_get')
442+ @patch('charmhelpers.contrib.openstack.context.local_unit')
443+ @patch('charmhelpers.contrib.openstack.context.get_host_ip')
444+ @patch('charmhelpers.contrib.openstack.context.config')
445+ @patch('charmhelpers.contrib.hahelpers.cluster.config_get')
446+ @patch('charmhelpers.contrib.openstack.context.relation_ids')
447+ @patch('charmhelpers.contrib.hahelpers.cluster.relation_ids')
448+ def test_ctxt(self, _harelation_ids, _ctxtrelation_ids, _haconfig,
449+ _ctxtconfig, _get_host_ip, _local_unit, _unit_get):
450+ _get_host_ip.return_value = '10.0.0.10'
451+ _unit_get.return_value = '10.0.0.10'
452+ _ctxtconfig.side_effect = self.test_config.get
453+ _haconfig.side_effect = self.test_config.get
454+ _harelation_ids.return_value = []
455+ haproxy_context = context.HAProxyContext()
456+ expect = {
457+ 'cephradosgw_bind_port': 70,
458+ 'service_ports': {'cephradosgw-server': [80, 70]}
459+ }
460+ self.assertEqual(expect, haproxy_context())
461+
462+
463+class IdentityServiceContextTest(CharmTestCase):
464+
465+ def setUp(self):
466+ super(IdentityServiceContextTest, self).setUp(context, TO_PATCH)
467+ self.relation_get.side_effect = self.test_relation.get
468+ self.config.side_effect = self.test_config.get
469+
470+ @patch.object(charmhelpers.contrib.openstack.context, 'format_ipv6_addr')
471+ @patch.object(charmhelpers.contrib.openstack.context, 'context_complete')
472+ @patch.object(charmhelpers.contrib.openstack.context, 'relation_get')
473+ @patch.object(charmhelpers.contrib.openstack.context, 'related_units')
474+ @patch.object(charmhelpers.contrib.openstack.context, 'relation_ids')
475+ @patch.object(charmhelpers.contrib.openstack.context, 'log')
476+ def test_ids_ctxt(self, _log, _rids, _runits, _rget, _ctxt_comp,
477+ _format_ipv6_addr):
478+ self.test_config.set('operator-roles', 'Babel')
479+ self.test_config.set('cache-size', '42')
480+ self.test_config.set('revocation-check-interval', '7500000')
481+ self.test_relation.set({'admin_token': 'ubuntutesting'})
482+ self.relation_ids.return_value = ['identity-service:5']
483+ self.related_units.return_value = ['keystone/0']
484+ _format_ipv6_addr.return_value = False
485+ _rids.return_value = 'rid1'
486+ _runits.return_value = 'runit'
487+ _ctxt_comp.return_value = True
488+ id_data = {
489+ 'service_port': 9876,
490+ 'service_host': '127.0.0.4',
491+ 'service_tenant_id': '2852107b8f8f473aaf0d769c7bbcf86b',
492+ 'auth_host': '127.0.0.5',
493+ 'auth_port': 5432,
494+ 'service_tenant': 'ten',
495+ 'service_username': 'admin',
496+ 'service_password': 'adminpass',
497+ }
498+ _rget.return_value = id_data
499+ ids_ctxt = context.IdentityServiceContext()
500+ expect = {
501+ 'admin_password': 'adminpass',
502+ 'admin_tenant_id': '2852107b8f8f473aaf0d769c7bbcf86b',
503+ 'admin_tenant_name': 'ten',
504+ 'admin_token': 'ubuntutesting',
505+ 'admin_user': 'admin',
506+ 'auth_host': '127.0.0.5',
507+ 'auth_port': 5432,
508+ 'auth_protocol': 'http',
509+ 'auth_type': 'keystone',
510+ 'cache_size': '42',
511+ 'revocation_check_interval': '7500000',
512+ 'service_host': '127.0.0.4',
513+ 'service_port': 9876,
514+ 'service_protocol': 'http',
515+ 'user_roles': 'Babel',
516+ }
517+ self.assertEqual(expect, ids_ctxt())
518+
519+ @patch.object(charmhelpers.contrib.openstack.context, 'format_ipv6_addr')
520+ @patch.object(charmhelpers.contrib.openstack.context, 'context_complete')
521+ @patch.object(charmhelpers.contrib.openstack.context, 'relation_get')
522+ @patch.object(charmhelpers.contrib.openstack.context, 'related_units')
523+ @patch.object(charmhelpers.contrib.openstack.context, 'relation_ids')
524+ @patch.object(charmhelpers.contrib.openstack.context, 'log')
525+ def test_ids_ctxt_no_admin_token(self, _log, _rids, _runits, _rget,
526+ _ctxt_comp, _format_ipv6_addr):
527+ self.test_config.set('operator-roles', 'Babel')
528+ self.test_config.set('cache-size', '42')
529+ self.test_config.set('revocation-check-interval', '7500000')
530+ self.test_relation.set({})
531+ self.relation_ids.return_value = ['identity-service:5']
532+ self.related_units.return_value = ['keystone/0']
533+ _format_ipv6_addr.return_value = False
534+ _rids.return_value = 'rid1'
535+ _runits.return_value = 'runit'
536+ _ctxt_comp.return_value = True
537+ id_data = {
538+ 'service_port': 9876,
539+ 'service_host': '127.0.0.4',
540+ 'service_tenant_id': '2852107b8f8f473aaf0d769c7bbcf86b',
541+ 'auth_host': '127.0.0.5',
542+ 'auth_port': 5432,
543+ 'service_tenant': 'ten',
544+ 'service_username': 'admin',
545+ 'service_password': 'adminpass',
546+ }
547+ _rget.return_value = id_data
548+ ids_ctxt = context.IdentityServiceContext()
549+ self.assertEqual({}, ids_ctxt())
550+
551+ @patch.object(charmhelpers.contrib.openstack.context, 'relation_ids')
552+ @patch.object(charmhelpers.contrib.openstack.context, 'log')
553+ def test_ids_ctxt_no_rels(self, _log, _rids):
554+ _rids.return_value = []
555+ ids_ctxt = context.IdentityServiceContext()
556+ self.assertEquals(ids_ctxt(), None)
557+
558+
559+class MonContextTest(CharmTestCase):
560+
561+ def setUp(self):
562+ super(MonContextTest, self).setUp(context, TO_PATCH)
563+ self.config.side_effect = self.test_config.get
564+
565+ def test_ctxt(self):
566+ self.socket.gethostname.return_value = '10.0.0.10'
567+ mon_ctxt = context.MonContext()
568+ addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
569+
570+ def _relation_get(attr, unit, rid):
571+ if attr == 'ceph-public-address':
572+ return addresses.pop()
573+ elif attr == 'auth':
574+ return 'cephx'
575+ self.relation_get.side_effect = _relation_get
576+ self.relation_ids.return_value = ['mon:6']
577+ self.related_units.return_value = ['ceph/0', 'ceph/1', 'ceph/2']
578+ expect = {
579+ 'auth_supported': 'cephx',
580+ 'embedded_webserver': False,
581+ 'hostname': '10.0.0.10',
582+ 'mon_hosts': '10.5.4.1:6789 10.5.4.2:6789 10.5.4.3:6789',
583+ 'old_auth': False,
584+ 'use_syslog': 'false'
585+ }
586+ self.assertEqual(expect, mon_ctxt())
587+
588+ def test_ctxt_missing_data(self):
589+ self.socket.gethostname.return_value = '10.0.0.10'
590+ mon_ctxt = context.MonContext()
591+ self.relation_get.return_value = None
592+ self.relation_ids.return_value = ['mon:6']
593+ self.related_units.return_value = ['ceph/0', 'ceph/1', 'ceph/2']
594+ self.assertEqual({}, mon_ctxt())
595+
596+ def test_ctxt_inconsistent_auths(self):
597+ self.socket.gethostname.return_value = '10.0.0.10'
598+ mon_ctxt = context.MonContext()
599+ addresses = ['10.5.4.1', '10.5.4.2', '10.5.4.3']
600+ auths = ['cephx', 'cephy', 'cephz']
601+
602+ def _relation_get(attr, unit, rid):
603+ if attr == 'ceph-public-address':
604+ return addresses.pop()
605+ elif attr == 'auth':
606+ return auths.pop()
607+ self.relation_get.side_effect = _relation_get
608+ self.relation_ids.return_value = ['mon:6']
609+ self.related_units.return_value = ['ceph/0', 'ceph/1', 'ceph/2']
610+ expect = {
611+ 'auth_supported': 'none',
612+ 'embedded_webserver': False,
613+ 'hostname': '10.0.0.10',
614+ 'mon_hosts': '10.5.4.1:6789 10.5.4.2:6789 10.5.4.3:6789',
615+ 'old_auth': False,
616+ 'use_syslog': 'false'
617+ }
618+ self.assertEqual(expect, mon_ctxt())
619
620=== modified file 'unit_tests/test_hooks.py'
621--- unit_tests/test_hooks.py 2015-06-04 23:06:40 +0000
622+++ unit_tests/test_hooks.py 2015-10-12 10:56:13 +0000
623@@ -23,6 +23,7 @@
624 import hooks as ceph_hooks
625
626 TO_PATCH = [
627+ 'CONFIGS',
628 'add_source',
629 'apt_update',
630 'apt_install',
631@@ -31,22 +32,20 @@
632 'cmp_pkgrevno',
633 'execd_preinstall',
634 'enable_pocket',
635- 'get_host_ip',
636 'get_iface_for_address',
637 'get_netmask_for_address',
638- 'get_unit_hostname',
639 'glob',
640 'is_apache_24',
641 'log',
642 'lsb_release',
643 'open_port',
644 'os',
645- 'related_units',
646 'relation_ids',
647 'relation_set',
648 'relation_get',
649 'render_template',
650 'shutil',
651+ 'status_set',
652 'subprocess',
653 'sys',
654 'unit_get',
655@@ -123,31 +122,6 @@
656 self.enable_pocket.assert_called_with('multiverse')
657 self.os.makedirs.called_with('/var/lib/ceph/nss')
658
659- def test_emit_cephconf(self):
660- _get_keystone_conf = self.patch('get_keystone_conf')
661- _get_auth = self.patch('get_auth')
662- _get_mon_hosts = self.patch('get_mon_hosts')
663- _get_auth.return_value = 'cephx'
664- _get_keystone_conf.return_value = {'keystone_key': 'keystone_value'}
665- _get_mon_hosts.return_value = ['10.0.0.1:6789', '10.0.0.2:6789']
666- self.get_unit_hostname.return_value = 'bob'
667- self.os.path.exists.return_value = False
668- cephcontext = {
669- 'auth_supported': 'cephx',
670- 'mon_hosts': '10.0.0.1:6789 10.0.0.2:6789',
671- 'hostname': 'bob',
672- 'old_auth': False,
673- 'use_syslog': 'false',
674- 'keystone_key': 'keystone_value',
675- 'embedded_webserver': False,
676- }
677- self.cmp_pkgrevno.return_value = 1
678- with patch_open() as (_open, _file):
679- ceph_hooks.emit_cephconf()
680- self.os.makedirs.assert_called_with('/etc/ceph')
681- _open.assert_called_with('/etc/ceph/ceph.conf', 'w')
682- self.render_template.assert_called_with('ceph.conf', cephcontext)
683-
684 def test_emit_apacheconf(self):
685 self.is_apache_24.return_value = True
686 self.unit_get.return_value = '10.0.0.1'
687@@ -195,7 +169,6 @@
688
689 def test_config_changed(self):
690 _install_packages = self.patch('install_packages')
691- _emit_cephconf = self.patch('emit_cephconf')
692 _emit_apacheconf = self.patch('emit_apacheconf')
693 _install_www_scripts = self.patch('install_www_scripts')
694 _apache_sites = self.patch('apache_sites')
695@@ -203,105 +176,30 @@
696 _apache_reload = self.patch('apache_reload')
697 ceph_hooks.config_changed()
698 _install_packages.assert_called()
699- _emit_cephconf.assert_called()
700+ self.CONFIGS.write_all.assert_called_with()
701 _emit_apacheconf.assert_called()
702 _install_www_scripts.assert_called()
703 _apache_sites.assert_called()
704 _apache_modules.assert_called()
705 _apache_reload.assert_called()
706
707- def test_get_mon_hosts(self):
708- self.relation_ids.return_value = ['monrelid']
709- self.related_units.return_value = ['monunit']
710-
711- def rel_get(k, *args):
712- return {'private-address': '127.0.0.1',
713- 'ceph-public-address': '10.0.0.1'}[k]
714-
715- self.relation_get.side_effect = rel_get
716- self.get_host_ip.side_effect = lambda x: x
717- self.assertEquals(ceph_hooks.get_mon_hosts(), ['10.0.0.1:6789'])
718-
719- def test_get_conf(self):
720- self.relation_ids.return_value = ['monrelid']
721- self.related_units.return_value = ['monunit']
722- self.relation_get.return_value = 'bob'
723- self.assertEquals(ceph_hooks.get_conf('key'), 'bob')
724-
725- def test_get_conf_nomatch(self):
726- self.relation_ids.return_value = ['monrelid']
727- self.related_units.return_value = ['monunit']
728- self.relation_get.return_value = ''
729- self.assertEquals(ceph_hooks.get_conf('key'), None)
730-
731- def test_get_auth(self):
732- self.relation_ids.return_value = ['monrelid']
733- self.related_units.return_value = ['monunit']
734- self.relation_get.return_value = 'bob'
735- self.assertEquals(ceph_hooks.get_auth(), 'bob')
736-
737- def test_get_keystone_conf(self):
738- self.test_config.set('operator-roles', 'admin')
739- self.test_config.set('cache-size', '42')
740- self.test_config.set('revocation-check-interval', '21')
741- self.relation_ids.return_value = ['idrelid']
742- self.related_units.return_value = ['idunit']
743-
744- def _relation_get(key, unit, relid):
745- ks_dict = {
746- 'auth_protocol': 'https',
747- 'auth_host': '10.0.0.2',
748- 'auth_port': '8090',
749- 'admin_token': 'sectocken',
750- }
751- return ks_dict[key]
752- self.relation_get.side_effect = _relation_get
753- self.assertEquals(ceph_hooks.get_keystone_conf(), {
754- 'auth_type': 'keystone',
755- 'auth_protocol': 'https',
756- 'admin_token': 'sectocken',
757- 'user_roles': 'admin',
758- 'auth_host': '10.0.0.2',
759- 'cache_size': '42',
760- 'auth_port': '8090',
761- 'revocation_check_interval': '21'})
762-
763- def test_get_keystone_conf_missinginfo(self):
764- self.test_config.set('operator-roles', 'admin')
765- self.test_config.set('cache-size', '42')
766- self.test_config.set('revocation-check-interval', '21')
767- self.relation_ids.return_value = ['idrelid']
768- self.related_units.return_value = ['idunit']
769-
770- def _relation_get(key, unit, relid):
771- ks_dict = {
772- 'auth_protocol': 'https',
773- 'auth_host': '10.0.0.2',
774- 'auth_port': '8090',
775- }
776- return ks_dict[key] if key in ks_dict else None
777- self.relation_get.side_effect = _relation_get
778- self.assertEquals(ceph_hooks.get_keystone_conf(), None)
779-
780 def test_mon_relation(self):
781- _emit_cephconf = self.patch('emit_cephconf')
782 _ceph = self.patch('ceph')
783 _restart = self.patch('restart')
784 self.relation_get.return_value = 'seckey'
785 ceph_hooks.mon_relation()
786 _restart.assert_called()
787 _ceph.import_radosgw_key.assert_called_with('seckey')
788- _emit_cephconf.assert_called()
789+ self.CONFIGS.write_all.assert_called_with()
790
791 def test_mon_relation_nokey(self):
792- _emit_cephconf = self.patch('emit_cephconf')
793 _ceph = self.patch('ceph')
794 _restart = self.patch('restart')
795 self.relation_get.return_value = None
796 ceph_hooks.mon_relation()
797 self.assertFalse(_ceph.import_radosgw_key.called)
798 self.assertFalse(_restart.called)
799- _emit_cephconf.assert_called()
800+ self.CONFIGS.write_all.assert_called_with()
801
802 def test_gateway_relation(self):
803 self.unit_get.return_value = 'myserver'
804@@ -374,10 +272,9 @@
805 admin_url='http://myserv:80/swift')
806
807 def test_identity_changed(self):
808- _emit_cephconf = self.patch('emit_cephconf')
809 _restart = self.patch('restart')
810 ceph_hooks.identity_changed()
811- _emit_cephconf.assert_called()
812+ self.CONFIGS.write_all.assert_called_with()
813 _restart.assert_called()
814
815 @patch('charmhelpers.contrib.openstack.ip.is_clustered')
816@@ -391,12 +288,11 @@
817 self.assertEquals(ceph_hooks.canonical_url({}, PUBLIC),
818 'http://[%s]' % ipv6_addr)
819
820- @patch.object(ceph_hooks, 'CONFIGS')
821- def test_cluster_changed(self, configs):
822+ def test_cluster_changed(self):
823 _id_joined = self.patch('identity_joined')
824 self.relation_ids.return_value = ['rid']
825 ceph_hooks.cluster_changed()
826- configs.write_all.assert_called()
827+ self.CONFIGS.write_all.assert_called_with()
828 _id_joined.assert_called_with(relid='rid')
829
830 def test_ha_relation_joined_no_vip(self):

Subscribers

People subscribed via source and target branches