Merge lp:~ajkavanagh/charm-helpers/memcache-context-ipv4-support into lp:charm-helpers

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
Reviewer Review Type Date Requested Status
James Page Needs Fixing
Review via email: mp+322743@code.launchpad.net

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
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'}

Subscribers

People subscribed via source and target branches