Merge lp:~xianghui/charm-helpers/support-ipv6 into lp:charm-helpers
- support-ipv6
- Merge into devel
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Edward Hope-Morley | Approve | ||
Review via email:
|
Commit message
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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Edward Hope-Morley (hopem) wrote : | # |
Hui, I'm getting some lint errors:
$ make lint
Checking for Python syntax...
charmhelpers/
charmhelpers/
charmhelpers/
tests/contrib/
make: *** [lint] Error 1
Also, could you please update/add unit tests to cover the change you are making.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Edward Hope-Morley (hopem) wrote : | # |
Currently getting the following unit test failures as well: https:/
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Xiang Hui (xianghui) wrote : | # |
> Hui, I'm getting some lint errors:
>
> $ make lint
> Checking for Python syntax...
> charmhelpers/
> 'get_ipv6_addr'
> charmhelpers/
> charmhelpers/
> tests/contrib/
> 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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Xiang Hui (xianghui) wrote : | # |
> Currently getting the following unit test failures as well:
> https:/
Just the first two errors "get_ipv6_add.." caused by my changes due to the conflict mis-deleted.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Edward Hope-Morley (hopem) wrote : | # |
If I remove all your changes I get no errors i.e. trunk has no errors.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
James Page (james-page) : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Edward Hope-Morley (hopem) : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Edward Hope-Morley (hopem) wrote : | # |
Couple of issues that need fixing.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Xiang Hui (xianghui) wrote : | # |
> Couple of issues that need fixing.
Thanks, done.
Preview Diff
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 |
Need to fx merge conflict.