Merge lp:~xianghui/charm-helpers/support-ipv6 into lp:charm-helpers

Proposed by Xiang Hui
Status: Merged
Merged at revision: 193
Proposed branch: lp:~xianghui/charm-helpers/support-ipv6
Merge into: lp:charm-helpers
Diff against target: 278 lines (+150/-14)
6 files modified
charmhelpers/contrib/network/ip.py (+19/-1)
charmhelpers/contrib/openstack/context.py (+20/-4)
charmhelpers/contrib/openstack/ip.py (+7/-3)
charmhelpers/contrib/openstack/templates/haproxy.cfg (+3/-3)
tests/contrib/network/test_ip.py (+47/-1)
tests/contrib/openstack/test_os_contexts.py (+54/-2)
To merge this branch: bzr merge lp:~xianghui/charm-helpers/support-ipv6
Reviewer Review Type Date Requested Status
Edward Hope-Morley Approve
Review via email: mp+228637@code.launchpad.net

Description of the change

[Hui Xiang]
Allow rabbitmq-server client to get its ipv6 address.
  (rabbitmq-server charm already set "hostname" with relation-set().)
Allow ceph-client to get ipv6 address from 'host-ip'.
  (will let ceph to set "host-ip" with relation-set for clients)
Add get_ipv6_addr() funtion for ipv6.

To post a comment you must log in.
Revision history for this message
Edward Hope-Morley (hopem) wrote :

Need to fx merge conflict.

review: Needs Fixing
169. By Xiang Hui

Fix conflict with trunk.

Revision history for this message
Edward Hope-Morley (hopem) wrote :

Hui, I'm getting some lint errors:

$ make lint
Checking for Python syntax...
charmhelpers/contrib/hahelpers/cluster.py:183:25: F821 undefined name 'get_ipv6_addr'
charmhelpers/cli/commands.py:1:1: F401 'CommandLine' imported but unused
charmhelpers/cli/commands.py:2:1: F401 'host' imported but unused
tests/contrib/network/test_ip.py:161:78: W291 trailing whitespace
make: *** [lint] Error 1

Also, could you please update/add unit tests to cover the change you are making.

review: Needs Fixing
Revision history for this message
Edward Hope-Morley (hopem) wrote :

Currently getting the following unit test failures as well: https://pastebin.canonical.com/114395/

Revision history for this message
Xiang Hui (xianghui) wrote :

> Hui, I'm getting some lint errors:
>
> $ make lint
> Checking for Python syntax...
> charmhelpers/contrib/hahelpers/cluster.py:183:25: F821 undefined name
> 'get_ipv6_addr'
> charmhelpers/cli/commands.py:1:1: F401 'CommandLine' imported but unused
> charmhelpers/cli/commands.py:2:1: F401 'host' imported but unused
> tests/contrib/network/test_ip.py:161:78: W291 trailing whitespace
> make: *** [lint] Error 1
>
> Also, could you please update/add unit tests to cover the change you are
> making.

I don't know why the conflict deleted new added function "get_ipv6_addr()", will added on next patch, and except this error, other errors are not caused by my changes, if these strict lint rules applied to everyone?thanks.

Revision history for this message
Xiang Hui (xianghui) wrote :

> Currently getting the following unit test failures as well:
> https://pastebin.canonical.com/114395/

Just the first two errors "get_ipv6_add.." caused by my changes due to the conflict mis-deleted.

Revision history for this message
Edward Hope-Morley (hopem) wrote :

If I remove all your changes I get no errors i.e. trunk has no errors.

170. By Xiang Hui

Add ut and solved ut errors and conflicts.

Revision history for this message
Xiang Hui (xianghui) wrote :

> If I remove all your changes I get no errors i.e. trunk has no errors.
Done, thanks for your efforts.

Revision history for this message
James Page (james-page) :
Revision history for this message
Edward Hope-Morley (hopem) :
171. By Xiang Hui

MV get_ipv6_addr() to network/ip.py

172. By Xiang Hui

Merge from dev branch and fix unit test.

173. By Xiang Hui

Remove relation set 'hostname' on AMQP.

174. By Xiang Hui

