Merge lp:~cbjchen/charms/trusty/heat/next-ha into lp:~openstack-charmers-archive/charms/trusty/heat/next

Proposed by Liang Chen
Status: Superseded
Proposed branch: lp:~cbjchen/charms/trusty/heat/next-ha
Merge into: lp:~openstack-charmers-archive/charms/trusty/heat/next
Diff against target: 401 lines (+230/-17)
6 files modified
config.yaml (+34/-0)
hooks/heat_relations.py (+128/-4)
hooks/heat_utils.py (+3/-2)
metadata.yaml (+6/-0)
tests/basic_deployment.py (+6/-6)
unit_tests/test_heat_relations.py (+53/-5)
To merge this branch: bzr merge lp:~cbjchen/charms/trusty/heat/next-ha
Reviewer Review Type Date Requested Status
Liam Young (community) Needs Fixing
Review via email: mp+280946@code.launchpad.net

This proposal has been superseded by a proposal from 2016-02-15.

Description of the change

Provide Heat charm HA support

To post a comment you must log in.
Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_lint_check #15610 heat-next for cbjchen mp280946
    LINT FAIL: lint-test failed

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

Full lint test output: http://paste.ubuntu.com/14086903/
Build: http://10.245.162.77:8080/job/charm_lint_check/15610/

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

charm_unit_test #14567 heat-next for cbjchen mp280946
    UNIT FAIL: unit-test failed

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

Full unit test output: http://paste.ubuntu.com/14086905/
Build: http://10.245.162.77:8080/job/charm_unit_test/14567/

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

charm_amulet_test #8330 heat-next for cbjchen mp280946
    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/14086964/
Build: http://10.245.162.77:8080/job/charm_amulet_test/8330/

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

charm_lint_check #16509 heat-next for cbjchen mp280946
    LINT OK: passed

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

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

charm_unit_test #15415 heat-next for cbjchen mp280946
    UNIT FAIL: unit-test failed

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

Full unit test output: pastebin not avail., cmd error
Build: http://10.245.162.77:8080/job/charm_unit_test/15415/

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

charm_amulet_test #8495 heat-next for cbjchen mp280946
    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/14399097/
Build: http://10.245.162.77:8080/job/charm_amulet_test/8495/

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

charm_unit_test #15481 heat-next for cbjchen mp280946
    UNIT FAIL: unit-test failed

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

Full unit test output: http://paste.ubuntu.com/14407592/
Build: http://10.245.162.77:8080/job/charm_unit_test/15481/

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

charm_lint_check #16585 heat-next for cbjchen mp280946
    LINT OK: passed

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

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

charm_amulet_test #8502 heat-next for cbjchen mp280946
    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/14407692/
Build: http://10.245.162.77:8080/job/charm_amulet_test/8502/

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

amulet tests and unit tests need some tweaks. Otherwise looks good to me, thanks for mp!

review: Needs Fixing
Revision history for this message
Liang Chen (cbjchen) wrote :

rebased onto the latest heat/next branch

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

charm_lint_check #16835 heat-next for cbjchen mp280946
    LINT FAIL: lint-test failed

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

Full lint test output: http://paste.ubuntu.com/14438179/
Build: http://10.245.162.77:8080/job/charm_lint_check/16835/

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

charm_unit_test #15723 heat-next for cbjchen mp280946
    UNIT OK: passed

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

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

charm_unit_test #15842 heat-next for cbjchen mp280946
    UNIT FAIL: unit-test failed

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

Full unit test output: http://paste.ubuntu.com/14445600/
Build: http://10.245.162.77:8080/job/charm_unit_test/15842/

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

charm_lint_check #16962 heat-next for cbjchen mp280946
    LINT OK: passed

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

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

charm_amulet_test #8612 heat-next for cbjchen mp280946
    AMULET OK: passed

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

Revision history for this message
Liang Chen (cbjchen) wrote :

Hi Liam,
Patch rebased onto the latest heat/next branch.
amulet tests and unit tests are fixed.

Thanks,
Liang

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

