blob: 639fa3c7131c78973c0f4cc9e1292c6ed8451d34 [file] [log] [blame]
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001# Copyright 2012 OpenStack Foundation
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Ihar Hrachyshka59382252016-04-05 15:54:33 +020016import functools
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +020017import math
Lajos Katona2f904652018-08-23 14:04:56 +020018import time
Ihar Hrachyshka59382252016-04-05 15:54:33 +020019
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000020import netaddr
Chandan Kumarc125fd12017-11-15 19:41:01 +053021from neutron_lib import constants as const
Lajos Katona2f904652018-08-23 14:04:56 +020022from oslo_log import log
Chandan Kumarc125fd12017-11-15 19:41:01 +053023from tempest.common import utils as tutils
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000024from tempest.lib.common.utils import data_utils
25from tempest.lib import exceptions as lib_exc
26from tempest import test
27
Chandan Kumar667d3d32017-09-22 12:24:06 +053028from neutron_tempest_plugin.api import clients
29from neutron_tempest_plugin.common import constants
30from neutron_tempest_plugin.common import utils
31from neutron_tempest_plugin import config
32from neutron_tempest_plugin import exceptions
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000033
34CONF = config.CONF
35
Lajos Katona2f904652018-08-23 14:04:56 +020036LOG = log.getLogger(__name__)
37
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000038
39class BaseNetworkTest(test.BaseTestCase):
40
Brian Haleyae328b92018-10-09 19:51:54 -040041 """Base class for Neutron tests that use the Tempest Neutron REST client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000042
43 Per the Neutron API Guide, API v1.x was removed from the source code tree
44 (docs.openstack.org/api/openstack-network/2.0/content/Overview-d1e71.html)
45 Therefore, v2.x of the Neutron API is assumed. It is also assumed that the
46 following options are defined in the [network] section of etc/tempest.conf:
47
48 project_network_cidr with a block of cidr's from which smaller blocks
49 can be allocated for tenant networks
50
51 project_network_mask_bits with the mask bits to be used to partition
52 the block defined by tenant-network_cidr
53
54 Finally, it is assumed that the following option is defined in the
55 [service_available] section of etc/tempest.conf
56
57 neutron as True
58 """
59
60 force_tenant_isolation = False
61 credentials = ['primary']
62
63 # Default to ipv4.
Federico Ressi0ddc93b2018-04-09 12:01:48 +020064 _ip_version = const.IP_VERSION_4
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000065
Federico Ressi61b564e2018-07-06 08:10:31 +020066 # Derive from BaseAdminNetworkTest class to have this initialized
67 admin_client = None
68
Federico Ressia69dcd52018-07-06 09:45:34 +020069 external_network_id = CONF.network.public_network_id
70
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000071 @classmethod
72 def get_client_manager(cls, credential_type=None, roles=None,
73 force_new=None):
Genadi Chereshnyacc395c02016-07-25 12:17:37 +030074 manager = super(BaseNetworkTest, cls).get_client_manager(
75 credential_type=credential_type,
76 roles=roles,
77 force_new=force_new
78 )
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000079 # Neutron uses a different clients manager than the one in the Tempest
Jens Harbott860b46a2017-11-15 21:23:15 +000080 # save the original in case mixed tests need it
81 if credential_type == 'primary':
82 cls.os_tempest = manager
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000083 return clients.Manager(manager.credentials)
84
85 @classmethod
86 def skip_checks(cls):
87 super(BaseNetworkTest, cls).skip_checks()
88 if not CONF.service_available.neutron:
89 raise cls.skipException("Neutron support is required")
Federico Ressi0ddc93b2018-04-09 12:01:48 +020090 if (cls._ip_version == const.IP_VERSION_6 and
91 not CONF.network_feature_enabled.ipv6):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000092 raise cls.skipException("IPv6 Tests are disabled.")
Jakub Libosvar1982aa12017-05-30 11:15:33 +000093 for req_ext in getattr(cls, 'required_extensions', []):
Chandan Kumarc125fd12017-11-15 19:41:01 +053094 if not tutils.is_extension_enabled(req_ext, 'network'):
Jakub Libosvar1982aa12017-05-30 11:15:33 +000095 msg = "%s extension not enabled." % req_ext
96 raise cls.skipException(msg)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000097
98 @classmethod
99 def setup_credentials(cls):
100 # Create no network resources for these test.
101 cls.set_network_resources()
102 super(BaseNetworkTest, cls).setup_credentials()
103
104 @classmethod
105 def setup_clients(cls):
106 super(BaseNetworkTest, cls).setup_clients()
fumihiko kakumaa216fc12017-07-14 10:43:29 +0900107 cls.client = cls.os_primary.network_client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000108
109 @classmethod
110 def resource_setup(cls):
111 super(BaseNetworkTest, cls).resource_setup()
112
113 cls.networks = []
Miguel Lavalle124378b2016-09-21 16:41:47 -0500114 cls.admin_networks = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000115 cls.subnets = []
Kevin Bentonba3651c2017-09-01 17:13:01 -0700116 cls.admin_subnets = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000117 cls.ports = []
118 cls.routers = []
119 cls.floating_ips = []
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200120 cls.port_forwardings = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000121 cls.metering_labels = []
122 cls.service_profiles = []
123 cls.flavors = []
124 cls.metering_label_rules = []
125 cls.qos_rules = []
126 cls.qos_policies = []
127 cls.ethertype = "IPv" + str(cls._ip_version)
128 cls.address_scopes = []
129 cls.admin_address_scopes = []
130 cls.subnetpools = []
131 cls.admin_subnetpools = []
Itzik Brownbac51dc2016-10-31 12:25:04 +0000132 cls.security_groups = []
Dongcan Ye2de722e2018-07-04 11:01:37 +0000133 cls.admin_security_groups = []
Chandan Kumarc125fd12017-11-15 19:41:01 +0530134 cls.projects = []
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700135 cls.log_objects = []
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200136 cls.reserved_subnet_cidrs = set()
Federico Ressiab286e42018-06-19 09:52:10 +0200137 cls.keypairs = []
Federico Ressi82e83e32018-07-03 14:19:55 +0200138 cls.trunks = []
Kailun Qineaaf9782018-12-20 04:45:01 +0800139 cls.network_segment_ranges = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000140
141 @classmethod
142 def resource_cleanup(cls):
143 if CONF.service_available.neutron:
Federico Ressi82e83e32018-07-03 14:19:55 +0200144 # Clean up trunks
145 for trunk in cls.trunks:
146 cls._try_delete_resource(cls.delete_trunk, trunk)
147
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200148 # Clean up port forwardings
149 for pf in cls.port_forwardings:
150 cls._try_delete_resource(cls.delete_port_forwarding, pf)
151
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000152 # Clean up floating IPs
153 for floating_ip in cls.floating_ips:
Federico Ressia69dcd52018-07-06 09:45:34 +0200154 cls._try_delete_resource(cls.delete_floatingip, floating_ip)
155
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000156 # Clean up routers
157 for router in cls.routers:
158 cls._try_delete_resource(cls.delete_router,
159 router)
160 # Clean up metering label rules
161 for metering_label_rule in cls.metering_label_rules:
162 cls._try_delete_resource(
163 cls.admin_client.delete_metering_label_rule,
164 metering_label_rule['id'])
165 # Clean up metering labels
166 for metering_label in cls.metering_labels:
167 cls._try_delete_resource(
168 cls.admin_client.delete_metering_label,
169 metering_label['id'])
170 # Clean up flavors
171 for flavor in cls.flavors:
172 cls._try_delete_resource(
173 cls.admin_client.delete_flavor,
174 flavor['id'])
175 # Clean up service profiles
176 for service_profile in cls.service_profiles:
177 cls._try_delete_resource(
178 cls.admin_client.delete_service_profile,
179 service_profile['id'])
180 # Clean up ports
181 for port in cls.ports:
182 cls._try_delete_resource(cls.client.delete_port,
183 port['id'])
184 # Clean up subnets
185 for subnet in cls.subnets:
186 cls._try_delete_resource(cls.client.delete_subnet,
187 subnet['id'])
Kevin Bentonba3651c2017-09-01 17:13:01 -0700188 # Clean up admin subnets
189 for subnet in cls.admin_subnets:
190 cls._try_delete_resource(cls.admin_client.delete_subnet,
191 subnet['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000192 # Clean up networks
193 for network in cls.networks:
Federico Ressi61b564e2018-07-06 08:10:31 +0200194 cls._try_delete_resource(cls.delete_network, network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000195
Miguel Lavalle124378b2016-09-21 16:41:47 -0500196 # Clean up admin networks
197 for network in cls.admin_networks:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000198 cls._try_delete_resource(cls.admin_client.delete_network,
199 network['id'])
200
Itzik Brownbac51dc2016-10-31 12:25:04 +0000201 # Clean up security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200202 for security_group in cls.security_groups:
203 cls._try_delete_resource(cls.delete_security_group,
204 security_group)
Itzik Brownbac51dc2016-10-31 12:25:04 +0000205
Dongcan Ye2de722e2018-07-04 11:01:37 +0000206 # Clean up admin security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200207 for security_group in cls.admin_security_groups:
208 cls._try_delete_resource(cls.delete_security_group,
209 security_group,
210 client=cls.admin_client)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000211
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000212 for subnetpool in cls.subnetpools:
213 cls._try_delete_resource(cls.client.delete_subnetpool,
214 subnetpool['id'])
215
216 for subnetpool in cls.admin_subnetpools:
217 cls._try_delete_resource(cls.admin_client.delete_subnetpool,
218 subnetpool['id'])
219
220 for address_scope in cls.address_scopes:
221 cls._try_delete_resource(cls.client.delete_address_scope,
222 address_scope['id'])
223
224 for address_scope in cls.admin_address_scopes:
225 cls._try_delete_resource(
226 cls.admin_client.delete_address_scope,
227 address_scope['id'])
228
Chandan Kumarc125fd12017-11-15 19:41:01 +0530229 for project in cls.projects:
230 cls._try_delete_resource(
231 cls.identity_admin_client.delete_project,
232 project['id'])
233
Sławek Kapłońskie100c4d2017-08-23 21:18:34 +0000234 # Clean up QoS rules
235 for qos_rule in cls.qos_rules:
236 cls._try_delete_resource(cls.admin_client.delete_qos_rule,
237 qos_rule['id'])
238 # Clean up QoS policies
239 # as all networks and ports are already removed, QoS policies
240 # shouldn't be "in use"
241 for qos_policy in cls.qos_policies:
242 cls._try_delete_resource(cls.admin_client.delete_qos_policy,
243 qos_policy['id'])
244
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700245 # Clean up log_objects
246 for log_object in cls.log_objects:
247 cls._try_delete_resource(cls.admin_client.delete_log,
248 log_object['id'])
249
Federico Ressiab286e42018-06-19 09:52:10 +0200250 for keypair in cls.keypairs:
251 cls._try_delete_resource(cls.delete_keypair, keypair)
252
Kailun Qineaaf9782018-12-20 04:45:01 +0800253 # Clean up network_segment_ranges
254 for network_segment_range in cls.network_segment_ranges:
255 cls._try_delete_resource(
256 cls.admin_client.delete_network_segment_range,
257 network_segment_range['id'])
258
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000259 super(BaseNetworkTest, cls).resource_cleanup()
260
261 @classmethod
262 def _try_delete_resource(cls, delete_callable, *args, **kwargs):
263 """Cleanup resources in case of test-failure
264
265 Some resources are explicitly deleted by the test.
266 If the test failed to delete a resource, this method will execute
267 the appropriate delete methods. Otherwise, the method ignores NotFound
268 exceptions thrown for resources that were correctly deleted by the
269 test.
270
271 :param delete_callable: delete method
272 :param args: arguments for delete method
273 :param kwargs: keyword arguments for delete method
274 """
275 try:
276 delete_callable(*args, **kwargs)
277 # if resource is not found, this means it was deleted in the test
278 except lib_exc.NotFound:
279 pass
280
281 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200282 def create_network(cls, network_name=None, client=None, external=None,
283 shared=None, provider_network_type=None,
284 provider_physical_network=None,
285 provider_segmentation_id=None, **kwargs):
286 """Create a network.
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000287
Federico Ressi61b564e2018-07-06 08:10:31 +0200288 When client is not provider and admin_client is attribute is not None
289 (for example when using BaseAdminNetworkTest base class) and using any
290 of the convenience parameters (external, shared, provider_network_type,
291 provider_physical_network and provider_segmentation_id) it silently
292 uses admin_client. If the network is not shared then it uses the same
293 project_id as regular client.
294
295 :param network_name: Human-readable name of the network
296
297 :param client: client to be used for connecting to network service
298
299 :param external: indicates whether the network has an external routing
300 facility that's not managed by the networking service.
301
302 :param shared: indicates whether this resource is shared across all
303 projects. By default, only administrative users can change this value.
304 If True and admin_client attribute is not None, then the network is
305 created under administrative project.
306
307 :param provider_network_type: the type of physical network that this
308 network should be mapped to. For example, 'flat', 'vlan', 'vxlan', or
309 'gre'. Valid values depend on a networking back-end.
310
311 :param provider_physical_network: the physical network where this
312 network should be implemented. The Networking API v2.0 does not provide
313 a way to list available physical networks. For example, the Open
314 vSwitch plug-in configuration file defines a symbolic name that maps to
315 specific bridges on each compute host.
316
317 :param provider_segmentation_id: The ID of the isolated segment on the
318 physical network. The network_type attribute defines the segmentation
319 model. For example, if the network_type value is 'vlan', this ID is a
320 vlan identifier. If the network_type value is 'gre', this ID is a gre
321 key.
322
323 :param **kwargs: extra parameters to be forwarded to network service
324 """
325
326 name = (network_name or kwargs.pop('name', None) or
327 data_utils.rand_name('test-network-'))
328
329 # translate convenience parameters
330 admin_client_required = False
331 if provider_network_type:
332 admin_client_required = True
333 kwargs['provider:network_type'] = provider_network_type
334 if provider_physical_network:
335 admin_client_required = True
336 kwargs['provider:physical_network'] = provider_physical_network
337 if provider_segmentation_id:
338 admin_client_required = True
339 kwargs['provider:segmentation_id'] = provider_segmentation_id
340 if external is not None:
341 admin_client_required = True
342 kwargs['router:external'] = bool(external)
343 if shared is not None:
344 admin_client_required = True
345 kwargs['shared'] = bool(shared)
346
347 if not client:
348 if admin_client_required and cls.admin_client:
349 # For convenience silently switch to admin client
350 client = cls.admin_client
351 if not shared:
352 # Keep this network visible from current project
353 project_id = (kwargs.get('project_id') or
354 kwargs.get('tenant_id') or
355 cls.client.tenant_id)
356 kwargs.update(project_id=project_id, tenant_id=project_id)
357 else:
358 # Use default client
359 client = cls.client
360
361 network = client.create_network(name=name, **kwargs)['network']
362 network['client'] = client
363 cls.networks.append(network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000364 return network
365
366 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200367 def delete_network(cls, network, client=None):
368 client = client or network.get('client') or cls.client
369 client.delete_network(network['id'])
370
371 @classmethod
372 def create_shared_network(cls, network_name=None, **kwargs):
373 return cls.create_network(name=network_name, shared=True, **kwargs)
Miguel Lavalle124378b2016-09-21 16:41:47 -0500374
375 @classmethod
376 def create_network_keystone_v3(cls, network_name=None, project_id=None,
377 tenant_id=None, client=None):
Federico Ressi61b564e2018-07-06 08:10:31 +0200378 params = {}
379 if project_id:
380 params['project_id'] = project_id
381 if tenant_id:
382 params['tenant_id'] = tenant_id
383 return cls.create_network(name=network_name, client=client, **params)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000384
385 @classmethod
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200386 def create_subnet(cls, network, gateway='', cidr=None, mask_bits=None,
Federico Ressi98f20ec2018-05-11 06:09:49 +0200387 ip_version=None, client=None, reserve_cidr=True,
388 **kwargs):
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200389 """Wrapper utility that returns a test subnet.
390
391 Convenient wrapper for client.create_subnet method. It reserves and
392 allocates CIDRs to avoid creating overlapping subnets.
393
394 :param network: network where to create the subnet
395 network['id'] must contain the ID of the network
396
397 :param gateway: gateway IP address
398 It can be a str or a netaddr.IPAddress
399 If gateway is not given, then it will use default address for
400 given subnet CIDR, like "192.168.0.1" for "192.168.0.0/24" CIDR
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200401 if gateway is given as None then no gateway will be assigned
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200402
403 :param cidr: CIDR of the subnet to create
404 It can be either None, a str or a netaddr.IPNetwork instance
405
406 :param mask_bits: CIDR prefix length
407 It can be either None or a numeric value.
408 If cidr parameter is given then mask_bits is used to determinate a
409 sequence of valid CIDR to use as generated.
410 Please see netaddr.IPNetwork.subnet method documentation[1]
411
412 :param ip_version: ip version of generated subnet CIDRs
413 It can be None, IP_VERSION_4 or IP_VERSION_6
414 It has to match given either given CIDR and gateway
415
416 :param ip_version: numeric value (either IP_VERSION_4 or IP_VERSION_6)
417 this value must match CIDR and gateway IP versions if any of them is
418 given
419
420 :param client: client to be used to connect to network service
421
Federico Ressi98f20ec2018-05-11 06:09:49 +0200422 :param reserve_cidr: if True then it reserves assigned CIDR to avoid
423 using the same CIDR for further subnets in the scope of the same
424 test case class
425
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200426 :param **kwargs: optional parameters to be forwarded to wrapped method
427
428 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
429 """
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000430
431 # allow tests to use admin client
432 if not client:
433 client = cls.client
434
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200435 if gateway:
436 gateway_ip = netaddr.IPAddress(gateway)
437 if ip_version:
438 if ip_version != gateway_ip.version:
439 raise ValueError(
440 "Gateway IP version doesn't match IP version")
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000441 else:
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200442 ip_version = gateway_ip.version
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200443 else:
444 ip_version = ip_version or cls._ip_version
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200445
446 for subnet_cidr in cls.get_subnet_cidrs(
447 ip_version=ip_version, cidr=cidr, mask_bits=mask_bits):
Federico Ressi98f20ec2018-05-11 06:09:49 +0200448 if gateway is not None:
449 kwargs['gateway_ip'] = str(gateway or (subnet_cidr.ip + 1))
Slawek Kaplonski21f53422018-11-02 16:02:09 +0100450 else:
451 kwargs['gateway_ip'] = None
Federico Ressi98f20ec2018-05-11 06:09:49 +0200452 try:
453 body = client.create_subnet(
454 network_id=network['id'],
455 cidr=str(subnet_cidr),
456 ip_version=subnet_cidr.version,
457 **kwargs)
458 break
459 except lib_exc.BadRequest as e:
460 if 'overlaps with another subnet' not in str(e):
461 raise
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000462 else:
463 message = 'Available CIDR for subnet creation could not be found'
464 raise ValueError(message)
465 subnet = body['subnet']
Kevin Bentonba3651c2017-09-01 17:13:01 -0700466 if client is cls.client:
467 cls.subnets.append(subnet)
468 else:
469 cls.admin_subnets.append(subnet)
Federico Ressi98f20ec2018-05-11 06:09:49 +0200470 if reserve_cidr:
471 cls.reserve_subnet_cidr(subnet_cidr)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000472 return subnet
473
474 @classmethod
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200475 def reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
476 """Reserve given subnet CIDR making sure it is not used by create_subnet
477
478 :param addr: the CIDR address to be reserved
479 It can be a str or netaddr.IPNetwork instance
480
481 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
482 parameters
483 """
484
485 if not cls.try_reserve_subnet_cidr(addr, **ipnetwork_kwargs):
486 raise ValueError('Subnet CIDR already reserved: %r'.format(
487 addr))
488
489 @classmethod
490 def try_reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
491 """Reserve given subnet CIDR if it hasn't been reserved before
492
493 :param addr: the CIDR address to be reserved
494 It can be a str or netaddr.IPNetwork instance
495
496 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
497 parameters
498
499 :return: True if it wasn't reserved before, False elsewhere.
500 """
501
502 subnet_cidr = netaddr.IPNetwork(addr, **ipnetwork_kwargs)
503 if subnet_cidr in cls.reserved_subnet_cidrs:
504 return False
505 else:
506 cls.reserved_subnet_cidrs.add(subnet_cidr)
507 return True
508
509 @classmethod
510 def get_subnet_cidrs(
511 cls, cidr=None, mask_bits=None, ip_version=None):
512 """Iterate over a sequence of unused subnet CIDR for IP version
513
514 :param cidr: CIDR of the subnet to create
515 It can be either None, a str or a netaddr.IPNetwork instance
516
517 :param mask_bits: CIDR prefix length
518 It can be either None or a numeric value.
519 If cidr parameter is given then mask_bits is used to determinate a
520 sequence of valid CIDR to use as generated.
521 Please see netaddr.IPNetwork.subnet method documentation[1]
522
523 :param ip_version: ip version of generated subnet CIDRs
524 It can be None, IP_VERSION_4 or IP_VERSION_6
525 It has to match given CIDR if given
526
527 :return: iterator over reserved CIDRs of type netaddr.IPNetwork
528
529 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
530 """
531
532 if cidr:
533 # Generate subnet CIDRs starting from given CIDR
534 # checking it is of requested IP version
535 cidr = netaddr.IPNetwork(cidr, version=ip_version)
536 else:
537 # Generate subnet CIDRs starting from configured values
538 ip_version = ip_version or cls._ip_version
539 if ip_version == const.IP_VERSION_4:
540 mask_bits = mask_bits or config.safe_get_config_value(
541 'network', 'project_network_mask_bits')
542 cidr = netaddr.IPNetwork(config.safe_get_config_value(
543 'network', 'project_network_cidr'))
544 elif ip_version == const.IP_VERSION_6:
545 mask_bits = config.safe_get_config_value(
546 'network', 'project_network_v6_mask_bits')
547 cidr = netaddr.IPNetwork(config.safe_get_config_value(
548 'network', 'project_network_v6_cidr'))
549 else:
550 raise ValueError('Invalid IP version: {!r}'.format(ip_version))
551
552 if mask_bits:
553 subnet_cidrs = cidr.subnet(mask_bits)
554 else:
555 subnet_cidrs = iter([cidr])
556
557 for subnet_cidr in subnet_cidrs:
558 if subnet_cidr not in cls.reserved_subnet_cidrs:
559 yield subnet_cidr
560
561 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000562 def create_port(cls, network, **kwargs):
563 """Wrapper utility that returns a test port."""
Edan Davidd75e48e2018-01-03 02:49:52 -0500564 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
565 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000566 body = cls.client.create_port(network_id=network['id'],
567 **kwargs)
568 port = body['port']
569 cls.ports.append(port)
570 return port
571
572 @classmethod
573 def update_port(cls, port, **kwargs):
574 """Wrapper utility that updates a test port."""
575 body = cls.client.update_port(port['id'],
576 **kwargs)
577 return body['port']
578
579 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300580 def _create_router_with_client(
581 cls, client, router_name=None, admin_state_up=False,
582 external_network_id=None, enable_snat=None, **kwargs
583 ):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000584 ext_gw_info = {}
585 if external_network_id:
586 ext_gw_info['network_id'] = external_network_id
YAMAMOTO Takashi9bd4f972017-06-20 12:49:30 +0900587 if enable_snat is not None:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000588 ext_gw_info['enable_snat'] = enable_snat
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300589 body = client.create_router(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000590 router_name, external_gateway_info=ext_gw_info,
591 admin_state_up=admin_state_up, **kwargs)
592 router = body['router']
593 cls.routers.append(router)
594 return router
595
596 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300597 def create_router(cls, *args, **kwargs):
598 return cls._create_router_with_client(cls.client, *args, **kwargs)
599
600 @classmethod
601 def create_admin_router(cls, *args, **kwargs):
rajat294495c042017-06-28 15:37:16 +0530602 return cls._create_router_with_client(cls.os_admin.network_client,
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300603 *args, **kwargs)
604
605 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200606 def create_floatingip(cls, external_network_id=None, port=None,
607 client=None, **kwargs):
608 """Creates a floating IP.
609
610 Create a floating IP and schedule it for later deletion.
611 If a client is passed, then it is used for deleting the IP too.
612
613 :param external_network_id: network ID where to create
614 By default this is 'CONF.network.public_network_id'.
615
616 :param port: port to bind floating IP to
617 This is translated to 'port_id=port['id']'
618 By default it is None.
619
620 :param client: network client to be used for creating and cleaning up
621 the floating IP.
622
623 :param **kwargs: additional creation parameters to be forwarded to
624 networking server.
625 """
626
627 client = client or cls.client
628 external_network_id = (external_network_id or
629 cls.external_network_id)
630
631 if port:
Federico Ressi47f6ae42018-09-24 16:19:14 +0200632 port_id = kwargs.setdefault('port_id', port['id'])
633 if port_id != port['id']:
634 message = "Port ID specified twice: {!s} != {!s}".format(
635 port_id, port['id'])
636 raise ValueError(message)
Federico Ressia69dcd52018-07-06 09:45:34 +0200637
638 fip = client.create_floatingip(external_network_id,
639 **kwargs)['floatingip']
640
641 # save client to be used later in cls.delete_floatingip
642 # for final cleanup
643 fip['client'] = client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000644 cls.floating_ips.append(fip)
645 return fip
646
647 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200648 def delete_floatingip(cls, floating_ip, client=None):
649 """Delete floating IP
650
651 :param client: Client to be used
652 If client is not given it will use the client used to create
653 the floating IP, or cls.client if unknown.
654 """
655
656 client = client or floating_ip.get('client') or cls.client
657 client.delete_floatingip(floating_ip['id'])
658
659 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200660 def create_port_forwarding(cls, fip_id, internal_port_id,
661 internal_port, external_port,
662 internal_ip_address=None, protocol="tcp",
663 client=None):
664 """Creates a port forwarding.
665
666 Create a port forwarding and schedule it for later deletion.
667 If a client is passed, then it is used for deleting the PF too.
668
669 :param fip_id: The ID of the floating IP address.
670
671 :param internal_port_id: The ID of the Neutron port associated to
672 the floating IP port forwarding.
673
674 :param internal_port: The TCP/UDP/other protocol port number of the
675 Neutron port fixed IP address associated to the floating ip
676 port forwarding.
677
678 :param external_port: The TCP/UDP/other protocol port number of
679 the port forwarding floating IP address.
680
681 :param internal_ip_address: The fixed IPv4 address of the Neutron
682 port associated to the floating IP port forwarding.
683
684 :param protocol: The IP protocol used in the floating IP port
685 forwarding.
686
687 :param client: network client to be used for creating and cleaning up
688 the floating IP port forwarding.
689 """
690
691 client = client or cls.client
692
693 pf = client.create_port_forwarding(
694 fip_id, internal_port_id, internal_port, external_port,
695 internal_ip_address, protocol)['port_forwarding']
696
697 # save ID of floating IP associated with port forwarding for final
698 # cleanup
699 pf['floatingip_id'] = fip_id
700
701 # save client to be used later in cls.delete_port_forwarding
702 # for final cleanup
703 pf['client'] = client
704 cls.port_forwardings.append(pf)
705 return pf
706
707 @classmethod
708 def delete_port_forwarding(cls, pf, client=None):
709 """Delete port forwarding
710
711 :param client: Client to be used
712 If client is not given it will use the client used to create
713 the port forwarding, or cls.client if unknown.
714 """
715
716 client = client or pf.get('client') or cls.client
717 client.delete_port_forwarding(pf['floatingip_id'], pf['id'])
718
719 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000720 def create_router_interface(cls, router_id, subnet_id):
721 """Wrapper utility that returns a router interface."""
722 interface = cls.client.add_router_interface_with_subnet_id(
723 router_id, subnet_id)
724 return interface
725
726 @classmethod
Sławek Kapłońskiff294062016-12-04 15:00:54 +0000727 def get_supported_qos_rule_types(cls):
728 body = cls.client.list_qos_rule_types()
729 return [rule_type['type'] for rule_type in body['rule_types']]
730
731 @classmethod
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +0200732 def create_qos_policy(cls, name, description=None, shared=False,
Hirofumi Ichihara39a6ee12017-08-23 13:55:12 +0900733 tenant_id=None, is_default=False):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000734 """Wrapper utility that returns a test QoS policy."""
735 body = cls.admin_client.create_qos_policy(
Hirofumi Ichihara39a6ee12017-08-23 13:55:12 +0900736 name, description, shared, tenant_id, is_default)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000737 qos_policy = body['policy']
738 cls.qos_policies.append(qos_policy)
739 return qos_policy
740
741 @classmethod
Sławek Kapłoński153f3452017-03-24 22:04:53 +0000742 def create_qos_bandwidth_limit_rule(cls, policy_id, max_kbps,
743 max_burst_kbps,
Chandan Kumarc125fd12017-11-15 19:41:01 +0530744 direction=const.EGRESS_DIRECTION):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000745 """Wrapper utility that returns a test QoS bandwidth limit rule."""
746 body = cls.admin_client.create_bandwidth_limit_rule(
Sławek Kapłoński153f3452017-03-24 22:04:53 +0000747 policy_id, max_kbps, max_burst_kbps, direction)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000748 qos_rule = body['bandwidth_limit_rule']
749 cls.qos_rules.append(qos_rule)
750 return qos_rule
751
752 @classmethod
Lajos Katona2f904652018-08-23 14:04:56 +0200753 def create_qos_minimum_bandwidth_rule(cls, policy_id, min_kbps,
754 direction=const.EGRESS_DIRECTION):
755 """Wrapper utility that creates and returns a QoS min bw rule."""
756 body = cls.admin_client.create_minimum_bandwidth_rule(
757 policy_id, direction, min_kbps)
758 qos_rule = body['minimum_bandwidth_rule']
759 cls.qos_rules.append(qos_rule)
760 return qos_rule
761
762 @classmethod
Jakub Libosvar83704832017-12-06 16:02:28 +0000763 def delete_router(cls, router, client=None):
764 client = client or cls.client
Aditya Vaja49819a72018-11-26 14:20:10 -0800765 if 'routes' in router:
766 client.remove_router_extra_routes(router['id'])
Jakub Libosvar83704832017-12-06 16:02:28 +0000767 body = client.list_router_interfaces(router['id'])
Chandan Kumarc125fd12017-11-15 19:41:01 +0530768 interfaces = [port for port in body['ports']
769 if port['device_owner'] in const.ROUTER_INTERFACE_OWNERS]
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000770 for i in interfaces:
771 try:
Jakub Libosvar83704832017-12-06 16:02:28 +0000772 client.remove_router_interface_with_subnet_id(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000773 router['id'], i['fixed_ips'][0]['subnet_id'])
774 except lib_exc.NotFound:
775 pass
Jakub Libosvar83704832017-12-06 16:02:28 +0000776 client.delete_router(router['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000777
778 @classmethod
779 def create_address_scope(cls, name, is_admin=False, **kwargs):
780 if is_admin:
781 body = cls.admin_client.create_address_scope(name=name, **kwargs)
782 cls.admin_address_scopes.append(body['address_scope'])
783 else:
784 body = cls.client.create_address_scope(name=name, **kwargs)
785 cls.address_scopes.append(body['address_scope'])
786 return body['address_scope']
787
788 @classmethod
789 def create_subnetpool(cls, name, is_admin=False, **kwargs):
790 if is_admin:
791 body = cls.admin_client.create_subnetpool(name, **kwargs)
792 cls.admin_subnetpools.append(body['subnetpool'])
793 else:
794 body = cls.client.create_subnetpool(name, **kwargs)
795 cls.subnetpools.append(body['subnetpool'])
796 return body['subnetpool']
797
Chandan Kumarc125fd12017-11-15 19:41:01 +0530798 @classmethod
799 def create_project(cls, name=None, description=None):
800 test_project = name or data_utils.rand_name('test_project_')
801 test_description = description or data_utils.rand_name('desc_')
802 project = cls.identity_admin_client.create_project(
803 name=test_project,
804 description=test_description)['project']
805 cls.projects.append(project)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000806 # Create a project will create a default security group.
Dongcan Ye2de722e2018-07-04 11:01:37 +0000807 sgs_list = cls.admin_client.list_security_groups(
808 tenant_id=project['id'])['security_groups']
Federico Ressi4c590d72018-10-10 14:01:08 +0200809 for security_group in sgs_list:
810 # Make sure delete_security_group method will use
811 # the admin client for this group
812 security_group['client'] = cls.admin_client
813 cls.security_groups.append(security_group)
Chandan Kumarc125fd12017-11-15 19:41:01 +0530814 return project
815
816 @classmethod
Federico Ressi4c590d72018-10-10 14:01:08 +0200817 def create_security_group(cls, name=None, project=None, client=None,
818 **kwargs):
819 if project:
820 client = client or cls.admin_client
821 project_id = kwargs.setdefault('project_id', project['id'])
822 tenant_id = kwargs.setdefault('tenant_id', project['id'])
823 if project_id != project['id'] or tenant_id != project['id']:
824 raise ValueError('Project ID specified multiple times')
825 else:
826 client = client or cls.client
827
828 name = name or data_utils.rand_name(cls.__name__)
829 security_group = client.create_security_group(name=name, **kwargs)[
830 'security_group']
831 security_group['client'] = client
832 cls.security_groups.append(security_group)
833 return security_group
834
835 @classmethod
836 def delete_security_group(cls, security_group, client=None):
837 client = client or security_group.get('client') or cls.client
838 client.delete_security_group(security_group['id'])
839
840 @classmethod
841 def create_security_group_rule(cls, security_group=None, project=None,
842 client=None, ip_version=None, **kwargs):
843 if project:
844 client = client or cls.admin_client
845 project_id = kwargs.setdefault('project_id', project['id'])
846 tenant_id = kwargs.setdefault('tenant_id', project['id'])
847 if project_id != project['id'] or tenant_id != project['id']:
848 raise ValueError('Project ID specified multiple times')
849
850 if 'security_group_id' not in kwargs:
851 security_group = (security_group or
852 cls.get_security_group(client=client))
853
854 if security_group:
855 client = client or security_group.get('client')
856 security_group_id = kwargs.setdefault('security_group_id',
857 security_group['id'])
858 if security_group_id != security_group['id']:
859 raise ValueError('Security group ID specified multiple times.')
860
861 ip_version = ip_version or cls._ip_version
862 default_params = (
863 constants.DEFAULT_SECURITY_GROUP_RULE_PARAMS[ip_version])
864 for key, value in default_params.items():
865 kwargs.setdefault(key, value)
866
867 client = client or cls.client
868 return client.create_security_group_rule(**kwargs)[
869 'security_group_rule']
870
871 @classmethod
872 def get_security_group(cls, name='default', client=None):
873 client = client or cls.client
874 security_groups = client.list_security_groups()['security_groups']
875 for security_group in security_groups:
876 if security_group['name'] == name:
877 return security_group
878 raise ValueError("No such security group named {!r}".format(name))
Chandan Kumarc125fd12017-11-15 19:41:01 +0530879
Federico Ressiab286e42018-06-19 09:52:10 +0200880 @classmethod
881 def create_keypair(cls, client=None, name=None, **kwargs):
882 client = client or cls.os_primary.keypairs_client
883 name = name or data_utils.rand_name('keypair-test')
884 keypair = client.create_keypair(name=name, **kwargs)['keypair']
885
886 # save client for later cleanup
887 keypair['client'] = client
888 cls.keypairs.append(keypair)
889 return keypair
890
891 @classmethod
892 def delete_keypair(cls, keypair, client=None):
893 client = (client or keypair.get('client') or
894 cls.os_primary.keypairs_client)
895 client.delete_keypair(keypair_name=keypair['name'])
896
Federico Ressi82e83e32018-07-03 14:19:55 +0200897 @classmethod
898 def create_trunk(cls, port=None, subports=None, client=None, **kwargs):
899 """Create network trunk
900
901 :param port: dictionary containing parent port ID (port['id'])
902 :param client: client to be used for connecting to networking service
903 :param **kwargs: extra parameters to be forwarded to network service
904
905 :returns: dictionary containing created trunk details
906 """
907 client = client or cls.client
908
909 if port:
910 kwargs['port_id'] = port['id']
911
912 trunk = client.create_trunk(subports=subports, **kwargs)['trunk']
913 # Save client reference for later deletion
914 trunk['client'] = client
915 cls.trunks.append(trunk)
916 return trunk
917
918 @classmethod
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800919 def delete_trunk(cls, trunk, client=None, detach_parent_port=True):
Federico Ressi82e83e32018-07-03 14:19:55 +0200920 """Delete network trunk
921
922 :param trunk: dictionary containing trunk ID (trunk['id'])
923
924 :param client: client to be used for connecting to networking service
925 """
926 client = client or trunk.get('client') or cls.client
927 trunk.update(client.show_trunk(trunk['id'])['trunk'])
928
929 if not trunk['admin_state_up']:
930 # Cannot touch trunk before admin_state_up is True
931 client.update_trunk(trunk['id'], admin_state_up=True)
932 if trunk['sub_ports']:
933 # Removes trunk ports before deleting it
934 cls._try_delete_resource(client.remove_subports, trunk['id'],
935 trunk['sub_ports'])
936
937 # we have to detach the interface from the server before
938 # the trunk can be deleted.
939 parent_port = {'id': trunk['port_id']}
940
941 def is_parent_port_detached():
942 parent_port.update(client.show_port(parent_port['id'])['port'])
943 return not parent_port['device_id']
944
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800945 if detach_parent_port and not is_parent_port_detached():
Federico Ressi82e83e32018-07-03 14:19:55 +0200946 # this could probably happen when trunk is deleted and parent port
947 # has been assigned to a VM that is still running. Here we are
948 # assuming that device_id points to such VM.
949 cls.os_primary.compute.InterfacesClient().delete_interface(
950 parent_port['device_id'], parent_port['id'])
951 utils.wait_until_true(is_parent_port_detached)
952
953 client.delete_trunk(trunk['id'])
954
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000955
956class BaseAdminNetworkTest(BaseNetworkTest):
957
958 credentials = ['primary', 'admin']
959
960 @classmethod
961 def setup_clients(cls):
962 super(BaseAdminNetworkTest, cls).setup_clients()
fumihiko kakumaa216fc12017-07-14 10:43:29 +0900963 cls.admin_client = cls.os_admin.network_client
Jakub Libosvarf5758012017-08-15 13:45:30 +0000964 cls.identity_admin_client = cls.os_admin.projects_client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000965
966 @classmethod
967 def create_metering_label(cls, name, description):
968 """Wrapper utility that returns a test metering label."""
969 body = cls.admin_client.create_metering_label(
970 description=description,
971 name=data_utils.rand_name("metering-label"))
972 metering_label = body['metering_label']
973 cls.metering_labels.append(metering_label)
974 return metering_label
975
976 @classmethod
977 def create_metering_label_rule(cls, remote_ip_prefix, direction,
978 metering_label_id):
979 """Wrapper utility that returns a test metering label rule."""
980 body = cls.admin_client.create_metering_label_rule(
981 remote_ip_prefix=remote_ip_prefix, direction=direction,
982 metering_label_id=metering_label_id)
983 metering_label_rule = body['metering_label_rule']
984 cls.metering_label_rules.append(metering_label_rule)
985 return metering_label_rule
986
987 @classmethod
Kailun Qineaaf9782018-12-20 04:45:01 +0800988 def create_network_segment_range(cls, name, shared,
989 project_id, network_type,
990 physical_network, minimum,
991 maximum):
992 """Wrapper utility that returns a test network segment range."""
993 network_segment_range_args = {'name': name,
994 'shared': shared,
995 'project_id': project_id,
996 'network_type': network_type,
997 'physical_network': physical_network,
998 'minimum': minimum,
999 'maximum': maximum}
1000 body = cls.admin_client.create_network_segment_range(
1001 **network_segment_range_args)
1002 network_segment_range = body['network_segment_range']
1003 cls.network_segment_ranges.append(network_segment_range)
1004 return network_segment_range
1005
1006 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001007 def create_flavor(cls, name, description, service_type):
1008 """Wrapper utility that returns a test flavor."""
1009 body = cls.admin_client.create_flavor(
1010 description=description, service_type=service_type,
1011 name=name)
1012 flavor = body['flavor']
1013 cls.flavors.append(flavor)
1014 return flavor
1015
1016 @classmethod
1017 def create_service_profile(cls, description, metainfo, driver):
1018 """Wrapper utility that returns a test service profile."""
1019 body = cls.admin_client.create_service_profile(
1020 driver=driver, metainfo=metainfo, description=description)
1021 service_profile = body['service_profile']
1022 cls.service_profiles.append(service_profile)
1023 return service_profile
1024
1025 @classmethod
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001026 def create_log(cls, name, description=None,
1027 resource_type='security_group', resource_id=None,
1028 target_id=None, event='ALL', enabled=True):
1029 """Wrapper utility that returns a test log object."""
1030 log_args = {'name': name,
1031 'description': description,
1032 'resource_type': resource_type,
1033 'resource_id': resource_id,
1034 'target_id': target_id,
1035 'event': event,
1036 'enabled': enabled}
1037 body = cls.admin_client.create_log(**log_args)
1038 log_object = body['log']
1039 cls.log_objects.append(log_object)
1040 return log_object
1041
1042 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001043 def get_unused_ip(cls, net_id, ip_version=None):
Gary Kotton011345f2016-06-15 08:04:31 -07001044 """Get an unused ip address in a allocation pool of net"""
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001045 body = cls.admin_client.list_ports(network_id=net_id)
1046 ports = body['ports']
1047 used_ips = []
1048 for port in ports:
1049 used_ips.extend(
1050 [fixed_ip['ip_address'] for fixed_ip in port['fixed_ips']])
1051 body = cls.admin_client.list_subnets(network_id=net_id)
1052 subnets = body['subnets']
1053
1054 for subnet in subnets:
1055 if ip_version and subnet['ip_version'] != ip_version:
1056 continue
1057 cidr = subnet['cidr']
1058 allocation_pools = subnet['allocation_pools']
1059 iterators = []
1060 if allocation_pools:
1061 for allocation_pool in allocation_pools:
1062 iterators.append(netaddr.iter_iprange(
1063 allocation_pool['start'], allocation_pool['end']))
1064 else:
1065 net = netaddr.IPNetwork(cidr)
1066
1067 def _iterip():
1068 for ip in net:
1069 if ip not in (net.network, net.broadcast):
1070 yield ip
1071 iterators.append(iter(_iterip()))
1072
1073 for iterator in iterators:
1074 for ip in iterator:
1075 if str(ip) not in used_ips:
1076 return str(ip)
1077
1078 message = (
1079 "net(%s) has no usable IP address in allocation pools" % net_id)
1080 raise exceptions.InvalidConfiguration(message)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001081
Lajos Katona2f904652018-08-23 14:04:56 +02001082 @classmethod
1083 def create_provider_network(cls, physnet_name, start_segmentation_id,
1084 max_attempts=30):
1085 segmentation_id = start_segmentation_id
Lajos Katona7eb67252019-01-14 12:55:35 +01001086 for attempts in range(max_attempts):
Lajos Katona2f904652018-08-23 14:04:56 +02001087 try:
Lajos Katona7eb67252019-01-14 12:55:35 +01001088 return cls.create_network(
Lajos Katona2f904652018-08-23 14:04:56 +02001089 name=data_utils.rand_name('test_net'),
1090 shared=True,
1091 provider_network_type='vlan',
1092 provider_physical_network=physnet_name,
1093 provider_segmentation_id=segmentation_id)
Lajos Katona2f904652018-08-23 14:04:56 +02001094 except lib_exc.Conflict:
Lajos Katona2f904652018-08-23 14:04:56 +02001095 segmentation_id += 1
1096 if segmentation_id > 4095:
1097 raise lib_exc.TempestException(
1098 "No free segmentation id was found for provider "
1099 "network creation!")
1100 time.sleep(CONF.network.build_interval)
Lajos Katona7eb67252019-01-14 12:55:35 +01001101 LOG.exception("Failed to create provider network after "
1102 "%d attempts", max_attempts)
1103 raise lib_exc.TimeoutException
Lajos Katona2f904652018-08-23 14:04:56 +02001104
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001105
Sławek Kapłońskiff294062016-12-04 15:00:54 +00001106def require_qos_rule_type(rule_type):
1107 def decorator(f):
1108 @functools.wraps(f)
1109 def wrapper(self, *func_args, **func_kwargs):
1110 if rule_type not in self.get_supported_qos_rule_types():
1111 raise self.skipException(
1112 "%s rule type is required." % rule_type)
1113 return f(self, *func_args, **func_kwargs)
1114 return wrapper
1115 return decorator
1116
1117
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001118def _require_sorting(f):
1119 @functools.wraps(f)
1120 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301121 if not tutils.is_extension_enabled("sorting", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001122 self.skipTest('Sorting feature is required')
1123 return f(self, *args, **kwargs)
1124 return inner
1125
1126
1127def _require_pagination(f):
1128 @functools.wraps(f)
1129 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301130 if not tutils.is_extension_enabled("pagination", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001131 self.skipTest('Pagination feature is required')
1132 return f(self, *args, **kwargs)
1133 return inner
1134
1135
1136class BaseSearchCriteriaTest(BaseNetworkTest):
1137
1138 # This should be defined by subclasses to reflect resource name to test
1139 resource = None
1140
Armando Migliaccio57581c62016-07-01 10:13:19 -07001141 field = 'name'
1142
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001143 # NOTE(ihrachys): some names, like those starting with an underscore (_)
1144 # are sorted differently depending on whether the plugin implements native
1145 # sorting support, or not. So we avoid any such cases here, sticking to
1146 # alphanumeric. Also test a case when there are multiple resources with the
1147 # same name
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001148 resource_names = ('test1', 'abc1', 'test10', '123test') + ('test1',)
1149
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001150 force_tenant_isolation = True
1151
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001152 list_kwargs = {}
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001153
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001154 list_as_admin = False
1155
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001156 def assertSameOrder(self, original, actual):
1157 # gracefully handle iterators passed
1158 original = list(original)
1159 actual = list(actual)
1160 self.assertEqual(len(original), len(actual))
1161 for expected, res in zip(original, actual):
Armando Migliaccio57581c62016-07-01 10:13:19 -07001162 self.assertEqual(expected[self.field], res[self.field])
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001163
1164 @utils.classproperty
1165 def plural_name(self):
1166 return '%ss' % self.resource
1167
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001168 @property
1169 def list_client(self):
1170 return self.admin_client if self.list_as_admin else self.client
1171
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001172 def list_method(self, *args, **kwargs):
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001173 method = getattr(self.list_client, 'list_%s' % self.plural_name)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001174 kwargs.update(self.list_kwargs)
1175 return method(*args, **kwargs)
1176
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001177 def get_bare_url(self, url):
1178 base_url = self.client.base_url
1179 self.assertTrue(url.startswith(base_url))
1180 return url[len(base_url):]
1181
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001182 @classmethod
1183 def _extract_resources(cls, body):
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001184 return body[cls.plural_name]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001185
1186 def _test_list_sorts(self, direction):
1187 sort_args = {
1188 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001189 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001190 }
1191 body = self.list_method(**sort_args)
1192 resources = self._extract_resources(body)
1193 self.assertNotEmpty(
1194 resources, "%s list returned is empty" % self.resource)
Armando Migliaccio57581c62016-07-01 10:13:19 -07001195 retrieved_names = [res[self.field] for res in resources]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001196 expected = sorted(retrieved_names)
1197 if direction == constants.SORT_DIRECTION_DESC:
1198 expected = list(reversed(expected))
1199 self.assertEqual(expected, retrieved_names)
1200
1201 @_require_sorting
1202 def _test_list_sorts_asc(self):
1203 self._test_list_sorts(constants.SORT_DIRECTION_ASC)
1204
1205 @_require_sorting
1206 def _test_list_sorts_desc(self):
1207 self._test_list_sorts(constants.SORT_DIRECTION_DESC)
1208
1209 @_require_pagination
1210 def _test_list_pagination(self):
1211 for limit in range(1, len(self.resource_names) + 1):
1212 pagination_args = {
1213 'limit': limit,
1214 }
1215 body = self.list_method(**pagination_args)
1216 resources = self._extract_resources(body)
1217 self.assertEqual(limit, len(resources))
1218
1219 @_require_pagination
1220 def _test_list_no_pagination_limit_0(self):
1221 pagination_args = {
1222 'limit': 0,
1223 }
1224 body = self.list_method(**pagination_args)
1225 resources = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001226 self.assertGreaterEqual(len(resources), len(self.resource_names))
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001227
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001228 def _test_list_pagination_iteratively(self, lister):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001229 # first, collect all resources for later comparison
1230 sort_args = {
1231 'sort_dir': constants.SORT_DIRECTION_ASC,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001232 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001233 }
1234 body = self.list_method(**sort_args)
1235 expected_resources = self._extract_resources(body)
1236 self.assertNotEmpty(expected_resources)
1237
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001238 resources = lister(
1239 len(expected_resources), sort_args
1240 )
1241
1242 # finally, compare that the list retrieved in one go is identical to
1243 # the one containing pagination results
1244 self.assertSameOrder(expected_resources, resources)
1245
1246 def _list_all_with_marker(self, niterations, sort_args):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001247 # paginate resources one by one, using last fetched resource as a
1248 # marker
1249 resources = []
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001250 for i in range(niterations):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001251 pagination_args = sort_args.copy()
1252 pagination_args['limit'] = 1
1253 if resources:
1254 pagination_args['marker'] = resources[-1]['id']
1255 body = self.list_method(**pagination_args)
1256 resources_ = self._extract_resources(body)
1257 self.assertEqual(1, len(resources_))
1258 resources.extend(resources_)
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001259 return resources
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001260
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001261 @_require_pagination
1262 @_require_sorting
1263 def _test_list_pagination_with_marker(self):
1264 self._test_list_pagination_iteratively(self._list_all_with_marker)
1265
1266 def _list_all_with_hrefs(self, niterations, sort_args):
1267 # paginate resources one by one, using next href links
1268 resources = []
1269 prev_links = {}
1270
1271 for i in range(niterations):
1272 if prev_links:
1273 uri = self.get_bare_url(prev_links['next'])
1274 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001275 sort_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001276 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001277 self.plural_name, limit=1, **sort_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001278 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001279 self.plural_name, uri
1280 )
1281 resources_ = self._extract_resources(body)
1282 self.assertEqual(1, len(resources_))
1283 resources.extend(resources_)
1284
1285 # The last element is empty and does not contain 'next' link
1286 uri = self.get_bare_url(prev_links['next'])
1287 prev_links, body = self.client.get_uri_with_links(
1288 self.plural_name, uri
1289 )
1290 self.assertNotIn('next', prev_links)
1291
1292 # Now walk backwards and compare results
1293 resources2 = []
1294 for i in range(niterations):
1295 uri = self.get_bare_url(prev_links['previous'])
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001296 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001297 self.plural_name, uri
1298 )
1299 resources_ = self._extract_resources(body)
1300 self.assertEqual(1, len(resources_))
1301 resources2.extend(resources_)
1302
1303 self.assertSameOrder(resources, reversed(resources2))
1304
1305 return resources
1306
1307 @_require_pagination
1308 @_require_sorting
1309 def _test_list_pagination_with_href_links(self):
1310 self._test_list_pagination_iteratively(self._list_all_with_hrefs)
1311
1312 @_require_pagination
1313 @_require_sorting
1314 def _test_list_pagination_page_reverse_with_href_links(
1315 self, direction=constants.SORT_DIRECTION_ASC):
1316 pagination_args = {
1317 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001318 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001319 }
1320 body = self.list_method(**pagination_args)
1321 expected_resources = self._extract_resources(body)
1322
1323 page_size = 2
1324 pagination_args['limit'] = page_size
1325
1326 prev_links = {}
1327 resources = []
1328 num_resources = len(expected_resources)
1329 niterations = int(math.ceil(float(num_resources) / page_size))
1330 for i in range(niterations):
1331 if prev_links:
1332 uri = self.get_bare_url(prev_links['previous'])
1333 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001334 pagination_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001335 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001336 self.plural_name, page_reverse=True, **pagination_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001337 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001338 self.plural_name, uri
1339 )
1340 resources_ = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001341 self.assertGreaterEqual(page_size, len(resources_))
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001342 resources.extend(reversed(resources_))
1343
1344 self.assertSameOrder(expected_resources, reversed(resources))
1345
1346 @_require_pagination
1347 @_require_sorting
1348 def _test_list_pagination_page_reverse_asc(self):
1349 self._test_list_pagination_page_reverse(
1350 direction=constants.SORT_DIRECTION_ASC)
1351
1352 @_require_pagination
1353 @_require_sorting
1354 def _test_list_pagination_page_reverse_desc(self):
1355 self._test_list_pagination_page_reverse(
1356 direction=constants.SORT_DIRECTION_DESC)
1357
1358 def _test_list_pagination_page_reverse(self, direction):
1359 pagination_args = {
1360 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001361 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001362 'limit': 3,
1363 }
1364 body = self.list_method(**pagination_args)
1365 expected_resources = self._extract_resources(body)
1366
1367 pagination_args['limit'] -= 1
1368 pagination_args['marker'] = expected_resources[-1]['id']
1369 pagination_args['page_reverse'] = True
1370 body = self.list_method(**pagination_args)
1371
1372 self.assertSameOrder(
1373 # the last entry is not included in 2nd result when used as a
1374 # marker
1375 expected_resources[:-1],
1376 self._extract_resources(body))
Victor Morales1be97b42016-09-05 08:50:06 -05001377
Hongbin Lu54f55922018-07-12 19:05:39 +00001378 @tutils.requires_ext(extension="filter-validation", service="network")
1379 def _test_list_validation_filters(
1380 self, validation_args, filter_is_valid=True):
1381 if not filter_is_valid:
1382 self.assertRaises(lib_exc.BadRequest, self.list_method,
1383 **validation_args)
1384 else:
1385 body = self.list_method(**validation_args)
1386 resources = self._extract_resources(body)
1387 for resource in resources:
1388 self.assertIn(resource['name'], self.resource_names)