Merge lp:~ajkavanagh/charm-helpers/memcache-context-ipv4-support into lp:charm-helpers
- memcache-context-ipv4-support
- Merge into devel
Proposed by
Alex Kavanagh
Status: | Merged |
---|---|
Merged at revision: | 735 |
Proposed branch: | lp:~ajkavanagh/charm-helpers/memcache-context-ipv4-support |
Merge into: | lp:charm-helpers |
Diff against target: |
305 lines (+113/-28) 4 files modified
charmhelpers/contrib/network/ip.py (+10/-0) charmhelpers/contrib/openstack/context.py (+21/-9) tests/contrib/network/test_ip.py (+26/-4) tests/contrib/openstack/test_os_contexts.py (+56/-15) |
To merge this branch: | bzr merge lp:~ajkavanagh/charm-helpers/memcache-context-ipv4-support |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Page | Needs Fixing | ||
Review via email:
|
Commit message
Description of the change
Add ipv4 support to the class MemcacheContext
To post a comment you must log in.
- 735. By Alex Kavanagh
-
Revised version of ipv4 support in the MemcacheContext
This version defaults to ipv6 unless ipv6 is explicitly disabled or
not available. This retains the existing behaviour of defaulting to
ipv6 regardless of the 'prefer-ipv6' setting.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
James Page (james-page) : | # |
review:
Needs Fixing
- 736. By Alex Kavanagh
-
Remove spurious extra line missed during refactor
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 2017-03-31 12:58:23 +0000 |
3 | +++ charmhelpers/contrib/network/ip.py 2017-04-19 14:35:25 +0000 |
4 | @@ -239,6 +239,16 @@ |
5 | return None |
6 | |
7 | |
8 | +def is_ipv6_disabled(): |
9 | + try: |
10 | + result = subprocess.check_output( |
11 | + ['sysctl', 'net.ipv6.conf.all.disable_ipv6'], |
12 | + stderr=subprocess.STDOUT) |
13 | + return "net.ipv6.conf.all.disable_ipv6 = 1" in result |
14 | + except subprocess.CalledProcessError: |
15 | + return True |
16 | + |
17 | + |
18 | def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False, |
19 | fatal=True, exc_list=None): |
20 | """Return the assigned IP address for a given interface, if any. |
21 | |
22 | === modified file 'charmhelpers/contrib/openstack/context.py' |
23 | --- charmhelpers/contrib/openstack/context.py 2017-03-30 08:38:44 +0000 |
24 | +++ charmhelpers/contrib/openstack/context.py 2017-04-19 14:35:25 +0000 |
25 | @@ -89,6 +89,7 @@ |
26 | format_ipv6_addr, |
27 | is_address_in_network, |
28 | is_bridge_member, |
29 | + is_ipv6_disabled, |
30 | ) |
31 | from charmhelpers.contrib.openstack.utils import ( |
32 | config_flags_parser, |
33 | @@ -1602,7 +1603,7 @@ |
34 | """Memcache context |
35 | |
36 | This context provides options for configuring a local memcache client and |
37 | - server |
38 | + server for both IPv4 and IPv6 |
39 | """ |
40 | |
41 | def __init__(self, package=None): |
42 | @@ -1620,13 +1621,24 @@ |
43 | # Trusty version of memcached does not support ::1 as a listen |
44 | # address so use host file entry instead |
45 | release = lsb_release()['DISTRIB_CODENAME'].lower() |
46 | - if CompareHostReleases(release) > 'trusty': |
47 | - ctxt['memcache_server'] = '::1' |
48 | + if is_ipv6_disabled(): |
49 | + if CompareHostReleases(release) > 'trusty': |
50 | + ctxt['memcache_server'] = '127.0.0.1' |
51 | + else: |
52 | + ctxt['memcache_server'] = 'localhost' |
53 | + ctxt['memcache_server_formatted'] = '127.0.0.1' |
54 | + ctxt['memcache_port'] = '11211' |
55 | + ctxt['memcache_url'] = '{}:{}'.format( |
56 | + ctxt['memcache_server_formatted'], |
57 | + ctxt['memcache_port']) |
58 | else: |
59 | - ctxt['memcache_server'] = 'ip6-localhost' |
60 | - ctxt['memcache_server_formatted'] = '[::1]' |
61 | - ctxt['memcache_port'] = '11211' |
62 | - ctxt['memcache_url'] = 'inet6:{}:{}'.format( |
63 | - ctxt['memcache_server_formatted'], |
64 | - ctxt['memcache_port']) |
65 | + if CompareHostReleases(release) > 'trusty': |
66 | + ctxt['memcache_server'] = '::1' |
67 | + else: |
68 | + ctxt['memcache_server'] = 'ip6-localhost' |
69 | + ctxt['memcache_server_formatted'] = '[::1]' |
70 | + ctxt['memcache_port'] = '11211' |
71 | + ctxt['memcache_url'] = 'inet6:{}:{}'.format( |
72 | + ctxt['memcache_server_formatted'], |
73 | + ctxt['memcache_port']) |
74 | return ctxt |
75 | |
76 | === modified file 'tests/contrib/network/test_ip.py' |
77 | --- tests/contrib/network/test_ip.py 2017-03-31 14:35:43 +0000 |
78 | +++ tests/contrib/network/test_ip.py 2017-04-19 14:35:25 +0000 |
79 | @@ -292,7 +292,8 @@ |
80 | '255.255.0.0') |
81 | self.assertEquals(net_ip.get_netmask_for_address('172.4.5.5'), None) |
82 | self.assertEquals( |
83 | - net_ip.get_netmask_for_address('2a01:348:2f4:0:685e:5748:ae62:210f'), |
84 | + net_ip.get_netmask_for_address( |
85 | + '2a01:348:2f4:0:685e:5748:ae62:210f'), |
86 | '64' |
87 | ) |
88 | self.assertEquals( |
89 | @@ -379,6 +380,28 @@ |
90 | _interfaces.return_value = DUMMY_ADDRESSES.keys() |
91 | self.assertRaises(Exception, net_ip.get_ipv6_addr, 'eth1') |
92 | |
93 | + @patch('charmhelpers.contrib.network.ip.subprocess.check_output') |
94 | + def test_is_ipv6_disabled(self, mock_check_output): |
95 | + # verify that the function does look for the right thing |
96 | + mock_check_output.return_value = """ |
97 | + Some lines before |
98 | + net.ipv6.conf.all.disable_ipv6 = 1 |
99 | + Some lines afterward |
100 | + """ |
101 | + self.assertTrue(net_ip.is_ipv6_disabled()) |
102 | + mock_check_output.assert_called_once_with( |
103 | + ['sysctl', 'net.ipv6.conf.all.disable_ipv6'], |
104 | + stderr=subprocess.STDOUT) |
105 | + # if it isn't there, it must return false |
106 | + mock_check_output.return_value = "" |
107 | + self.assertFalse(net_ip.is_ipv6_disabled()) |
108 | + # If the syscall returns an error, then return True |
109 | + |
110 | + def fake_check_call(*args, **kwargs): |
111 | + raise subprocess.CalledProcessError(['called'], 1) |
112 | + mock_check_output.side_effect = fake_check_call |
113 | + self.assertTrue(net_ip.is_ipv6_disabled()) |
114 | + |
115 | @patch.object(netifaces, 'ifaddresses') |
116 | @patch.object(netifaces, 'interfaces') |
117 | def test_get_iface_addr(self, _interfaces, _ifaddresses): |
118 | @@ -561,9 +584,8 @@ |
119 | |
120 | @patch('charmhelpers.contrib.network.ip.subprocess.check_output') |
121 | @patch('charmhelpers.contrib.network.ip.get_iface_addr') |
122 | - def test_get_ipv6_global_dynamic_address_invalid_address(self, |
123 | - mock_get_iface_addr, |
124 | - mock_check_out): |
125 | + def test_get_ipv6_global_dynamic_address_invalid_address( |
126 | + self, mock_get_iface_addr, mock_check_out): |
127 | mock_get_iface_addr.return_value = [] |
128 | with nose.tools.assert_raises(Exception): |
129 | net_ip.get_ipv6_addr() |
130 | |
131 | === modified file 'tests/contrib/openstack/test_os_contexts.py' |
132 | --- tests/contrib/openstack/test_os_contexts.py 2017-03-30 08:38:44 +0000 |
133 | +++ tests/contrib/openstack/test_os_contexts.py 2017-04-19 14:35:25 +0000 |
134 | @@ -228,7 +228,9 @@ |
135 | } |
136 | |
137 | AMQP_OSLO_CONFIG = { |
138 | - 'oslo-messaging-flags': "rabbit_max_retries=1,rabbit_retry_backoff=1,rabbit_retry_interval=1" |
139 | + 'oslo-messaging-flags': ("rabbit_max_retries=1" |
140 | + ",rabbit_retry_backoff=1" |
141 | + ",rabbit_retry_interval=1") |
142 | } |
143 | |
144 | AMQP_NOVA_CONFIG = { |
145 | @@ -493,13 +495,15 @@ |
146 | 'cinder-subordinate:0': { |
147 | 'cinder-subordinate/0': { |
148 | 'private-address': 'cinder_node1', |
149 | - 'subordinate_configuration': json.dumps(yaml.load(CINDER_SUB_CONFIG1)), |
150 | + 'subordinate_configuration': json.dumps( |
151 | + yaml.load(CINDER_SUB_CONFIG1)), |
152 | }, |
153 | }, |
154 | 'cinder-subordinate:1': { |
155 | 'cinder-subordinate/1': { |
156 | 'private-address': 'cinder_node1', |
157 | - 'subordinate_configuration': json.dumps(yaml.load(CINDER_SUB_CONFIG2)), |
158 | + 'subordinate_configuration': json.dumps( |
159 | + yaml.load(CINDER_SUB_CONFIG2)), |
160 | }, |
161 | }, |
162 | } |
163 | @@ -508,19 +512,22 @@ |
164 | 'nova-ceilometer:6': { |
165 | 'ceilometer-agent/0': { |
166 | 'private-address': 'nova_node1', |
167 | - 'subordinate_configuration': json.dumps(yaml.load(NOVA_SUB_CONFIG1)), |
168 | + 'subordinate_configuration': json.dumps( |
169 | + yaml.load(NOVA_SUB_CONFIG1)), |
170 | }, |
171 | }, |
172 | 'neutron-plugin:3': { |
173 | 'neutron-ovs-plugin/0': { |
174 | 'private-address': 'nova_node1', |
175 | - 'subordinate_configuration': json.dumps(yaml.load(NOVA_SUB_CONFIG2)), |
176 | + 'subordinate_configuration': json.dumps( |
177 | + yaml.load(NOVA_SUB_CONFIG2)), |
178 | }, |
179 | }, |
180 | 'neutron-plugin:4': { |
181 | 'neutron-other-plugin/0': { |
182 | 'private-address': 'nova_node1', |
183 | - 'subordinate_configuration': json.dumps(yaml.load(NOVA_SUB_CONFIG3)), |
184 | + 'subordinate_configuration': json.dumps( |
185 | + yaml.load(NOVA_SUB_CONFIG3)), |
186 | }, |
187 | } |
188 | } |
189 | @@ -672,7 +679,8 @@ |
190 | self.assertEquals(result, expected) |
191 | |
192 | def test_shared_db_context_with_data_and_access_net_mismatch(self): |
193 | - '''Mismatch between hostname and hostname for access net - defers execution''' |
194 | + """Mismatch between hostname and hostname for access net - defers |
195 | + execution""" |
196 | relation = FakeRelation( |
197 | relation_data=SHARED_DB_RELATION_ACCESS_NETWORK) |
198 | self.relation_get.side_effect = relation.get |
199 | @@ -686,7 +694,7 @@ |
200 | 'hostname': '10.5.5.1'}) |
201 | |
202 | def test_shared_db_context_with_data_and_access_net_match(self): |
203 | - '''Correctly set hostname for access net returns complete context''' |
204 | + """Correctly set hostname for access net returns complete context""" |
205 | relation = FakeRelation( |
206 | relation_data=SHARED_DB_RELATION_ACCESS_NETWORK) |
207 | self.relation_get.side_effect = relation.get |
208 | @@ -952,7 +960,8 @@ |
209 | |
210 | def test_identity_service_context_with_data_versioned(self): |
211 | '''Test shared-db context with api version supplied from keystone''' |
212 | - relation = FakeRelation(relation_data=IDENTITY_SERVICE_RELATION_VERSIONED) |
213 | + relation = FakeRelation( |
214 | + relation_data=IDENTITY_SERVICE_RELATION_VERSIONED) |
215 | self.relation_get.side_effect = relation.get |
216 | identity_service = context.IdentityServiceContext() |
217 | result = identity_service() |
218 | @@ -1117,7 +1126,8 @@ |
219 | 'rabbitmq_user': 'adam', |
220 | 'rabbitmq_virtual_host': 'foo', |
221 | 'rabbitmq_hosts': 'rabbithost1,rabbithost2', |
222 | - 'transport_url': 'rabbit://adam:foobar@rabbithost1:5672,adam:foobar@rabbithost2:5672/foo' |
223 | + 'transport_url': ('rabbit://adam:foobar@rabbithost1:5672' |
224 | + ',adam:foobar@rabbithost2:5672/foo') |
225 | } |
226 | self.assertEquals(result, expected) |
227 | |
228 | @@ -1160,7 +1170,8 @@ |
229 | 'rabbitmq_user': 'adam', |
230 | 'rabbitmq_virtual_host': 'foo', |
231 | 'rabbitmq_hosts': '[2001:db8:1::1],[2001:db8:1::1]', |
232 | - 'transport_url': 'rabbit://adam:foobar@[2001:db8:1::1]:5672,adam:foobar@[2001:db8:1::1]:5672/foo' |
233 | + 'transport_url': ('rabbit://adam:foobar@[2001:db8:1::1]:5672' |
234 | + ',adam:foobar@[2001:db8:1::1]:5672/foo') |
235 | } |
236 | self.assertEquals(result, expected) |
237 | |
238 | @@ -1829,7 +1840,8 @@ |
239 | |
240 | @patch('charmhelpers.contrib.openstack.context.unit_get') |
241 | @patch('charmhelpers.contrib.openstack.context.local_unit') |
242 | - def test_haproxy_context_with_no_peers_singlemode(self, local_unit, unit_get): |
243 | + def test_haproxy_context_with_no_peers_singlemode( |
244 | + self, local_unit, unit_get): |
245 | '''Test haproxy context with single unit''' |
246 | # peer relations always show at least one peer relation, even |
247 | # if unit is alone. should be an incomplete context. |
248 | @@ -3186,7 +3198,8 @@ |
249 | rel = FakeRelation(QUANTUM_NETWORK_SERVICE_RELATION_VERSIONED) |
250 | self.relation_ids.side_effect = rel.relation_ids |
251 | self.related_units.side_effect = rel.relation_units |
252 | - relation = FakeRelation(relation_data=QUANTUM_NETWORK_SERVICE_RELATION_VERSIONED) |
253 | + relation = FakeRelation( |
254 | + relation_data=QUANTUM_NETWORK_SERVICE_RELATION_VERSIONED) |
255 | self.relation_get.side_effect = relation.get |
256 | self.assertEquals(context.NetworkServiceContext()(), data_result) |
257 | |
258 | @@ -3274,10 +3287,14 @@ |
259 | AA.manually_disable_aa_profile.assert_called_with() |
260 | |
261 | @patch.object(context, 'enable_memcache') |
262 | - def test_memcache_context(self, _enable_memcache): |
263 | + @patch.object(context, 'is_ipv6_disabled') |
264 | + def test_memcache_context_ipv6(self, _is_ipv6_disabled, _enable_memcache): |
265 | self.lsb_release.return_value = {'DISTRIB_CODENAME': 'xenial'} |
266 | _enable_memcache.return_value = True |
267 | - config = {'openstack-origin': 'distro'} |
268 | + _is_ipv6_disabled.return_value = False |
269 | + config = { |
270 | + 'openstack-origin': 'distro', |
271 | + } |
272 | self.config.side_effect = fake_config(config) |
273 | ctxt = context.MemcacheContext() |
274 | self.assertTrue(ctxt()['use_memcache']) |
275 | @@ -3294,6 +3311,30 @@ |
276 | self.assertEqual(ctxt(), expect) |
277 | |
278 | @patch.object(context, 'enable_memcache') |
279 | + @patch.object(context, 'is_ipv6_disabled') |
280 | + def test_memcache_context_ipv4(self, _is_ipv6_disabled, _enable_memcache): |
281 | + self.lsb_release.return_value = {'DISTRIB_CODENAME': 'xenial'} |
282 | + _enable_memcache.return_value = True |
283 | + _is_ipv6_disabled.return_value = True |
284 | + config = { |
285 | + 'openstack-origin': 'distro', |
286 | + } |
287 | + self.config.side_effect = fake_config(config) |
288 | + ctxt = context.MemcacheContext() |
289 | + self.assertTrue(ctxt()['use_memcache']) |
290 | + expect = { |
291 | + 'memcache_port': '11211', |
292 | + 'memcache_server': '127.0.0.1', |
293 | + 'memcache_server_formatted': '127.0.0.1', |
294 | + 'memcache_url': '127.0.0.1:11211', |
295 | + 'use_memcache': True} |
296 | + self.assertEqual(ctxt(), expect) |
297 | + self.lsb_release.return_value = {'DISTRIB_CODENAME': 'trusty'} |
298 | + expect['memcache_server'] = 'localhost' |
299 | + ctxt = context.MemcacheContext() |
300 | + self.assertEqual(ctxt(), expect) |
301 | + |
302 | + @patch.object(context, 'enable_memcache') |
303 | def test_memcache_off_context(self, _enable_memcache): |
304 | _enable_memcache.return_value = False |
305 | config = {'openstack-origin': 'distro'} |