1 # Copyright (C) 2014 eNovance SAS <licensing@enovance.com>
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 # not use this file except in compliance with the License. You may obtain
5 # a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations
17 from neutron.agent.linux import keepalived
18 from neutron.common import constants as n_consts
19 from neutron.tests import base
21 # Keepalived user guide:
22 # http://www.keepalived.org/pdf/UserGuide.pdf
25 class KeepalivedGetFreeRangeTestCase(base.BaseTestCase):
26 def test_get_free_range(self):
27 free_range = keepalived.get_free_range(
28 parent_range='169.254.0.0/16',
29 excluded_ranges=['169.254.0.0/24',
33 self.assertEqual('169.254.3.0/24', free_range)
35 def test_get_free_range_without_excluded(self):
36 free_range = keepalived.get_free_range(
37 parent_range='169.254.0.0/16',
40 self.assertEqual('169.254.0.0/20', free_range)
42 def test_get_free_range_excluded_out_of_parent(self):
43 free_range = keepalived.get_free_range(
44 parent_range='169.254.0.0/16',
45 excluded_ranges=['255.255.255.0/24'],
47 self.assertEqual('169.254.0.0/24', free_range)
49 def test_get_free_range_not_found(self):
50 tiny_parent_range = '192.168.1.0/24'
52 with testtools.ExpectedException(ValueError):
53 keepalived.get_free_range(
54 parent_range=tiny_parent_range,
59 class KeepalivedConfBaseMixin(object):
61 def _get_config(self):
62 config = keepalived.KeepalivedConf()
64 instance1 = keepalived.KeepalivedInstance('MASTER', 'eth0', 1,
67 instance1.set_authentication('AH', 'pass123')
68 instance1.track_interfaces.append("eth0")
70 vip_address1 = keepalived.KeepalivedVipAddress('192.168.1.0/24',
73 vip_address2 = keepalived.KeepalivedVipAddress('192.168.2.0/24',
76 vip_address3 = keepalived.KeepalivedVipAddress('192.168.3.0/24',
79 vip_address_ex = keepalived.KeepalivedVipAddress('192.168.55.0/24',
82 instance1.vips.append(vip_address1)
83 instance1.vips.append(vip_address2)
84 instance1.vips.append(vip_address3)
85 instance1.vips.append(vip_address_ex)
87 virtual_route = keepalived.KeepalivedVirtualRoute(n_consts.IPv4_ANY,
90 instance1.virtual_routes.gateway_routes = [virtual_route]
92 instance2 = keepalived.KeepalivedInstance('MASTER', 'eth4', 2,
94 mcast_src_ip='224.0.0.1')
95 instance2.track_interfaces.append("eth4")
97 vip_address1 = keepalived.KeepalivedVipAddress('192.168.3.0/24',
100 instance2.vips.append(vip_address1)
101 instance2.vips.append(vip_address2)
102 instance2.vips.append(vip_address_ex)
104 config.add_instance(instance1)
105 config.add_instance(instance2)
110 class KeepalivedConfTestCase(base.BaseTestCase,
111 KeepalivedConfBaseMixin):
113 expected = """vrrp_instance VR_1 {
119 garp_master_refresh 10
129 169.254.0.1/24 dev eth0
131 virtual_ipaddress_excluded {
132 192.168.1.0/24 dev eth1
133 192.168.2.0/24 dev eth2
134 192.168.3.0/24 dev eth2
135 192.168.55.0/24 dev eth10
138 0.0.0.0/0 via 192.168.1.1 dev eth1
147 garp_master_refresh 10
148 mcast_src_ip 224.0.0.1
153 169.254.0.2/24 dev eth4
155 virtual_ipaddress_excluded {
156 192.168.2.0/24 dev eth2
157 192.168.3.0/24 dev eth6
158 192.168.55.0/24 dev eth10
162 def test_config_generation(self):
163 config = self._get_config()
164 self.assertEqual(self.expected, config.get_config_str())
166 def test_config_with_reset(self):
167 config = self._get_config()
168 self.assertEqual(self.expected, config.get_config_str())
171 self.assertEqual('', config.get_config_str())
173 def test_get_existing_vip_ip_addresses_returns_list(self):
174 config = self._get_config()
175 instance = config.get_instance(1)
176 current_vips = sorted(instance.get_existing_vip_ip_addresses('eth2'))
177 self.assertEqual(['192.168.2.0/24', '192.168.3.0/24'], current_vips)
180 class KeepalivedStateExceptionTestCase(base.BaseTestCase):
181 def test_state_exception(self):
182 invalid_vrrp_state = 'a seal walks'
183 self.assertRaises(keepalived.InvalidInstanceStateException,
184 keepalived.KeepalivedInstance,
185 invalid_vrrp_state, 'eth0', 33,
186 ['169.254.192.0/18'])
188 invalid_auth_type = 'into a club'
189 instance = keepalived.KeepalivedInstance('MASTER', 'eth0', 1,
190 ['169.254.192.0/18'])
191 self.assertRaises(keepalived.InvalidAuthenticationTypeException,
192 instance.set_authentication,
193 invalid_auth_type, 'some_password')
196 class KeepalivedInstanceRoutesTestCase(base.BaseTestCase):
198 def _get_instance_routes(cls):
199 routes = keepalived.KeepalivedInstanceRoutes()
200 default_gw_eth0 = keepalived.KeepalivedVirtualRoute(
201 '0.0.0.0/0', '1.0.0.254', 'eth0')
202 default_gw_eth1 = keepalived.KeepalivedVirtualRoute(
203 '::/0', 'fe80::3e97:eff:fe26:3bfa/64', 'eth1')
204 routes.gateway_routes = [default_gw_eth0, default_gw_eth1]
206 keepalived.KeepalivedVirtualRoute('10.0.0.0/8', '1.0.0.1'),
207 keepalived.KeepalivedVirtualRoute('20.0.0.0/8', '2.0.0.2')]
208 routes.extra_routes = extra_routes
210 keepalived.KeepalivedVirtualRoute(
211 '30.0.0.0/8', None, 'eth0', scope='link')]
212 routes.extra_subnets = extra_subnets
215 def test_routes(self):
216 routes = self._get_instance_routes()
217 self.assertEqual(len(routes.routes), 5)
219 def test_remove_routes_on_interface(self):
220 routes = self._get_instance_routes()
221 routes.remove_routes_on_interface('eth0')
222 self.assertEqual(len(routes.routes), 3)
223 routes.remove_routes_on_interface('eth1')
224 self.assertEqual(len(routes.routes), 2)
226 def test_build_config(self):
227 expected = """ virtual_routes {
228 0.0.0.0/0 via 1.0.0.254 dev eth0
229 ::/0 via fe80::3e97:eff:fe26:3bfa/64 dev eth1
230 10.0.0.0/8 via 1.0.0.1
231 20.0.0.0/8 via 2.0.0.2
232 30.0.0.0/8 dev eth0 scope link
234 routes = self._get_instance_routes()
235 self.assertEqual(expected, '\n'.join(routes.build_config()))
238 class KeepalivedInstanceTestCase(base.BaseTestCase,
239 KeepalivedConfBaseMixin):
240 def test_get_primary_vip(self):
241 instance = keepalived.KeepalivedInstance('MASTER', 'ha0', 42,
242 ['169.254.192.0/18'])
243 self.assertEqual('169.254.0.42/24', instance.get_primary_vip())
245 def test_remove_addresses_by_interface(self):
246 config = self._get_config()
247 instance = config.get_instance(1)
248 instance.remove_vips_vroutes_by_interface('eth2')
249 instance.remove_vips_vroutes_by_interface('eth10')
251 expected = """vrrp_instance VR_1 {
257 garp_master_refresh 10
267 169.254.0.1/24 dev eth0
269 virtual_ipaddress_excluded {
270 192.168.1.0/24 dev eth1
273 0.0.0.0/0 via 192.168.1.1 dev eth1
282 garp_master_refresh 10
283 mcast_src_ip 224.0.0.1
288 169.254.0.2/24 dev eth4
290 virtual_ipaddress_excluded {
291 192.168.2.0/24 dev eth2
292 192.168.3.0/24 dev eth6
293 192.168.55.0/24 dev eth10
297 self.assertEqual(expected, config.get_config_str())
299 def test_build_config_no_vips(self):
300 expected = """vrrp_instance VR_1 {
306 garp_master_refresh 10
308 169.254.0.1/24 dev eth0
311 instance = keepalived.KeepalivedInstance(
312 'MASTER', 'eth0', 1, ['169.254.192.0/18'])
313 self.assertEqual(expected, '\n'.join(instance.build_config()))
316 class KeepalivedVipAddressTestCase(base.BaseTestCase):
317 def test_vip_with_scope(self):
318 vip = keepalived.KeepalivedVipAddress('fe80::3e97:eff:fe26:3bfa/64',
321 self.assertEqual('fe80::3e97:eff:fe26:3bfa/64 dev eth1 scope link',
324 def test_add_vip_idempotent(self):
325 instance = keepalived.KeepalivedInstance('MASTER', 'eth0', 1,
326 ['169.254.192.0/18'])
327 instance.add_vip('192.168.222.1/32', 'eth11', None)
328 instance.add_vip('192.168.222.1/32', 'eth12', 'link')
329 self.assertEqual(1, len(instance.vips))
332 class KeepalivedVirtualRouteTestCase(base.BaseTestCase):
333 def test_virtual_route_with_dev(self):
334 route = keepalived.KeepalivedVirtualRoute(n_consts.IPv4_ANY, '1.2.3.4',
336 self.assertEqual('0.0.0.0/0 via 1.2.3.4 dev eth0',
337 route.build_config())
339 def test_virtual_route_without_dev(self):
340 route = keepalived.KeepalivedVirtualRoute('50.0.0.0/8', '1.2.3.4')
341 self.assertEqual('50.0.0.0/8 via 1.2.3.4', route.build_config())