Merge lp:~yolanda.robla/charms/precise/heat/postgresql into lp:~openstack-charmers-archive/charms/precise/heat/trunk

Proposed by Yolanda Robla
Status: Work in progress
Proposed branch: lp:~yolanda.robla/charms/precise/heat/postgresql
Merge into: lp:~openstack-charmers-archive/charms/precise/heat/trunk
Diff against target: 415 lines (+155/-19)
8 files modified
hooks/charmhelpers/contrib/hahelpers/cluster.py (+4/-4)
hooks/charmhelpers/contrib/openstack/context.py (+64/-11)
hooks/heat_relations.py (+33/-1)
hooks/heat_utils.py (+2/-0)
metadata.yaml (+2/-0)
revision (+1/-1)
templates/heat.conf (+2/-2)
unit_tests/test_heat_relations.py (+47/-0)
To merge this branch: bzr merge lp:~yolanda.robla/charms/precise/heat/postgresql
Reviewer Review Type Date Requested Status
OpenStack Charmers Pending
Review via email: mp+212806@code.launchpad.net

Description of the change

added postgresql

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

OK _ needs twiddling as currently no heat icehouse branch.

Unmerged revisions

24. By Yolanda Robla

resync charmhelper

23. By Yolanda Robla

added postgresql support

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'hooks/charmhelpers/contrib/hahelpers/cluster.py'
2--- hooks/charmhelpers/contrib/hahelpers/cluster.py 2013-11-21 09:41:44 +0000
3+++ hooks/charmhelpers/contrib/hahelpers/cluster.py 2014-03-26 09:39:55 +0000
4@@ -126,17 +126,17 @@
5 return public_port - (i * 10)
6
7
8-def determine_haproxy_port(public_port):
9+def determine_apache_port(public_port):
10 '''
11- Description: Determine correct proxy listening port based on public IP +
12- existence of HTTPS reverse proxy.
13+ Description: Determine correct apache listening port based on public IP +
14+ state of the cluster.
15
16 public_port: int: standard public port for given service
17
18 returns: int: the correct listening port for the HAProxy service
19 '''
20 i = 0
21- if https():
22+ if len(peer_units()) > 0 or is_clustered():
23 i += 1
24 return public_port - (i * 10)
25
26
27=== modified file 'hooks/charmhelpers/contrib/openstack/context.py'
28--- hooks/charmhelpers/contrib/openstack/context.py 2014-02-14 13:58:56 +0000
29+++ hooks/charmhelpers/contrib/openstack/context.py 2014-03-26 09:39:55 +0000
30@@ -26,11 +26,10 @@
31 )
32
33 from charmhelpers.contrib.hahelpers.cluster import (
34+ determine_apache_port,
35 determine_api_port,
36- determine_haproxy_port,
37 https,
38- is_clustered,
39- peer_units,
40+ is_clustered
41 )
42
43 from charmhelpers.contrib.hahelpers.apache import (
44@@ -147,6 +146,36 @@
45 'database': self.database,
46 'database_user': self.user,
47 'database_password': passwd,
48+ 'database_type': 'mysql',
49+ }
50+ if context_complete(ctxt):
51+ return ctxt
52+ return {}
53+
54+
55+class PostgresqlDBContext(OSContextGenerator):
56+ interfaces = ['pgsql-db']
57+
58+ def __init__(self, database=None):
59+ self.database = database
60+
61+ def __call__(self):
62+ self.database = self.database or config('database')
63+ if self.database is None:
64+ log('Could not generate postgresql_db context. '
65+ 'Missing required charm config options. '
66+ '(database name)')
67+ raise OSContextError
68+ ctxt = {}
69+
70+ for rid in relation_ids(self.interfaces[0]):
71+ for unit in related_units(rid):
72+ ctxt = {
73+ 'database_host': relation_get('host', rid=rid, unit=unit),
74+ 'database': self.database,
75+ 'database_user': relation_get('user', rid=rid, unit=unit),
76+ 'database_password': relation_get('password', rid=rid, unit=unit),
77+ 'database_type': 'postgresql',
78 }
79 if context_complete(ctxt):
80 return ctxt
81@@ -218,7 +247,12 @@
82 # Sufficient information found = break out!
83 break
84 # Used for active/active rabbitmq >= grizzly
85- if 'clustered' not in ctxt and len(related_units(rid)) > 1:
86+ if ('clustered' not in ctxt or relation_get('ha-vip-only') == 'True') and \
87+ len(related_units(rid)) > 1:
88+ if relation_get('ha_queues'):
89+ ctxt['rabbitmq_ha_queues'] = relation_get('ha_queues')
90+ else:
91+ ctxt['rabbitmq_ha_queues'] = False
92 rabbitmq_hosts = []
93 for unit in related_units(rid):
94 rabbitmq_hosts.append(relation_get('private-address',
95@@ -237,10 +271,13 @@
96 '''This generates context for /etc/ceph/ceph.conf templates'''
97 if not relation_ids('ceph'):
98 return {}
99+
100 log('Generating template context for ceph')
101+
102 mon_hosts = []
103 auth = None
104 key = None
105+ use_syslog = str(config('use-syslog')).lower()
106 for rid in relation_ids('ceph'):
107 for unit in related_units(rid):
108 mon_hosts.append(relation_get('private-address', rid=rid,
109@@ -252,6 +289,7 @@
110 'mon_hosts': ' '.join(mon_hosts),
111 'auth': auth,
112 'key': key,
113+ 'use_syslog': use_syslog
114 }
115
116 if not os.path.isdir('/etc/ceph'):
117@@ -380,17 +418,15 @@
118 'private_address': unit_get('private-address'),
119 'endpoints': []
120 }
121- for ext_port in self.external_ports:
122- if peer_units() or is_clustered():
123- int_port = determine_haproxy_port(ext_port)
124- else:
125- int_port = determine_api_port(ext_port)
126+ for api_port in self.external_ports:
127+ ext_port = determine_apache_port(api_port)
128+ int_port = determine_api_port(api_port)
129 portmap = (int(ext_port), int(int_port))
130 ctxt['endpoints'].append(portmap)
131 return ctxt
132
133
134-class NeutronContext(object):
135+class NeutronContext(OSContextGenerator):
136 interfaces = []
137
138 @property
139@@ -451,6 +487,22 @@
140
141 return nvp_ctxt
142
143+ def neutron_ctxt(self):
144+ if https():
145+ proto = 'https'
146+ else:
147+ proto = 'http'
148+ if is_clustered():
149+ host = config('vip')
150+ else:
151+ host = unit_get('private-address')
152+ url = '%s://%s:%s' % (proto, host, '9696')
153+ ctxt = {
154+ 'network_manager': self.network_manager,
155+ 'neutron_url': url,
156+ }
157+ return ctxt
158+
159 def __call__(self):
160 self._ensure_packages()
161
162@@ -460,7 +512,7 @@
163 if not self.plugin:
164 return {}
165
166- ctxt = {'network_manager': self.network_manager}
167+ ctxt = self.neutron_ctxt()
168
169 if self.plugin == 'ovs':
170 ctxt.update(self.ovs_ctxt())
171@@ -586,6 +638,7 @@
172
173
174 class SyslogContext(OSContextGenerator):
175+
176 def __call__(self):
177 ctxt = {
178 'use_syslog': config('use-syslog')
179
180=== added file 'hooks/heat_context.pyc'
181Binary files hooks/heat_context.pyc 1970-01-01 00:00:00 +0000 and hooks/heat_context.pyc 2014-03-26 09:39:55 +0000 differ
182=== modified file 'hooks/heat_relations.py'
183--- hooks/heat_relations.py 2013-12-04 17:04:42 +0000
184+++ hooks/heat_relations.py 2014-03-26 09:39:55 +0000
185@@ -18,7 +18,9 @@
186 UnregisteredHookError,
187 config,
188 charm_dir,
189+ is_relation_made,
190 log,
191+ ERROR,
192 relation_set,
193 open_port,
194 unit_get
195@@ -101,11 +103,30 @@
196
197 @hooks.hook('shared-db-relation-joined')
198 def db_joined():
199+ if is_relation_made('pgsql-db'):
200+ # error, postgresql is used
201+ e = ('Attempting to associate a mysql database when there is already '
202+ 'associated a postgresql one')
203+ log(e, level=ERROR)
204+ raise Exception(e)
205+
206 relation_set(heat_database=config('database'),
207 heat_username=config('database-user'),
208 heat_hostname=unit_get('private-address'))
209
210
211+@hooks.hook('pgsql-db-relation-joined')
212+def pgsql_db_joined():
213+ if is_relation_made('shared-db'):
214+ # raise error
215+ e = ('Attempting to associate a postgresql database when there is already '
216+ 'associated a mysql one')
217+ log(e, level=ERROR)
218+ raise Exception(e)
219+
220+ relation_set(database=config('database'))
221+
222+
223 @hooks.hook('shared-db-relation-changed')
224 @restart_on_change(restart_map())
225 def db_changed():
226@@ -116,6 +137,16 @@
227 check_call(['heat-manage', 'db_sync'])
228
229
230+@hooks.hook('pgsql-db-relation-changed')
231+@restart_on_change(restart_map())
232+def pgsql_db_changed():
233+ if 'pgsql-db' not in CONFIGS.complete_contexts():
234+ log('pgsql-db relation incomplete. Peer not ready?')
235+ return
236+ CONFIGS.write(HEAT_CONF)
237+ check_call(['heat-manage', 'db_sync'])
238+
239+
240 @hooks.hook('identity-service-relation-joined')
241 def identity_joined(rid=None):
242 base_url = canonical_url(CONFIGS)
243@@ -149,7 +180,8 @@
244
245 @hooks.hook('amqp-relation-broken',
246 'identity-service-relation-broken',
247- 'shared-db-relation-broken')
248+ 'shared-db-relation-broken',
249+ 'pgsql-db-relation-broken')
250 def relation_broken():
251 CONFIGS.write_all()
252
253
254=== added file 'hooks/heat_relations.pyc'
255Binary files hooks/heat_relations.pyc 1970-01-01 00:00:00 +0000 and hooks/heat_relations.pyc 2014-03-26 09:39:55 +0000 differ
256=== modified file 'hooks/heat_utils.py'
257--- hooks/heat_utils.py 2014-02-14 13:58:56 +0000
258+++ hooks/heat_utils.py 2014-03-26 09:39:55 +0000
259@@ -30,6 +30,7 @@
260
261 BASE_PACKAGES = [
262 'python-keystoneclient',
263+ 'python-psycopg2',
264 'uuid',
265 ]
266
267@@ -52,6 +53,7 @@
268 'services': BASE_SERVICES,
269 'contexts': [context.AMQPContext(),
270 context.SharedDBContext(relation_prefix='heat'),
271+ context.PostgresqlDBContext(),
272 context.OSConfigFlagContext(),
273 heat_context.HeatIdentityServiceContext(),
274 heat_context.EncryptionContext(),
275
276=== added file 'hooks/heat_utils.pyc'
277Binary files hooks/heat_utils.pyc 1970-01-01 00:00:00 +0000 and hooks/heat_utils.pyc 2014-03-26 09:39:55 +0000 differ
278=== added symlink 'hooks/pgsql-db-relation-broken'
279=== target is u'heat_relations.py'
280=== added symlink 'hooks/pgsql-db-relation-changed'
281=== target is u'heat_relations.py'
282=== added symlink 'hooks/pgsql-db-relation-joined'
283=== target is u'heat_relations.py'
284=== modified file 'metadata.yaml'
285--- metadata.yaml 2013-11-29 15:36:45 +0000
286+++ metadata.yaml 2014-03-26 09:39:55 +0000
287@@ -13,6 +13,8 @@
288 requires:
289 shared-db:
290 interface: mysql-shared
291+ pgsql-db:
292+ interface: pgsql
293 amqp:
294 interface: rabbitmq
295 identity-service:
296
297=== modified file 'revision'
298--- revision 2014-02-03 13:05:03 +0000
299+++ revision 2014-03-26 09:39:55 +0000
300@@ -1,1 +1,1 @@
301-12
302+13
303
304=== modified file 'templates/heat.conf'
305--- templates/heat.conf 2014-02-03 13:05:03 +0000
306+++ templates/heat.conf 2014-03-26 09:39:55 +0000
307@@ -37,7 +37,7 @@
308 {% endif -%}
309
310 {% if database_host -%}
311-sql_connection = mysql://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}
312+sql_connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}
313 {% endif -%}
314
315 verbose = True
316@@ -60,7 +60,7 @@
317
318 # sql
319 {% if database_host -%}
320-connection = mysql://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}
321+connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}
322 {% endif -%}
323
324 [paste_deploy]
325
326=== modified file 'unit_tests/test_heat_relations.py'
327--- unit_tests/test_heat_relations.py 2013-12-11 11:21:44 +0000
328+++ unit_tests/test_heat_relations.py 2014-03-26 09:39:55 +0000
329@@ -20,6 +20,7 @@
330 'Hooks',
331 'canonical_url',
332 'config',
333+ 'is_relation_made',
334 'open_port',
335 'relation_set',
336 'unit_get',
337@@ -77,18 +78,49 @@
338
339 def test_db_joined(self):
340 self.unit_get.return_value = 'heat.foohost.com'
341+ self.is_relation_made.return_value = False
342 relations.db_joined()
343 self.relation_set.assert_called_with(heat_database='heat',
344 heat_username='heat',
345 heat_hostname='heat.foohost.com')
346 self.unit_get.assert_called_with('private-address')
347
348+ def test_postgresql_db_joined(self):
349+ self.unit_get.return_value = 'heat.foohost.com'
350+ self.is_relation_made.return_value = False
351+ relations.pgsql_db_joined()
352+ self.relation_set.assert_called_with(database='heat')
353+
354+ def test_db_joined_with_postgres(self):
355+ self.unit_get.return_value = 'heat.foohost.com'
356+ self.is_relation_made.return_value = True
357+ with self.assertRaises(Exception) as context:
358+ relations.db_joined()
359+ self.assertEqual(context.exception.message,
360+ 'Attempting to associate a mysql database when there '
361+ 'is already associated a postgresql one')
362+
363+ def test_postgresql_joined_with_db(self):
364+ self.unit_get.return_value = 'heat.foohost.com'
365+ self.is_relation_made.return_value = True
366+ with self.assertRaises(Exception) as context:
367+ relations.pgsql_db_joined()
368+ self.assertEqual(context.exception.message,
369+ 'Attempting to associate a postgresql database when there '
370+ 'is already associated a mysql one')
371+
372 def _shared_db_test(self, configs):
373 configs.complete_contexts = MagicMock()
374 configs.complete_contexts.return_value = ['shared-db']
375 configs.write = MagicMock()
376 relations.db_changed()
377
378+ def _postgresql_db_test(self, configs):
379+ configs.complete_contexts = MagicMock()
380+ configs.complete_contexts.return_value = ['pgsql-db']
381+ configs.write = MagicMock()
382+ relations.pgsql_db_changed()
383+
384 @patch.object(relations, 'CONFIGS')
385 def test_db_changed(self, configs):
386 self._shared_db_test(configs)
387@@ -96,6 +128,12 @@
388 configs.write.call_args_list)
389
390 @patch.object(relations, 'CONFIGS')
391+ def test_pgsql_db_changed(self, configs):
392+ self._postgresql_db_test(configs)
393+ self.assertEquals([call('/etc/heat/heat.conf')],
394+ configs.write.call_args_list)
395+
396+ @patch.object(relations, 'CONFIGS')
397 def test_db_changed_missing_relation_data(self, configs):
398 configs.complete_contexts = MagicMock()
399 configs.complete_contexts.return_value = []
400@@ -104,6 +142,15 @@
401 'shared-db relation incomplete. Peer not ready?'
402 )
403
404+ @patch.object(relations, 'CONFIGS')
405+ def test_pgsql_db_changed_missing_relation_data(self, configs):
406+ configs.complete_contexts = MagicMock()
407+ configs.complete_contexts.return_value = []
408+ relations.pgsql_db_changed()
409+ self.log.assert_called_with(
410+ 'pgsql-db relation incomplete. Peer not ready?'
411+ )
412+
413 def test_amqp_joined(self):
414 relations.amqp_joined()
415 self.relation_set.assert_called_with(

Subscribers

People subscribed via source and target branches