1 # Copyright (c) 2015 Red Hat, Inc.
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
20 from neutron.api.v2 import attributes as attr
21 from neutron.common import constants
22 from neutron import context
23 from neutron.db import address_scope_db
24 from neutron.db import db_base_plugin_v2
25 from neutron.extensions import address_scope as ext_address_scope
26 from neutron.tests.unit.db import test_db_base_plugin_v2
28 DB_PLUGIN_KLASS = ('neutron.tests.unit.extensions.test_address_scope.'
29 'AddressScopeTestPlugin')
32 class AddressScopeTestExtensionManager(object):
34 def get_resources(self):
35 # Add the resources to the global attribute map
36 # This is done here as the setup process won't
37 # initialize the main API router which extends
38 # the global attribute map
39 attr.RESOURCE_ATTRIBUTE_MAP.update(
40 ext_address_scope.RESOURCE_ATTRIBUTE_MAP)
41 return ext_address_scope.Address_scope.get_resources()
43 def get_actions(self):
46 def get_request_extensions(self):
50 class AddressScopeTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
52 def _create_address_scope(self, fmt, ip_version=constants.IP_VERSION_4,
53 expected_res_status=None, admin=False, **kwargs):
54 address_scope = {'address_scope': {}}
55 address_scope['address_scope']['ip_version'] = ip_version
56 for k, v in kwargs.items():
57 address_scope['address_scope'][k] = str(v)
59 address_scope_req = self.new_create_request('address-scopes',
63 neutron_context = context.Context('', kwargs.get('tenant_id',
65 address_scope_req.environ['neutron.context'] = neutron_context
67 address_scope_res = address_scope_req.get_response(self.ext_api)
68 if expected_res_status:
69 self.assertEqual(address_scope_res.status_int, expected_res_status)
70 return address_scope_res
72 def _make_address_scope(self, fmt, ip_version, admin=False, **kwargs):
73 res = self._create_address_scope(fmt, ip_version,
74 admin=admin, **kwargs)
75 if res.status_int >= webob.exc.HTTPClientError.code:
76 raise webob.exc.HTTPClientError(code=res.status_int)
77 return self.deserialize(fmt, res)
79 @contextlib.contextmanager
80 def address_scope(self, ip_version=constants.IP_VERSION_4,
81 admin=False, **kwargs):
82 addr_scope = self._make_address_scope(self.fmt, ip_version,
86 def _test_create_address_scope(self, ip_version=constants.IP_VERSION_4,
87 admin=False, expected=None, **kwargs):
89 keys.setdefault('tenant_id', self._tenant_id)
90 with self.address_scope(ip_version,
91 admin=admin, **keys) as addr_scope:
92 keys['ip_version'] = ip_version
93 self._validate_resource(addr_scope, keys, 'address_scope')
95 self._compare_resource(addr_scope, expected, 'address_scope')
98 def _test_update_address_scope(self, addr_scope_id, data, admin=False,
99 expected=None, tenant_id=None):
100 update_req = self.new_update_request(
101 'address-scopes', data, addr_scope_id)
103 neutron_context = context.Context('', tenant_id or self._tenant_id)
104 update_req.environ['neutron.context'] = neutron_context
106 update_res = update_req.get_response(self.ext_api)
108 addr_scope = self.deserialize(self.fmt, update_res)
109 self._compare_resource(addr_scope, expected, 'address_scope')
115 class AddressScopeTestPlugin(db_base_plugin_v2.NeutronDbPluginV2,
116 address_scope_db.AddressScopeDbMixin):
117 __native_pagination_support = True
118 __native_sorting_support = True
120 supported_extension_aliases = ["address-scope"]
123 class TestAddressScope(AddressScopeTestCase):
126 plugin = DB_PLUGIN_KLASS
127 ext_mgr = AddressScopeTestExtensionManager()
128 super(TestAddressScope, self).setUp(plugin=plugin, ext_mgr=ext_mgr)
130 def test_create_address_scope_ipv4(self):
131 expected_addr_scope = {'name': 'foo-address-scope',
132 'tenant_id': self._tenant_id,
134 'ip_version': constants.IP_VERSION_4}
135 self._test_create_address_scope(name='foo-address-scope',
136 expected=expected_addr_scope)
138 def test_create_address_scope_ipv6(self):
139 expected_addr_scope = {'name': 'foo-address-scope',
140 'tenant_id': self._tenant_id,
142 'ip_version': constants.IP_VERSION_6}
143 self._test_create_address_scope(constants.IP_VERSION_6,
144 name='foo-address-scope',
145 expected=expected_addr_scope)
147 def test_create_address_scope_empty_name(self):
148 expected_addr_scope = {'name': '',
149 'tenant_id': self._tenant_id,
151 self._test_create_address_scope(name='', expected=expected_addr_scope)
154 self._test_create_address_scope(expected=expected_addr_scope)
156 def test_create_address_scope_shared_admin(self):
157 expected_addr_scope = {'name': 'foo-address-scope', 'shared': True}
158 self._test_create_address_scope(name='foo-address-scope', admin=True,
160 expected=expected_addr_scope)
162 def test_created_address_scope_shared_non_admin(self):
163 res = self._create_address_scope(self.fmt, name='foo-address-scope',
164 tenant_id=self._tenant_id,
165 admin=False, shared=True)
166 self.assertEqual(webob.exc.HTTPForbidden.code, res.status_int)
168 def test_created_address_scope_specify_id(self):
169 res = self._create_address_scope(self.fmt, name='foo-address-scope',
171 self.assertEqual(webob.exc.HTTPClientError.code, res.status_int)
173 def test_delete_address_scope(self):
174 with self.address_scope(name='foo-address-scope') as addr_scope:
175 self._delete('address-scopes', addr_scope['address_scope']['id'])
176 self._show('address-scopes', addr_scope['address_scope']['id'],
177 expected_code=webob.exc.HTTPNotFound.code)
179 def test_update_address_scope(self):
180 addr_scope = self._test_create_address_scope(name='foo-address-scope')
181 data = {'address_scope': {'name': 'bar-address-scope'}}
182 self._test_update_address_scope(addr_scope['address_scope']['id'],
183 data, expected=data['address_scope'])
185 def test_update_address_scope_shared_true_admin(self):
186 addr_scope = self._test_create_address_scope(name='foo-address-scope')
187 data = {'address_scope': {'shared': True}}
188 self._test_update_address_scope(addr_scope['address_scope']['id'],
190 expected=data['address_scope'])
192 def test_update_address_scope_shared_true_non_admin(self):
193 addr_scope = self._test_create_address_scope(name='foo-address-scope')
194 data = {'address_scope': {'shared': True}}
195 res = self._test_update_address_scope(
196 addr_scope['address_scope']['id'], data, admin=False)
197 self.assertEqual(webob.exc.HTTPForbidden.code, res.status_int)
199 def test_update_address_scope_shared_false_admin(self):
200 addr_scope = self._test_create_address_scope(name='foo-address-scope',
201 admin=True, shared=True)
202 data = {'address_scope': {'shared': False}}
203 res = self._test_update_address_scope(
204 addr_scope['address_scope']['id'], data, admin=True)
205 self.assertEqual(webob.exc.HTTPClientError.code, res.status_int)
207 def test_get_address_scope(self):
208 addr_scope = self._test_create_address_scope(name='foo-address-scope')
209 req = self.new_show_request('address-scopes',
210 addr_scope['address_scope']['id'])
211 res = self.deserialize(self.fmt, req.get_response(self.ext_api))
212 self.assertEqual(addr_scope['address_scope']['id'],
213 res['address_scope']['id'])
215 def test_get_address_scope_different_tenants_not_shared(self):
216 addr_scope = self._test_create_address_scope(name='foo-address-scope')
217 req = self.new_show_request('address-scopes',
218 addr_scope['address_scope']['id'])
219 neutron_context = context.Context('', 'not-the-owner')
220 req.environ['neutron.context'] = neutron_context
221 res = req.get_response(self.ext_api)
222 self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int)
224 def test_get_address_scope_different_tenants_shared(self):
225 addr_scope = self._test_create_address_scope(name='foo-address-scope',
226 shared=True, admin=True)
227 req = self.new_show_request('address-scopes',
228 addr_scope['address_scope']['id'])
229 neutron_context = context.Context('', 'test-tenant-2')
230 req.environ['neutron.context'] = neutron_context
231 res = self.deserialize(self.fmt, req.get_response(self.ext_api))
232 self.assertEqual(addr_scope['address_scope']['id'],
233 res['address_scope']['id'])
235 def test_list_address_scopes(self):
236 self._test_create_address_scope(name='foo-address-scope')
237 self._test_create_address_scope(constants.IP_VERSION_6,
238 name='bar-address-scope')
239 res = self._list('address-scopes')
240 self.assertEqual(2, len(res['address_scopes']))
242 def test_list_address_scopes_different_tenants_shared(self):
243 self._test_create_address_scope(name='foo-address-scope', shared=True,
245 admin_res = self._list('address-scopes')
246 mortal_res = self._list(
248 neutron_context=context.Context('', 'not-the-owner'))
249 self.assertEqual(1, len(admin_res['address_scopes']))
250 self.assertEqual(1, len(mortal_res['address_scopes']))
252 def test_list_address_scopes_different_tenants_not_shared(self):
253 self._test_create_address_scope(constants.IP_VERSION_6,
254 name='foo-address-scope')
255 admin_res = self._list('address-scopes')
256 mortal_res = self._list(
258 neutron_context=context.Context('', 'not-the-owner'))
259 self.assertEqual(1, len(admin_res['address_scopes']))
260 self.assertEqual(0, len(mortal_res['address_scopes']))
263 class TestSubnetPoolsWithAddressScopes(AddressScopeTestCase):
265 plugin = DB_PLUGIN_KLASS
266 ext_mgr = AddressScopeTestExtensionManager()
267 super(TestSubnetPoolsWithAddressScopes, self).setUp(plugin=plugin,
270 def _test_create_subnetpool(self, prefixes, expected=None,
271 admin=False, **kwargs):
273 keys.setdefault('tenant_id', self._tenant_id)
274 with self.subnetpool(prefixes, admin, **keys) as subnetpool:
275 self._validate_resource(subnetpool, keys, 'subnetpool')
277 self._compare_resource(subnetpool, expected, 'subnetpool')
280 def test_create_subnetpool_associate_address_scope(self):
281 with self.address_scope(name='foo-address-scope') as addr_scope:
282 address_scope_id = addr_scope['address_scope']['id']
283 subnet = netaddr.IPNetwork('10.10.10.0/24')
284 expected = {'address_scope_id': address_scope_id}
285 self._test_create_subnetpool([subnet.cidr], expected=expected,
286 name='foo-subnetpool',
288 address_scope_id=address_scope_id)
290 def test_create_subnetpool_associate_invalid_address_scope(self):
292 webob.exc.HTTPClientError, self._test_create_subnetpool, [],
293 min_prefixlen='21', address_scope_id='foo-addr-scope-id')
295 def test_create_subnetpool_assoc_address_scope_with_prefix_intersect(self):
296 with self.address_scope(name='foo-address-scope') as addr_scope:
297 address_scope_id = addr_scope['address_scope']['id']
298 subnet = netaddr.IPNetwork('10.10.10.0/24')
299 expected = {'address_scope_id': address_scope_id}
300 self._test_create_subnetpool([subnet.cidr], expected=expected,
301 name='foo-subnetpool',
303 address_scope_id=address_scope_id)
304 overlap_subnet = netaddr.IPNetwork('10.10.10.10/24')
306 webob.exc.HTTPClientError, self._test_create_subnetpool,
307 [overlap_subnet.cidr], min_prefixlen='21',
308 address_scope_id=address_scope_id)
310 def test_update_subnetpool_associate_address_scope(self):
311 subnet = netaddr.IPNetwork('10.10.10.0/24')
312 initial_subnetpool = self._test_create_subnetpool([subnet.cidr],
315 with self.address_scope(name='foo-address-scope') as addr_scope:
316 address_scope_id = addr_scope['address_scope']['id']
317 data = {'subnetpool': {'address_scope_id': address_scope_id}}
318 req = self.new_update_request(
319 'subnetpools', data, initial_subnetpool['subnetpool']['id'])
320 api = self._api_for_resource('subnetpools')
321 res = self.deserialize(self.fmt, req.get_response(api))
322 self._compare_resource(res, data['subnetpool'], 'subnetpool')
324 def test_update_subnetpool_associate_invalid_address_scope(self):
325 subnet = netaddr.IPNetwork('10.10.10.0/24')
326 initial_subnetpool = self._test_create_subnetpool([subnet.cidr],
329 data = {'subnetpool': {'address_scope_id': 'foo-addr-scope-id'}}
330 req = self.new_update_request(
331 'subnetpools', data, initial_subnetpool['subnetpool']['id'])
332 api = self._api_for_resource('subnetpools')
333 res = req.get_response(api)
334 self.assertEqual(webob.exc.HTTPClientError.code, res.status_int)
336 def test_update_subnetpool_disassociate_address_scope(self):
337 with self.address_scope(name='foo-address-scope') as addr_scope:
338 address_scope_id = addr_scope['address_scope']['id']
339 subnet = netaddr.IPNetwork('10.10.10.0/24')
340 expected = {'address_scope_id': address_scope_id}
341 initial_subnetpool = self._test_create_subnetpool(
342 [subnet.cidr], expected=expected, name='foo-sp',
343 min_prefixlen='21', address_scope_id=address_scope_id)
345 data = {'subnetpool': {'address_scope_id': None}}
346 req = self.new_update_request(
347 'subnetpools', data, initial_subnetpool['subnetpool']['id'])
348 api = self._api_for_resource('subnetpools')
349 res = self.deserialize(self.fmt, req.get_response(api))
350 self._compare_resource(res, data['subnetpool'], 'subnetpool')
352 def test_update_subnetpool_associate_another_address_scope(self):
353 with self.address_scope(name='foo-address-scope') as addr_scope:
354 address_scope_id = addr_scope['address_scope']['id']
355 subnet = netaddr.IPNetwork('10.10.10.0/24')
356 expected = {'address_scope_id': address_scope_id}
357 initial_subnetpool = self._test_create_subnetpool(
358 [subnet.cidr], expected=expected, name='foo-sp',
359 min_prefixlen='21', address_scope_id=address_scope_id)
361 with self.address_scope(name='foo-address-scope') as other_a_s:
362 other_a_s_id = other_a_s['address_scope']['id']
363 update_data = {'subnetpool': {'address_scope_id':
365 req = self.new_update_request(
366 'subnetpools', update_data,
367 initial_subnetpool['subnetpool']['id'])
368 api = self._api_for_resource('subnetpools')
369 res = self.deserialize(self.fmt, req.get_response(api))
370 self._compare_resource(res, update_data['subnetpool'],
373 def test_delete_address_scope_in_use(self):
374 with self.address_scope(name='foo-address-scope') as addr_scope:
375 address_scope_id = addr_scope['address_scope']['id']
376 subnet = netaddr.IPNetwork('10.10.10.0/24')
377 expected = {'address_scope_id': address_scope_id}
378 self._test_create_subnetpool([subnet.cidr], expected=expected,
379 name='foo-subnetpool',
381 address_scope_id=address_scope_id)
382 self._delete('address-scopes', address_scope_id,
383 expected_code=webob.exc.HTTPConflict.code)
385 def test_add_subnetpool_address_scope_wrong_address_family(self):
386 with self.address_scope(constants.IP_VERSION_6,
387 name='foo-address-scope') as addr_scope:
388 address_scope_id = addr_scope['address_scope']['id']
389 subnet = netaddr.IPNetwork('10.10.10.0/24')
390 self.assertRaises(webob.exc.HTTPClientError,
391 self._test_create_subnetpool,
392 [subnet.cidr], name='foo-subnetpool',
394 address_scope_id=address_scope_id)
396 def test_update_subnetpool_associate_address_scope_wrong_family(self):
397 with self.address_scope(constants.IP_VERSION_6,
398 name='foo-address-scope') as addr_scope:
399 address_scope_id = addr_scope['address_scope']['id']
400 subnet = netaddr.IPNetwork('2001:db8::/64')
401 expected = {'address_scope_id': address_scope_id}
402 initial_subnetpool = self._test_create_subnetpool(
403 [subnet.cidr], expected=expected, name='foo-sp',
404 min_prefixlen='64', address_scope_id=address_scope_id)
406 with self.address_scope(name='foo-address-scope') as other_a_s:
407 other_a_s_id = other_a_s['address_scope']['id']
408 update_data = {'subnetpool': {'address_scope_id':
410 req = self.new_update_request(
411 'subnetpools', update_data,
412 initial_subnetpool['subnetpool']['id'])
413 api = self._api_for_resource('subnetpools')
414 res = req.get_response(api)
415 self.assertEqual(webob.exc.HTTPBadRequest.code,