Set lock_path correctly.
[openstack-build/neutron-build.git] / neutron / tests / unit / plugins / ml2 / drivers / base_type_tunnel.py
1 # Copyright (c) 2014 OpenStack Foundation, all rights reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12 # implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 import mock
17 from six import moves
18 import testtools
19 from testtools import matchers
20
21 from neutron.common import exceptions as exc
22 from neutron.db import api as db
23 from neutron.plugins.ml2 import driver_api as api
24 from neutron.plugins.ml2.drivers import type_tunnel
25
26 TUNNEL_IP_ONE = "10.10.10.10"
27 TUNNEL_IP_TWO = "10.10.10.20"
28 HOST_ONE = 'fake_host_one'
29 HOST_TWO = 'fake_host_two'
30 TUN_MIN = 100
31 TUN_MAX = 109
32 TUNNEL_RANGES = [(TUN_MIN, TUN_MAX)]
33 UPDATED_TUNNEL_RANGES = [(TUN_MIN + 5, TUN_MAX + 5)]
34
35
36 class TunnelTypeTestMixin(object):
37     DRIVER_CLASS = None
38     TYPE = None
39
40     def setUp(self):
41         super(TunnelTypeTestMixin, self).setUp()
42         self.driver = self.DRIVER_CLASS()
43         self.driver.tunnel_ranges = TUNNEL_RANGES
44         self.driver.sync_allocations()
45         self.session = db.get_session()
46
47     def test_tunnel_type(self):
48         self.assertEqual(self.TYPE, self.driver.get_type())
49
50     def test_validate_provider_segment(self):
51         segment = {api.NETWORK_TYPE: self.TYPE,
52                    api.PHYSICAL_NETWORK: 'phys_net',
53                    api.SEGMENTATION_ID: None}
54
55         with testtools.ExpectedException(exc.InvalidInput):
56             self.driver.validate_provider_segment(segment)
57
58         segment[api.PHYSICAL_NETWORK] = None
59         self.driver.validate_provider_segment(segment)
60
61         segment[api.SEGMENTATION_ID] = 1
62         self.driver.validate_provider_segment(segment)
63
64     def test_sync_tunnel_allocations(self):
65         self.assertIsNone(
66             self.driver.get_allocation(self.session, (TUN_MIN - 1)))
67         self.assertFalse(
68             self.driver.get_allocation(self.session, (TUN_MIN)).allocated)
69         self.assertFalse(
70             self.driver.get_allocation(self.session, (TUN_MIN + 1)).allocated)
71         self.assertFalse(
72             self.driver.get_allocation(self.session, (TUN_MAX - 1)).allocated)
73         self.assertFalse(
74             self.driver.get_allocation(self.session, (TUN_MAX)).allocated)
75         self.assertIsNone(
76             self.driver.get_allocation(self.session, (TUN_MAX + 1)))
77
78         self.driver.tunnel_ranges = UPDATED_TUNNEL_RANGES
79         self.driver.sync_allocations()
80
81         self.assertIsNone(
82             self.driver.get_allocation(self.session, (TUN_MIN + 5 - 1)))
83         self.assertFalse(
84             self.driver.get_allocation(self.session, (TUN_MIN + 5)).allocated)
85         self.assertFalse(
86             self.driver.get_allocation(self.session,
87                                        (TUN_MIN + 5 + 1)).allocated)
88         self.assertFalse(
89             self.driver.get_allocation(self.session,
90                                        (TUN_MAX + 5 - 1)).allocated)
91         self.assertFalse(
92             self.driver.get_allocation(self.session, (TUN_MAX + 5)).allocated)
93         self.assertIsNone(
94             self.driver.get_allocation(self.session, (TUN_MAX + 5 + 1)))
95
96     def _test_sync_allocations_and_allocated(self, tunnel_id):
97         segment = {api.NETWORK_TYPE: self.TYPE,
98                    api.PHYSICAL_NETWORK: None,
99                    api.SEGMENTATION_ID: tunnel_id}
100         self.driver.reserve_provider_segment(self.session, segment)
101
102         self.driver.tunnel_ranges = UPDATED_TUNNEL_RANGES
103         self.driver.sync_allocations()
104
105         self.assertTrue(
106             self.driver.get_allocation(self.session, tunnel_id).allocated)
107
108     def test_sync_allocations_and_allocated_in_initial_range(self):
109         self._test_sync_allocations_and_allocated(TUN_MIN + 2)
110
111     def test_sync_allocations_and_allocated_in_final_range(self):
112         self._test_sync_allocations_and_allocated(TUN_MAX + 2)
113
114     def test_sync_allocations_no_op(self):
115
116         def verify_no_chunk(iterable, chunk_size):
117             # no segment removed/added
118             self.assertEqual(0, len(list(iterable)))
119             return []
120         with mock.patch.object(
121                 type_tunnel, 'chunks', side_effect=verify_no_chunk) as chunks:
122             self.driver.sync_allocations()
123             self.assertEqual(2, len(chunks.mock_calls))
124
125     def test_partial_segment_is_partial_segment(self):
126         segment = {api.NETWORK_TYPE: self.TYPE,
127                    api.PHYSICAL_NETWORK: None,
128                    api.SEGMENTATION_ID: None}
129         self.assertTrue(self.driver.is_partial_segment(segment))
130
131     def test_specific_segment_is_not_partial_segment(self):
132         segment = {api.NETWORK_TYPE: self.TYPE,
133                    api.PHYSICAL_NETWORK: None,
134                    api.SEGMENTATION_ID: 101}
135         self.assertFalse(self.driver.is_partial_segment(segment))
136
137     def test_reserve_provider_segment_full_specs(self):
138         segment = {api.NETWORK_TYPE: self.TYPE,
139                    api.PHYSICAL_NETWORK: None,
140                    api.SEGMENTATION_ID: 101}
141         observed = self.driver.reserve_provider_segment(self.session, segment)
142         alloc = self.driver.get_allocation(self.session,
143                                            observed[api.SEGMENTATION_ID])
144         self.assertTrue(alloc.allocated)
145
146         with testtools.ExpectedException(exc.TunnelIdInUse):
147             self.driver.reserve_provider_segment(self.session, segment)
148
149         self.driver.release_segment(self.session, segment)
150         alloc = self.driver.get_allocation(self.session,
151                                            observed[api.SEGMENTATION_ID])
152         self.assertFalse(alloc.allocated)
153
154         segment[api.SEGMENTATION_ID] = 1000
155         observed = self.driver.reserve_provider_segment(self.session, segment)
156         alloc = self.driver.get_allocation(self.session,
157                                            observed[api.SEGMENTATION_ID])
158         self.assertTrue(alloc.allocated)
159
160         self.driver.release_segment(self.session, segment)
161         alloc = self.driver.get_allocation(self.session,
162                                            observed[api.SEGMENTATION_ID])
163         self.assertIsNone(alloc)
164
165     def test_reserve_provider_segment(self):
166         tunnel_ids = set()
167         specs = {api.NETWORK_TYPE: self.TYPE,
168                  api.PHYSICAL_NETWORK: 'None',
169                  api.SEGMENTATION_ID: None}
170
171         for x in moves.range(TUN_MIN, TUN_MAX + 1):
172             segment = self.driver.reserve_provider_segment(self.session,
173                                                            specs)
174             self.assertEqual(self.TYPE, segment[api.NETWORK_TYPE])
175             self.assertThat(segment[api.SEGMENTATION_ID],
176                             matchers.GreaterThan(TUN_MIN - 1))
177             self.assertThat(segment[api.SEGMENTATION_ID],
178                             matchers.LessThan(TUN_MAX + 1))
179             tunnel_ids.add(segment[api.SEGMENTATION_ID])
180
181         with testtools.ExpectedException(exc.NoNetworkAvailable):
182             segment = self.driver.reserve_provider_segment(self.session,
183                                                            specs)
184
185         segment = {api.NETWORK_TYPE: self.TYPE,
186                    api.PHYSICAL_NETWORK: 'None',
187                    api.SEGMENTATION_ID: tunnel_ids.pop()}
188         self.driver.release_segment(self.session, segment)
189         segment = self.driver.reserve_provider_segment(self.session, specs)
190         self.assertThat(segment[api.SEGMENTATION_ID],
191                         matchers.GreaterThan(TUN_MIN - 1))
192         self.assertThat(segment[api.SEGMENTATION_ID],
193                         matchers.LessThan(TUN_MAX + 1))
194         tunnel_ids.add(segment[api.SEGMENTATION_ID])
195
196         for tunnel_id in tunnel_ids:
197             segment[api.SEGMENTATION_ID] = tunnel_id
198             self.driver.release_segment(self.session, segment)
199
200     def test_allocate_tenant_segment(self):
201         tunnel_ids = set()
202         for x in moves.range(TUN_MIN, TUN_MAX + 1):
203             segment = self.driver.allocate_tenant_segment(self.session)
204             self.assertThat(segment[api.SEGMENTATION_ID],
205                             matchers.GreaterThan(TUN_MIN - 1))
206             self.assertThat(segment[api.SEGMENTATION_ID],
207                             matchers.LessThan(TUN_MAX + 1))
208             tunnel_ids.add(segment[api.SEGMENTATION_ID])
209
210         segment = self.driver.allocate_tenant_segment(self.session)
211         self.assertIsNone(segment)
212
213         segment = {api.NETWORK_TYPE: self.TYPE,
214                    api.PHYSICAL_NETWORK: 'None',
215                    api.SEGMENTATION_ID: tunnel_ids.pop()}
216         self.driver.release_segment(self.session, segment)
217         segment = self.driver.allocate_tenant_segment(self.session)
218         self.assertThat(segment[api.SEGMENTATION_ID],
219                         matchers.GreaterThan(TUN_MIN - 1))
220         self.assertThat(segment[api.SEGMENTATION_ID],
221                         matchers.LessThan(TUN_MAX + 1))
222         tunnel_ids.add(segment[api.SEGMENTATION_ID])
223
224         for tunnel_id in tunnel_ids:
225             segment[api.SEGMENTATION_ID] = tunnel_id
226             self.driver.release_segment(self.session, segment)
227
228     def add_endpoint(self, ip=TUNNEL_IP_ONE, host=HOST_ONE):
229         return self.driver.add_endpoint(ip, host)
230
231     def test_add_endpoint(self):
232         endpoint = self.add_endpoint()
233         self.assertEqual(TUNNEL_IP_ONE, endpoint.ip_address)
234         self.assertEqual(HOST_ONE, endpoint.host)
235         return endpoint
236
237     def test_add_endpoint_for_existing_tunnel_ip(self):
238         self.add_endpoint()
239
240         with mock.patch.object(type_tunnel.LOG, 'warning') as log_warn:
241             self.add_endpoint()
242             log_warn.assert_called_once_with(mock.ANY, TUNNEL_IP_ONE)
243
244     def test_get_endpoint_by_host(self):
245         self.add_endpoint()
246
247         host_endpoint = self.driver.get_endpoint_by_host(HOST_ONE)
248         self.assertEqual(TUNNEL_IP_ONE, host_endpoint.ip_address)
249         return host_endpoint
250
251     def test_get_endpoint_by_host_for_not_existing_host(self):
252         ip_endpoint = self.driver.get_endpoint_by_host(HOST_TWO)
253         self.assertIsNone(ip_endpoint)
254
255     def test_get_endpoint_by_ip(self):
256         self.add_endpoint()
257
258         ip_endpoint = self.driver.get_endpoint_by_ip(TUNNEL_IP_ONE)
259         self.assertEqual(HOST_ONE, ip_endpoint.host)
260         return ip_endpoint
261
262     def test_get_endpoint_by_ip_for_not_existing_tunnel_ip(self):
263         ip_endpoint = self.driver.get_endpoint_by_ip(TUNNEL_IP_TWO)
264         self.assertIsNone(ip_endpoint)
265
266     def test_delete_endpoint(self):
267         self.add_endpoint()
268
269         self.assertIsNone(self.driver.delete_endpoint(TUNNEL_IP_ONE))
270         # Get all the endpoints and verify its empty
271         endpoints = self.driver.get_endpoints()
272         self.assertNotIn(TUNNEL_IP_ONE, endpoints)
273
274
275 class TunnelTypeMultiRangeTestMixin(object):
276     DRIVER_CLASS = None
277
278     TUN_MIN0 = 100
279     TUN_MAX0 = 101
280     TUN_MIN1 = 200
281     TUN_MAX1 = 201
282     TUNNEL_MULTI_RANGES = [(TUN_MIN0, TUN_MAX0), (TUN_MIN1, TUN_MAX1)]
283
284     def setUp(self):
285         super(TunnelTypeMultiRangeTestMixin, self).setUp()
286         self.driver = self.DRIVER_CLASS()
287         self.driver.tunnel_ranges = self.TUNNEL_MULTI_RANGES
288         self.driver.sync_allocations()
289         self.session = db.get_session()
290
291     def test_release_segment(self):
292         segments = [self.driver.allocate_tenant_segment(self.session)
293                     for i in range(4)]
294
295         # Release them in random order. No special meaning.
296         for i in (0, 2, 1, 3):
297             self.driver.release_segment(self.session, segments[i])
298
299         for key in (self.TUN_MIN0, self.TUN_MAX0,
300                     self.TUN_MIN1, self.TUN_MAX1):
301             alloc = self.driver.get_allocation(self.session, key)
302             self.assertFalse(alloc.allocated)
303
304
305 class TunnelRpcCallbackTestMixin(object):
306
307     DRIVER_CLASS = None
308     TYPE = None
309
310     def setUp(self):
311         super(TunnelRpcCallbackTestMixin, self).setUp()
312         self.driver = self.DRIVER_CLASS()
313
314     def _test_tunnel_sync(self, kwargs, delete_tunnel=False):
315         with mock.patch.object(self.notifier,
316                                'tunnel_update') as tunnel_update,\
317                 mock.patch.object(self.notifier,
318                                   'tunnel_delete') as tunnel_delete:
319             details = self.callbacks.tunnel_sync('fake_context', **kwargs)
320             tunnels = details['tunnels']
321             for tunnel in tunnels:
322                 self.assertEqual(kwargs['tunnel_ip'], tunnel['ip_address'])
323                 self.assertEqual(kwargs['host'], tunnel['host'])
324             self.assertTrue(tunnel_update.called)
325             if delete_tunnel:
326                 self.assertTrue(tunnel_delete.called)
327             else:
328                 self.assertFalse(tunnel_delete.called)
329
330     def _test_tunnel_sync_raises(self, kwargs):
331         with mock.patch.object(self.notifier,
332                                'tunnel_update') as tunnel_update,\
333                 mock.patch.object(self.notifier,
334                                   'tunnel_delete') as tunnel_delete:
335             self.assertRaises(exc.InvalidInput,
336                               self.callbacks.tunnel_sync,
337                               'fake_context', **kwargs)
338             self.assertFalse(tunnel_update.called)
339             self.assertFalse(tunnel_delete.called)
340
341     def test_tunnel_sync_called_without_host_passed(self):
342         kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE,
343                   'host': None}
344         self._test_tunnel_sync(kwargs)
345
346     def test_tunnel_sync_called_with_host_passed_for_existing_tunnel_ip(self):
347         self.driver.add_endpoint(TUNNEL_IP_ONE, None)
348
349         kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE,
350                   'host': HOST_ONE}
351         self._test_tunnel_sync(kwargs)
352
353     def test_tunnel_sync_called_with_host_passed(self):
354         kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE,
355                   'host': HOST_ONE}
356         self._test_tunnel_sync(kwargs)
357
358     def test_tunnel_sync_called_for_existing_endpoint(self):
359         self.driver.add_endpoint(TUNNEL_IP_ONE, HOST_ONE)
360
361         kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE,
362                   'host': HOST_ONE}
363         self._test_tunnel_sync(kwargs)
364
365     def test_tunnel_sync_called_for_existing_host_with_tunnel_ip_changed(self):
366         self.driver.add_endpoint(TUNNEL_IP_ONE, HOST_ONE)
367
368         kwargs = {'tunnel_ip': TUNNEL_IP_TWO, 'tunnel_type': self.TYPE,
369                   'host': HOST_ONE}
370         self._test_tunnel_sync(kwargs, True)
371
372     def test_tunnel_sync_called_with_used_tunnel_ip_host_roaming(self):
373         self.driver.add_endpoint(TUNNEL_IP_ONE, HOST_ONE)
374
375         kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE,
376                   'host': HOST_TWO}
377         self._test_tunnel_sync(kwargs, False)
378
379     def test_tunnel_sync_called_with_used_tunnel_ip_roaming_case_two(self):
380         self.driver.add_endpoint(TUNNEL_IP_ONE, None)
381         self.driver.add_endpoint(TUNNEL_IP_TWO, HOST_TWO)
382
383         kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE,
384                   'host': HOST_TWO}
385         self._test_tunnel_sync(kwargs, False)
386
387     def test_tunnel_sync_called_without_tunnel_ip(self):
388         kwargs = {'tunnel_type': self.TYPE, 'host': None}
389         self._test_tunnel_sync_raises(kwargs)
390
391     def test_tunnel_sync_called_without_tunnel_type(self):
392         kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'host': None}
393         self._test_tunnel_sync_raises(kwargs)