charm_lint_check #16963 heat-next for cbjchen mp280946
    LINT OK: passed

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

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

charm_unit_test #15843 heat-next for cbjchen mp280946
    UNIT OK: passed

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

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

charm_amulet_test #8613 heat-next for cbjchen mp280946
    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/14447806/
Build: http://10.245.162.77:8080/job/charm_amulet_test/8613/

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

charm_amulet_test #8617 heat-next for cbjchen mp280946
    AMULET OK: passed

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

66. By Liam Young

Update test combo definitions, remove Vivid deprecated release tests, update bundletester testplan yaml, update tests README.

67. By James Page

Fix liberty/mitaka typo from previous test definition update batch.

68. By James Page

Fix support for liberty and kilo openstack releases.

69. By James Page

Tidy tox targets

70. By Edward Hope-Morley

[hopem,r=jamespage]

Sync charmhelpers to get support for supplying a Ascii Armor PGP key to
openstack-origin (currently only supports Radix64)
Closes-Bug: 1518975

71. By Liang Chen

Provide heat charm HA support

[cbjchen,r=]

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'config.yaml'
2--- config.yaml 2015-12-02 15:50:33 +0000
3+++ config.yaml 2016-01-09 14:43:24 +0000
4@@ -122,3 +122,37 @@
5 order for this charm to function correctly, the privacy extension must be
6 disabled and a non-temporary address must be configured/available on
7 your network interface.
8+ # HA configuration settings
9+ vip:
10+ type: string
11+ default:
12+ description: |
13+ Virtual IP(s) to use to front API services in HA configuration.
14+ .
15+ If multiple networks are being used, a VIP should be provided for each
16+ network, separated by spaces.
17+ vip_iface:
18+ type: string
19+ default: eth0
20+ description: |
21+ Default network interface to use for HA vip when it cannot be automatically
22+ determined.
23+ vip_cidr:
24+ type: int
25+ default: 24
26+ description: |
27+ Default CIDR netmask to use for HA vip when it cannot be automatically
28+ determined.
29+ ha-bindiface:
30+ type: string
31+ default: eth0
32+ description: |
33+ Default network interface on which HA cluster will bind to communication
34+ with the other members of the HA Cluster.
35+ ha-mcastport:
36+ type: int
37+ default: 5959
38+ description: |
39+ Default multicast port number that will be used to communicate between
40+ HA Cluster nodes.
41+
42
43=== added symlink 'hooks/cluster-relation-changed'
44=== target is u'heat_relations.py'
45=== added symlink 'hooks/cluster-relation-departed'
46=== target is u'heat_relations.py'
47=== added symlink 'hooks/cluster-relation-joined'
48=== target is u'heat_relations.py'
49=== added symlink 'hooks/ha-relation-changed'
50=== target is u'heat_relations.py'
51=== added symlink 'hooks/ha-relation-joined'
52=== target is u'heat_relations.py'
53=== modified file 'hooks/heat_relations.py'
54--- hooks/heat_relations.py 2015-10-30 11:22:56 +0000
55+++ hooks/heat_relations.py 2016-01-09 14:43:24 +0000
56@@ -19,7 +19,9 @@
57 charm_dir,
58 log,
59 relation_ids,
60+ relation_get,
61 relation_set,
62+ local_unit,
63 open_port,
64 unit_get,
65 status_set,
66@@ -35,6 +37,19 @@
67 apt_update
68 )
69
70+from charmhelpers.contrib.hahelpers.cluster import (
71+ is_elected_leader,
72+ get_hacluster_config,
73+)
74+
75+from charmhelpers.contrib.network.ip import (
76+ get_iface_for_address,
77+ get_netmask_for_address,
78+ get_address_in_network,
79+ get_ipv6_addr,
80+ is_ipv6
81+)
82+
83 from charmhelpers.contrib.openstack.utils import (
84 configure_installation_source,
85 openstack_upgrade_available,
86@@ -55,6 +70,7 @@
87 determine_packages,
88 migrate_database,
89 register_configs,
90+ CLUSTER_RES,
91 HEAT_CONF,
92 REQUIRED_INTERFACES,
93 setup_ipv6,
94@@ -64,6 +80,7 @@
95 API_PORTS,
96 )
97
98+from charmhelpers.contrib.openstack.context import ADDRESS_TYPES
99 from charmhelpers.payload.execd import execd_preinstall
100
101 hooks = Hooks()
102@@ -108,6 +125,11 @@
103 CONFIGS.write_all()
104 configure_https()
105
106+ for rid in relation_ids('cluster'):
107+ cluster_joined(relation_id=rid)
108+ for r_id in relation_ids('ha'):
109+ ha_joined(relation_id=r_id)
110+
111
112 @hooks.hook('amqp-relation-joined')
113 def amqp_joined(relation_id=None):
114@@ -131,9 +153,9 @@
115 config('database-user'),
116 relation_prefix='heat')
117 else:
118- relation_set(heat_database=config('database'),
119- heat_username=config('database-user'),
120- heat_hostname=unit_get('private-address'))
121+ relation_set(database=config('database'),
122+ username=config('database-user'),
123+ hostname=unit_get('private-address'))
124
125
126 @hooks.hook('shared-db-relation-changed')
127@@ -143,7 +165,15 @@
128 log('shared-db relation incomplete. Peer not ready?')
129 return
130 CONFIGS.write(HEAT_CONF)
131- migrate_database()
132+
133+ if is_elected_leader(CLUSTER_RES):
134+ allowed_units = relation_get('allowed_units')
135+ if allowed_units and local_unit() in allowed_units.split():
136+ log('Cluster leader, performing db sync')
137+ migrate_database()
138+ else:
139+ log('allowed_units either not presented, or local unit '
140+ 'not in acl list: %s' % repr(allowed_units))
141
142
143 def configure_https():
144@@ -216,6 +246,100 @@
145 CONFIGS.write_all()
146
147
148+@hooks.hook('cluster-relation-joined')
149+def cluster_joined(relation_id=None):
150+ for addr_type in ADDRESS_TYPES:
151+ address = get_address_in_network(
152+ config('os-{}-network'.format(addr_type))
153+ )
154+ if address:
155+ relation_set(
156+ relation_id=relation_id,
157+ relation_settings={'{}-address'.format(addr_type): address}
158+ )
159+
160+ if config('prefer-ipv6'):
161+ private_addr = get_ipv6_addr(exc_list=[config('vip')])[0]
162+ relation_set(relation_id=relation_id,
163+ relation_settings={'private-address': private_addr})
164+
165+
166+@hooks.hook('cluster-relation-changed',
167+ 'cluster-relation-departed')
168+@restart_on_change(restart_map(), stopstart=True)
169+def cluster_changed():
170+ CONFIGS.write_all()
171+
172+
173+@hooks.hook('ha-relation-joined')
174+def ha_joined(relation_id=None):
175+ cluster_config = get_hacluster_config()
176+
177+ resources = {
178+ 'res_heat_haproxy': 'lsb:haproxy'
179+ }
180+
181+ resource_params = {
182+ 'res_heat_haproxy': 'op monitor interval="5s"'
183+ }
184+
185+ vip_group = []
186+ for vip in cluster_config['vip'].split():
187+ if is_ipv6(vip):
188+ res_heat_vip = 'ocf:heartbeat:IPv6addr'
189+ vip_params = 'ipv6addr'
190+ else:
191+ res_heat_vip = 'ocf:heartbeat:IPaddr2'
192+ vip_params = 'ip'
193+
194+ iface = (get_iface_for_address(vip) or
195+ config('vip_iface'))
196+ netmask = (get_netmask_for_address(vip) or
197+ config('vip_cidr'))
198+
199+ if iface is not None:
200+ vip_key = 'res_heat_{}_vip'.format(iface)
201+ resources[vip_key] = res_heat_vip
202+ resource_params[vip_key] = (
203+ 'params {ip}="{vip}" cidr_netmask="{netmask}"'
204+ ' nic="{iface}"'.format(ip=vip_params,
205+ vip=vip,
206+ iface=iface,
207+ netmask=netmask)
208+ )
209+ vip_group.append(vip_key)
210+
211+ if len(vip_group) >= 1:
212+ relation_set(relation_id=relation_id,
213+ groups={'grp_heat_vips': ' '.join(vip_group)})
214+
215+ init_services = {
216+ 'res_heat_haproxy': 'haproxy'
217+ }
218+ clones = {
219+ 'cl_heat_haproxy': 'res_heat_haproxy'
220+ }
221+ relation_set(relation_id=relation_id,
222+ init_services=init_services,
223+ corosync_bindiface=cluster_config['ha-bindiface'],
224+ corosync_mcastport=cluster_config['ha-mcastport'],
225+ resources=resources,
226+ resource_params=resource_params,
227+ clones=clones)
228+
229+
230+@hooks.hook('ha-relation-changed')
231+def ha_changed():
232+ clustered = relation_get('clustered')
233+ if not clustered or clustered in [None, 'None', '']:
234+ log('ha_changed: hacluster subordinate not fully clustered.')
235+ else:
236+ log('Cluster configured, notifying other services and updating '
237+ 'keystone endpoint configuration')
238+ for rid in relation_ids('identity-service'):
239+ identity_joined(rid=rid)
240+
241+
242 def main():
243 try:
244 hooks.execute(sys.argv)
245
246=== modified file 'hooks/heat_utils.py'
247--- hooks/heat_utils.py 2015-12-02 15:50:33 +0000
248+++ hooks/heat_utils.py 2016-01-09 14:43:24 +0000
249@@ -68,6 +68,8 @@
250 'heat-engine'
251 ]
252
253+# Cluster resource used to determine leadership when hacluster'd
254+CLUSTER_RES = 'grp_heat_vips'
255 SVC = 'heat'
256 HEAT_DIR = '/etc/heat'
257 HEAT_CONF = '/etc/heat/heat.conf'
258@@ -81,8 +83,7 @@
259 (HEAT_CONF, {
260 'services': BASE_SERVICES,
261 'contexts': [context.AMQPContext(ssl_dir=HEAT_DIR),
262- context.SharedDBContext(relation_prefix='heat',
263- ssl_dir=HEAT_DIR),
264+ context.SharedDBContext(ssl_dir=HEAT_DIR),
265 context.OSConfigFlagContext(),
266 HeatIdentityServiceContext(service=SVC, service_user=SVC),
267 HeatHAProxyContext(),
268
269=== modified file 'metadata.yaml'
270--- metadata.yaml 2015-11-18 10:35:35 +0000
271+++ metadata.yaml 2016-01-09 14:43:24 +0000
272@@ -14,3 +14,9 @@
273 interface: rabbitmq
274 identity-service:
275 interface: keystone
276+ ha:
277+ interface: hacluster
278+ scope: container
279+peers:
280+ cluster:
281+ interface: heat-ha
282
283=== modified file 'tests/basic_deployment.py'
284--- tests/basic_deployment.py 2015-06-12 13:50:03 +0000
285+++ tests/basic_deployment.py 2016-01-09 14:43:24 +0000
286@@ -342,9 +342,9 @@
287 relation = ['shared-db', 'mysql:shared-db']
288 expected = {
289 'private-address': u.valid_ip,
290- 'heat_database': 'heat',
291- 'heat_username': 'heat',
292- 'heat_hostname': u.valid_ip
293+ 'database': 'heat',
294+ 'username': 'heat',
295+ 'hostname': u.valid_ip
296 }
297
298 ret = u.validate_relation_data(unit, relation, expected)
299@@ -360,8 +360,8 @@
300 expected = {
301 'private-address': u.valid_ip,
302 'db_host': u.valid_ip,
303- 'heat_allowed_units': 'heat/0',
304- 'heat_password': u.not_null
305+ 'allowed_units': 'heat/0',
306+ 'password': u.not_null
307 }
308
309 ret = u.validate_relation_data(unit, relation, expected)
310@@ -465,7 +465,7 @@
311 u.log.debug('mysql:heat relation: {}'.format(mysql_rel))
312
313 db_uri = "mysql://{}:{}@{}/{}".format('heat',
314- mysql_rel['heat_password'],
315+ mysql_rel['password'],
316 mysql_rel['db_host'],
317 'heat')
318
319
320=== modified file 'unit_tests/test_heat_relations.py'
321--- unit_tests/test_heat_relations.py 2015-11-16 09:14:33 +0000
322+++ unit_tests/test_heat_relations.py 2016-01-09 14:43:24 +0000
323@@ -41,6 +41,13 @@
324 'execd_preinstall',
325 'log',
326 'migrate_database',
327+ 'is_elected_leader',
328+ 'relation_ids',
329+ 'relation_get',
330+ 'local_unit',
331+ 'get_hacluster_config',
332+ 'get_iface_for_address',
333+ 'get_netmask_for_address',
334 ]
335
336
337@@ -91,12 +98,14 @@
338 def test_db_joined(self):
339 self.unit_get.return_value = 'heat.foohost.com'
340 relations.db_joined()
341- self.relation_set.assert_called_with(heat_database='heat',
342- heat_username='heat',
343- heat_hostname='heat.foohost.com')
344+ self.relation_set.assert_called_with(database='heat',
345+ username='heat',
346+ hostname='heat.foohost.com')
347 self.unit_get.assert_called_with('private-address')
348
349 def _shared_db_test(self, configs):
350+ self.relation_get.return_value = 'heat/0 heat/1'
351+ self.local_unit.return_value = 'heat/0'
352 configs.complete_contexts = MagicMock()
353 configs.complete_contexts.return_value = ['shared-db']
354 configs.write = MagicMock()
355@@ -240,5 +249,44 @@
356 self.sync_db_with_multi_ipv6_addresses.return_value = MagicMock()
357 self.test_config.set('prefer-ipv6', True)
358 relations.db_joined()
359- self.sync_db_with_multi_ipv6_addresses.assert_called_with_once(
360- 'heat', 'heat')
361+ self.sync_db_with_multi_ipv6_addresses.assert_called_with(
362+ 'heat', 'heat', relation_prefix='heat')
363+
364+ @patch.object(relations, 'CONFIGS')
365+ def test_non_leader_db_changed(self, configs):
366+ self.is_elected_leader.return_value = False
367+ configs.complete_contexts.return_value = []
368+ self.relation_get.return_value = 'heat/0 heat/1'
369+ self.local_unit.return_value = 'heat/0'
370+ configs.complete_contexts = MagicMock()
371+ configs.complete_contexts.return_value = ['shared-db']
372+ configs.write = MagicMock()
373+ relations.db_changed()
374+ self.assertFalse(self.migrate_database.called)
375+
376+ @patch.object(relations, 'CONFIGS')
377+ def test_ha_joined(self, configs):
378+ self.get_hacluster_config.return_value = {
379+ 'ha-bindiface': 'eth0',
380+ 'ha-mcastport': '5959',
381+ 'vip': '10.5.105.3'
382+ }
383+ self.get_iface_for_address.return_value = 'eth0'
384+ self.get_netmask_for_address.return_value = '255.255.255.0'
385+ relations.ha_joined()
386+ expected = {
387+ 'relation_id': None,
388+ 'init_services': {'res_heat_haproxy': 'haproxy'},
389+ 'corosync_bindiface': 'eth0',
390+ 'corosync_mcastport': '5959',
391+ 'resources': {
392+ 'res_heat_haproxy': 'lsb:haproxy',
393+ 'res_heat_eth0_vip': 'ocf:heartbeat:IPaddr2'},
394+ 'resource_params': {
395+ 'res_heat_haproxy': 'op monitor interval="5s"',
396+ 'res_heat_eth0_vip': ('params ip="10.5.105.3" '
397+ 'cidr_netmask="255.255.255.0" '
398+ 'nic="eth0"')},
399+ 'clones': {'cl_heat_haproxy': 'res_heat_haproxy'}
400+ }
401+ self.relation_set.assert_called_with(**expected)

Subscribers

People subscribed via source and target branches