If prefer ipv6 addr only, fallback to ipv6.

175. By Xiang Hui

Support haproxy for ipv6.

Revision history for this message
Edward Hope-Morley (hopem) wrote :

Couple of issues that need fixing.

review: Needs Fixing
176. By Xiang Hui

Fix trivial issue.

Revision history for this message
Xiang Hui (xianghui) wrote :

> Couple of issues that need fixing.
Thanks, done.

Revision history for this message
Edward Hope-Morley (hopem) wrote :

LGTM +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charmhelpers/contrib/network/ip.py'
2--- charmhelpers/contrib/network/ip.py 2014-07-24 08:44:12 +0000
3+++ charmhelpers/contrib/network/ip.py 2014-08-06 03:04:25 +0000
4@@ -4,7 +4,7 @@
5
6 from charmhelpers.fetch import apt_install
7 from charmhelpers.core.hookenv import (
8- ERROR, log,
9+ ERROR, log, config,
10 )
11
12 try:
13@@ -154,3 +154,21 @@
14 get_iface_for_address = partial(_get_for_address, key='iface')
15
16 get_netmask_for_address = partial(_get_for_address, key='netmask')
17+
18+
19+def get_ipv6_addr(iface="eth0"):
20+ try:
21+ iface_addrs = netifaces.ifaddresses(iface)
22+ if netifaces.AF_INET6 not in iface_addrs:
23+ raise Exception("Interface '%s' doesn't have an ipv6 address." % iface)
24+
25+ addresses = netifaces.ifaddresses(iface)[netifaces.AF_INET6]
26+ ipv6_addr = [a['addr'] for a in addresses if not a['addr'].startswith('fe80')
27+ and config('vip') != a['addr']]
28+ if not ipv6_addr:
29+ raise Exception("Interface '%s' doesn't have global ipv6 address." % iface)
30+
31+ return ipv6_addr[0]
32+
33+ except ValueError:
34+ raise ValueError("Invalid interface '%s'" % iface)
35
36=== modified file 'charmhelpers/contrib/openstack/context.py'
37--- charmhelpers/contrib/openstack/context.py 2014-07-28 11:28:49 +0000
38+++ charmhelpers/contrib/openstack/context.py 2014-08-06 03:04:25 +0000
39@@ -44,7 +44,10 @@
40 neutron_plugin_attribute,
41 )
42
43-from charmhelpers.contrib.network.ip import get_address_in_network
44+from charmhelpers.contrib.network.ip import (
45+ get_address_in_network,
46+ get_ipv6_addr,
47+)
48
49 CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt'
50
51@@ -401,9 +404,12 @@
52
53 cluster_hosts = {}
54 l_unit = local_unit().replace('/', '-')
55- cluster_hosts[l_unit] = \
56- get_address_in_network(config('os-internal-network'),
57- unit_get('private-address'))
58+ if config('prefer-ipv6'):
59+ addr = get_ipv6_addr()
60+ else:
61+ addr = unit_get('private-address')
62+ cluster_hosts[l_unit] = get_address_in_network(config('os-internal-network'),
63+ addr)
64
65 for rid in relation_ids('cluster'):
66 for unit in related_units(rid):
67@@ -414,6 +420,16 @@
68 ctxt = {
69 'units': cluster_hosts,
70 }
71+
72+ if config('prefer-ipv6'):
73+ ctxt['local_host'] = 'ip6-localhost'
74+ ctxt['haproxy_host'] = '::'
75+ ctxt['stat_port'] = ':::8888'
76+ else:
77+ ctxt['local_host'] = '127.0.0.1'
78+ ctxt['haproxy_host'] = '0.0.0.0'
79+ ctxt['stat_port'] = ':8888'
80+
81 if len(cluster_hosts.keys()) > 1:
82 # Enable haproxy when we have enough peers.
83 log('Ensuring haproxy enabled in /etc/default/haproxy.')
84
85=== modified file 'charmhelpers/contrib/openstack/ip.py'
86--- charmhelpers/contrib/openstack/ip.py 2014-07-03 15:15:30 +0000
87+++ charmhelpers/contrib/openstack/ip.py 2014-08-06 03:04:25 +0000
88@@ -7,6 +7,7 @@
89 get_address_in_network,
90 is_address_in_network,
91 is_ipv6,
92+ get_ipv6_addr,
93 )
94
95 from charmhelpers.contrib.hahelpers.cluster import is_clustered
96@@ -64,10 +65,13 @@
97 vip):
98 resolved_address = vip
99 else:
100+ if config('prefer-ipv6'):
101+ fallback_addr = get_ipv6_addr()
102+ else:
103+ fallback_addr = unit_get(_address_map[endpoint_type]['fallback'])
104 resolved_address = get_address_in_network(
105- config(_address_map[endpoint_type]['config']),
106- unit_get(_address_map[endpoint_type]['fallback'])
107- )
108+ config(_address_map[endpoint_type]['config']), fallback_addr)
109+
110 if resolved_address is None:
111 raise ValueError('Unable to resolve a suitable IP address'
112 ' based on charm state and configuration')
113
114=== modified file 'charmhelpers/contrib/openstack/templates/haproxy.cfg'
115--- charmhelpers/contrib/openstack/templates/haproxy.cfg 2014-07-03 12:50:03 +0000
116+++ charmhelpers/contrib/openstack/templates/haproxy.cfg 2014-08-06 03:04:25 +0000
117@@ -1,6 +1,6 @@
118 global
119- log 127.0.0.1 local0
120- log 127.0.0.1 local1 notice
121+ log {{ local_host }} local0
122+ log {{ local_host }} local1 notice
123 maxconn 20000
124 user haproxy
125 group haproxy
126@@ -17,7 +17,7 @@
127 timeout client 30000
128 timeout server 30000
129
130-listen stats :8888
131+listen stats {{ stat_port }}
132 mode http
133 stats enable
134 stats hide-version
135
136=== modified file 'tests/contrib/network/test_ip.py'
137--- tests/contrib/network/test_ip.py 2014-07-24 08:29:56 +0000
138+++ tests/contrib/network/test_ip.py 2014-08-06 03:04:25 +0000
139@@ -158,4 +158,50 @@
140 def test_is_ipv6(self):
141 self.assertFalse(net_ip.is_ipv6('myhost'))
142 self.assertFalse(net_ip.is_ipv6('172.4.5.5'))
143- self.assertTrue(net_ip.is_ipv6('2a01:348:2f4:0:685e:5748:ae62:209f'))
144\ No newline at end of file
145+ self.assertTrue(net_ip.is_ipv6('2a01:348:2f4:0:685e:5748:ae62:209f'))
146+
147+ @patch.object(netifaces, 'ifaddresses')
148+ def test_get_ipv6_addr_no_ipv6(self, _ifaddresses):
149+ DUMMY_ADDRESSES = {
150+ 'eth0': {
151+ 2: [{'addr': '192.168.1.55',
152+ 'broadcast': '192.168.1.255',
153+ 'netmask': '255.255.255.0'}]
154+ }
155+ }
156+
157+ def mock_ifaddresses(iface):
158+ return DUMMY_ADDRESSES[iface]
159+
160+ _ifaddresses.side_effect = mock_ifaddresses
161+ self.assertRaises(Exception, net_ip.get_ipv6_addr)
162+
163+ @patch.object(netifaces, 'ifaddresses')
164+ def test_get_ipv6_addr_no_global_ipv6(self, _ifaddresses):
165+ DUMMY_ADDRESSES = {
166+ 'eth0': {
167+ 10: [{'addr': 'fe80::3e97:eff:fe8b:1cf7%eth0',
168+ 'netmask': 'ffff:ffff:ffff:ffff::'}],
169+ }
170+ }
171+
172+ def mock_ifaddresses(iface):
173+ return DUMMY_ADDRESSES[iface]
174+
175+ _ifaddresses.side_effect = mock_ifaddresses
176+ self.assertRaises(Exception, net_ip.get_ipv6_addr)
177+
178+ @patch.object(netifaces, 'ifaddresses')
179+ def test_get_ipv6_addr_invalid_nic(self, _ifaddresses):
180+ DUMMY_ADDRESSES = {
181+ 'eth0': {
182+ 10: [{'addr': 'fe80::3e97:eff:fe8b:1cf7%eth0',
183+ 'netmask': 'ffff:ffff:ffff:ffff::'}],
184+ }
185+ }
186+
187+ def mock_ifaddresses(iface):
188+ return DUMMY_ADDRESSES[iface]
189+
190+ _ifaddresses.side_effect = ValueError()
191+ self.assertRaises(ValueError, net_ip.get_ipv6_addr, 'eth1')
192
193=== modified file 'tests/contrib/openstack/test_os_contexts.py'
194--- tests/contrib/openstack/test_os_contexts.py 2014-07-28 11:28:49 +0000
195+++ tests/contrib/openstack/test_os_contexts.py 2014-08-06 03:04:25 +0000
196@@ -315,7 +315,8 @@
197 'time',
198 'https',
199 'get_address_in_network',
200- 'local_unit'
201+ 'local_unit',
202+ 'get_ipv6_addr'
203 ]
204
205
206@@ -835,6 +836,52 @@
207 self.relation_get.side_effect = relation.get
208 self.related_units.side_effect = relation.relation_units
209 self.get_address_in_network.return_value = 'cluster-peer0.localnet'
210+ self.config.return_value = False
211+ haproxy = context.HAProxyContext()
212+ with patch_open() as (_open, _file):
213+ result = haproxy()
214+ ex = {
215+ 'units': {
216+ 'peer-0': 'cluster-peer0.localnet',
217+ 'peer-1': 'cluster-peer1.localnet',
218+ 'peer-2': 'cluster-peer2.localnet',
219+ },
220+
221+ 'local_host': '127.0.0.1',
222+ 'haproxy_host': '0.0.0.0',
223+ 'stat_port': ':8888',
224+ }
225+ # the context gets generated.
226+ self.assertEquals(ex, result)
227+ # and /etc/default/haproxy is updated.
228+ self.assertEquals(_file.write.call_args_list,
229+ [call('ENABLED=1\n')])
230+
231+ @patch('charmhelpers.contrib.openstack.context.unit_get')
232+ @patch('charmhelpers.contrib.openstack.context.local_unit')
233+ @patch('charmhelpers.contrib.openstack.context.get_ipv6_addr')
234+ def test_haproxy_context_with_data_ipv6(
235+ self, local_unit, unit_get, get_ipv6_addr):
236+ '''Test haproxy context with all relation data'''
237+ cluster_relation = {
238+ 'cluster:0': {
239+ 'peer/1': {
240+ 'private-address': 'cluster-peer1.localnet',
241+ },
242+ 'peer/2': {
243+ 'private-address': 'cluster-peer2.localnet',
244+ },
245+ },
246+ }
247+
248+ unit_get.return_value = 'peer/0'
249+ relation = FakeRelation(cluster_relation)
250+ self.relation_ids.side_effect = relation.relation_ids
251+ self.relation_get.side_effect = relation.get
252+ self.related_units.side_effect = relation.relation_units
253+ self.get_address_in_network.return_value = 'cluster-peer0.localnet'
254+ self.get_ipv6_addr.return_value = 'cluster-peer0.localnet'
255+ self.config.side_effect = [True, None, True]
256 haproxy = context.HAProxyContext()
257 with patch_open() as (_open, _file):
258 result = haproxy()
259@@ -843,7 +890,11 @@
260 'peer-0': 'cluster-peer0.localnet',
261 'peer-1': 'cluster-peer1.localnet',
262 'peer-2': 'cluster-peer2.localnet'
263- }
264+ },
265+
266+ 'local_host': 'ip6-localhost',
267+ 'haproxy_host': '::',
268+ 'stat_port': ':::8888',
269 }
270 # the context gets generated.
271 self.assertEquals(ex, result)
272@@ -876,6 +927,7 @@
273 self.relation_ids.side_effect = relation.relation_ids
274 self.relation_get.side_effect = relation.get
275 self.related_units.side_effect = relation.relation_units
276+ self.config.return_value = False
277 haproxy = context.HAProxyContext()
278 self.assertEquals({}, haproxy())
279

Subscribers

People subscribed via source and target branches