blob: d63dec8f19cfe179ea2167c63f094aebd4df5403 [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 = []
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200140 cls.conntrack_helpers = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000141
142 @classmethod
143 def resource_cleanup(cls):
144 if CONF.service_available.neutron:
Federico Ressi82e83e32018-07-03 14:19:55 +0200145 # Clean up trunks
146 for trunk in cls.trunks:
147 cls._try_delete_resource(cls.delete_trunk, trunk)
148
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200149 # Clean up port forwardings
150 for pf in cls.port_forwardings:
151 cls._try_delete_resource(cls.delete_port_forwarding, pf)
152
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000153 # Clean up floating IPs
154 for floating_ip in cls.floating_ips:
Federico Ressia69dcd52018-07-06 09:45:34 +0200155 cls._try_delete_resource(cls.delete_floatingip, floating_ip)
156
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200157 # Clean up conntrack helpers
158 for cth in cls.conntrack_helpers:
159 cls._try_delete_resource(cls.delete_conntrack_helper, cth)
160
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000161 # Clean up routers
162 for router in cls.routers:
163 cls._try_delete_resource(cls.delete_router,
164 router)
165 # Clean up metering label rules
166 for metering_label_rule in cls.metering_label_rules:
167 cls._try_delete_resource(
168 cls.admin_client.delete_metering_label_rule,
169 metering_label_rule['id'])
170 # Clean up metering labels
171 for metering_label in cls.metering_labels:
172 cls._try_delete_resource(
173 cls.admin_client.delete_metering_label,
174 metering_label['id'])
175 # Clean up flavors
176 for flavor in cls.flavors:
177 cls._try_delete_resource(
178 cls.admin_client.delete_flavor,
179 flavor['id'])
180 # Clean up service profiles
181 for service_profile in cls.service_profiles:
182 cls._try_delete_resource(
183 cls.admin_client.delete_service_profile,
184 service_profile['id'])
185 # Clean up ports
186 for port in cls.ports:
187 cls._try_delete_resource(cls.client.delete_port,
188 port['id'])
189 # Clean up subnets
190 for subnet in cls.subnets:
191 cls._try_delete_resource(cls.client.delete_subnet,
192 subnet['id'])
Kevin Bentonba3651c2017-09-01 17:13:01 -0700193 # Clean up admin subnets
194 for subnet in cls.admin_subnets:
195 cls._try_delete_resource(cls.admin_client.delete_subnet,
196 subnet['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000197 # Clean up networks
198 for network in cls.networks:
Federico Ressi61b564e2018-07-06 08:10:31 +0200199 cls._try_delete_resource(cls.delete_network, network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000200
Miguel Lavalle124378b2016-09-21 16:41:47 -0500201 # Clean up admin networks
202 for network in cls.admin_networks:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000203 cls._try_delete_resource(cls.admin_client.delete_network,
204 network['id'])
205
Itzik Brownbac51dc2016-10-31 12:25:04 +0000206 # Clean up security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200207 for security_group in cls.security_groups:
208 cls._try_delete_resource(cls.delete_security_group,
209 security_group)
Itzik Brownbac51dc2016-10-31 12:25:04 +0000210
Dongcan Ye2de722e2018-07-04 11:01:37 +0000211 # Clean up admin security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200212 for security_group in cls.admin_security_groups:
213 cls._try_delete_resource(cls.delete_security_group,
214 security_group,
215 client=cls.admin_client)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000216
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000217 for subnetpool in cls.subnetpools:
218 cls._try_delete_resource(cls.client.delete_subnetpool,
219 subnetpool['id'])
220
221 for subnetpool in cls.admin_subnetpools:
222 cls._try_delete_resource(cls.admin_client.delete_subnetpool,
223 subnetpool['id'])
224
225 for address_scope in cls.address_scopes:
226 cls._try_delete_resource(cls.client.delete_address_scope,
227 address_scope['id'])
228
229 for address_scope in cls.admin_address_scopes:
230 cls._try_delete_resource(
231 cls.admin_client.delete_address_scope,
232 address_scope['id'])
233
Chandan Kumarc125fd12017-11-15 19:41:01 +0530234 for project in cls.projects:
235 cls._try_delete_resource(
236 cls.identity_admin_client.delete_project,
237 project['id'])
238
Sławek Kapłońskie100c4d2017-08-23 21:18:34 +0000239 # Clean up QoS rules
240 for qos_rule in cls.qos_rules:
241 cls._try_delete_resource(cls.admin_client.delete_qos_rule,
242 qos_rule['id'])
243 # Clean up QoS policies
244 # as all networks and ports are already removed, QoS policies
245 # shouldn't be "in use"
246 for qos_policy in cls.qos_policies:
247 cls._try_delete_resource(cls.admin_client.delete_qos_policy,
248 qos_policy['id'])
249
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700250 # Clean up log_objects
251 for log_object in cls.log_objects:
252 cls._try_delete_resource(cls.admin_client.delete_log,
253 log_object['id'])
254
Federico Ressiab286e42018-06-19 09:52:10 +0200255 for keypair in cls.keypairs:
256 cls._try_delete_resource(cls.delete_keypair, keypair)
257
Kailun Qineaaf9782018-12-20 04:45:01 +0800258 # Clean up network_segment_ranges
259 for network_segment_range in cls.network_segment_ranges:
260 cls._try_delete_resource(
261 cls.admin_client.delete_network_segment_range,
262 network_segment_range['id'])
263
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000264 super(BaseNetworkTest, cls).resource_cleanup()
265
266 @classmethod
267 def _try_delete_resource(cls, delete_callable, *args, **kwargs):
268 """Cleanup resources in case of test-failure
269
270 Some resources are explicitly deleted by the test.
271 If the test failed to delete a resource, this method will execute
272 the appropriate delete methods. Otherwise, the method ignores NotFound
273 exceptions thrown for resources that were correctly deleted by the
274 test.
275
276 :param delete_callable: delete method
277 :param args: arguments for delete method
278 :param kwargs: keyword arguments for delete method
279 """
280 try:
281 delete_callable(*args, **kwargs)
282 # if resource is not found, this means it was deleted in the test
283 except lib_exc.NotFound:
284 pass
285
286 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200287 def create_network(cls, network_name=None, client=None, external=None,
288 shared=None, provider_network_type=None,
289 provider_physical_network=None,
290 provider_segmentation_id=None, **kwargs):
291 """Create a network.
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000292
Federico Ressi61b564e2018-07-06 08:10:31 +0200293 When client is not provider and admin_client is attribute is not None
294 (for example when using BaseAdminNetworkTest base class) and using any
295 of the convenience parameters (external, shared, provider_network_type,
296 provider_physical_network and provider_segmentation_id) it silently
297 uses admin_client. If the network is not shared then it uses the same
298 project_id as regular client.
299
300 :param network_name: Human-readable name of the network
301
302 :param client: client to be used for connecting to network service
303
304 :param external: indicates whether the network has an external routing
305 facility that's not managed by the networking service.
306
307 :param shared: indicates whether this resource is shared across all
308 projects. By default, only administrative users can change this value.
309 If True and admin_client attribute is not None, then the network is
310 created under administrative project.
311
312 :param provider_network_type: the type of physical network that this
313 network should be mapped to. For example, 'flat', 'vlan', 'vxlan', or
314 'gre'. Valid values depend on a networking back-end.
315
316 :param provider_physical_network: the physical network where this
317 network should be implemented. The Networking API v2.0 does not provide
318 a way to list available physical networks. For example, the Open
319 vSwitch plug-in configuration file defines a symbolic name that maps to
320 specific bridges on each compute host.
321
322 :param provider_segmentation_id: The ID of the isolated segment on the
323 physical network. The network_type attribute defines the segmentation
324 model. For example, if the network_type value is 'vlan', this ID is a
325 vlan identifier. If the network_type value is 'gre', this ID is a gre
326 key.
327
328 :param **kwargs: extra parameters to be forwarded to network service
329 """
330
331 name = (network_name or kwargs.pop('name', None) or
332 data_utils.rand_name('test-network-'))
333
334 # translate convenience parameters
335 admin_client_required = False
336 if provider_network_type:
337 admin_client_required = True
338 kwargs['provider:network_type'] = provider_network_type
339 if provider_physical_network:
340 admin_client_required = True
341 kwargs['provider:physical_network'] = provider_physical_network
342 if provider_segmentation_id:
343 admin_client_required = True
344 kwargs['provider:segmentation_id'] = provider_segmentation_id
345 if external is not None:
346 admin_client_required = True
347 kwargs['router:external'] = bool(external)
348 if shared is not None:
349 admin_client_required = True
350 kwargs['shared'] = bool(shared)
351
352 if not client:
353 if admin_client_required and cls.admin_client:
354 # For convenience silently switch to admin client
355 client = cls.admin_client
356 if not shared:
357 # Keep this network visible from current project
358 project_id = (kwargs.get('project_id') or
359 kwargs.get('tenant_id') or
360 cls.client.tenant_id)
361 kwargs.update(project_id=project_id, tenant_id=project_id)
362 else:
363 # Use default client
364 client = cls.client
365
366 network = client.create_network(name=name, **kwargs)['network']
367 network['client'] = client
368 cls.networks.append(network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000369 return network
370
371 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200372 def delete_network(cls, network, client=None):
373 client = client or network.get('client') or cls.client
374 client.delete_network(network['id'])
375
376 @classmethod
377 def create_shared_network(cls, network_name=None, **kwargs):
378 return cls.create_network(name=network_name, shared=True, **kwargs)
Miguel Lavalle124378b2016-09-21 16:41:47 -0500379
380 @classmethod
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200381 def create_subnet(cls, network, gateway='', cidr=None, mask_bits=None,
Federico Ressi98f20ec2018-05-11 06:09:49 +0200382 ip_version=None, client=None, reserve_cidr=True,
383 **kwargs):
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200384 """Wrapper utility that returns a test subnet.
385
386 Convenient wrapper for client.create_subnet method. It reserves and
387 allocates CIDRs to avoid creating overlapping subnets.
388
389 :param network: network where to create the subnet
390 network['id'] must contain the ID of the network
391
392 :param gateway: gateway IP address
393 It can be a str or a netaddr.IPAddress
394 If gateway is not given, then it will use default address for
395 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 +0200396 if gateway is given as None then no gateway will be assigned
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200397
398 :param cidr: CIDR of the subnet to create
399 It can be either None, a str or a netaddr.IPNetwork instance
400
401 :param mask_bits: CIDR prefix length
402 It can be either None or a numeric value.
403 If cidr parameter is given then mask_bits is used to determinate a
404 sequence of valid CIDR to use as generated.
405 Please see netaddr.IPNetwork.subnet method documentation[1]
406
407 :param ip_version: ip version of generated subnet CIDRs
408 It can be None, IP_VERSION_4 or IP_VERSION_6
409 It has to match given either given CIDR and gateway
410
411 :param ip_version: numeric value (either IP_VERSION_4 or IP_VERSION_6)
412 this value must match CIDR and gateway IP versions if any of them is
413 given
414
415 :param client: client to be used to connect to network service
416
Federico Ressi98f20ec2018-05-11 06:09:49 +0200417 :param reserve_cidr: if True then it reserves assigned CIDR to avoid
418 using the same CIDR for further subnets in the scope of the same
419 test case class
420
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200421 :param **kwargs: optional parameters to be forwarded to wrapped method
422
423 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
424 """
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000425
426 # allow tests to use admin client
427 if not client:
428 client = cls.client
429
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200430 if gateway:
431 gateway_ip = netaddr.IPAddress(gateway)
432 if ip_version:
433 if ip_version != gateway_ip.version:
434 raise ValueError(
435 "Gateway IP version doesn't match IP version")
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000436 else:
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200437 ip_version = gateway_ip.version
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200438 else:
439 ip_version = ip_version or cls._ip_version
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200440
441 for subnet_cidr in cls.get_subnet_cidrs(
442 ip_version=ip_version, cidr=cidr, mask_bits=mask_bits):
Federico Ressi98f20ec2018-05-11 06:09:49 +0200443 if gateway is not None:
444 kwargs['gateway_ip'] = str(gateway or (subnet_cidr.ip + 1))
Slawek Kaplonski21f53422018-11-02 16:02:09 +0100445 else:
446 kwargs['gateway_ip'] = None
Federico Ressi98f20ec2018-05-11 06:09:49 +0200447 try:
448 body = client.create_subnet(
449 network_id=network['id'],
450 cidr=str(subnet_cidr),
451 ip_version=subnet_cidr.version,
452 **kwargs)
453 break
454 except lib_exc.BadRequest as e:
455 if 'overlaps with another subnet' not in str(e):
456 raise
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000457 else:
458 message = 'Available CIDR for subnet creation could not be found'
459 raise ValueError(message)
460 subnet = body['subnet']
Kevin Bentonba3651c2017-09-01 17:13:01 -0700461 if client is cls.client:
462 cls.subnets.append(subnet)
463 else:
464 cls.admin_subnets.append(subnet)
Federico Ressi98f20ec2018-05-11 06:09:49 +0200465 if reserve_cidr:
466 cls.reserve_subnet_cidr(subnet_cidr)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000467 return subnet
468
469 @classmethod
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200470 def reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
471 """Reserve given subnet CIDR making sure it is not used by create_subnet
472
473 :param addr: the CIDR address to be reserved
474 It can be a str or netaddr.IPNetwork instance
475
476 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
477 parameters
478 """
479
480 if not cls.try_reserve_subnet_cidr(addr, **ipnetwork_kwargs):
Bernard Cafarellic3bec862020-09-10 13:59:49 +0200481 raise ValueError('Subnet CIDR already reserved: {0!r}'.format(
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200482 addr))
483
484 @classmethod
485 def try_reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
486 """Reserve given subnet CIDR if it hasn't been reserved before
487
488 :param addr: the CIDR address to be reserved
489 It can be a str or netaddr.IPNetwork instance
490
491 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
492 parameters
493
494 :return: True if it wasn't reserved before, False elsewhere.
495 """
496
497 subnet_cidr = netaddr.IPNetwork(addr, **ipnetwork_kwargs)
498 if subnet_cidr in cls.reserved_subnet_cidrs:
499 return False
500 else:
501 cls.reserved_subnet_cidrs.add(subnet_cidr)
502 return True
503
504 @classmethod
505 def get_subnet_cidrs(
506 cls, cidr=None, mask_bits=None, ip_version=None):
507 """Iterate over a sequence of unused subnet CIDR for IP version
508
509 :param cidr: CIDR of the subnet to create
510 It can be either None, a str or a netaddr.IPNetwork instance
511
512 :param mask_bits: CIDR prefix length
513 It can be either None or a numeric value.
514 If cidr parameter is given then mask_bits is used to determinate a
515 sequence of valid CIDR to use as generated.
516 Please see netaddr.IPNetwork.subnet method documentation[1]
517
518 :param ip_version: ip version of generated subnet CIDRs
519 It can be None, IP_VERSION_4 or IP_VERSION_6
520 It has to match given CIDR if given
521
522 :return: iterator over reserved CIDRs of type netaddr.IPNetwork
523
524 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
525 """
526
527 if cidr:
528 # Generate subnet CIDRs starting from given CIDR
529 # checking it is of requested IP version
530 cidr = netaddr.IPNetwork(cidr, version=ip_version)
531 else:
532 # Generate subnet CIDRs starting from configured values
533 ip_version = ip_version or cls._ip_version
534 if ip_version == const.IP_VERSION_4:
535 mask_bits = mask_bits or config.safe_get_config_value(
536 'network', 'project_network_mask_bits')
537 cidr = netaddr.IPNetwork(config.safe_get_config_value(
538 'network', 'project_network_cidr'))
539 elif ip_version == const.IP_VERSION_6:
540 mask_bits = config.safe_get_config_value(
541 'network', 'project_network_v6_mask_bits')
542 cidr = netaddr.IPNetwork(config.safe_get_config_value(
543 'network', 'project_network_v6_cidr'))
544 else:
545 raise ValueError('Invalid IP version: {!r}'.format(ip_version))
546
547 if mask_bits:
548 subnet_cidrs = cidr.subnet(mask_bits)
549 else:
550 subnet_cidrs = iter([cidr])
551
552 for subnet_cidr in subnet_cidrs:
553 if subnet_cidr not in cls.reserved_subnet_cidrs:
554 yield subnet_cidr
555
556 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000557 def create_port(cls, network, **kwargs):
558 """Wrapper utility that returns a test port."""
Edan Davidd75e48e2018-01-03 02:49:52 -0500559 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
560 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
Glenn Van de Water5d9b1402020-09-16 15:14:14 +0200561 if CONF.network.port_profile and 'binding:profile' not in kwargs:
562 kwargs['binding:profile'] = CONF.network.port_profile
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000563 body = cls.client.create_port(network_id=network['id'],
564 **kwargs)
565 port = body['port']
566 cls.ports.append(port)
567 return port
568
569 @classmethod
570 def update_port(cls, port, **kwargs):
571 """Wrapper utility that updates a test port."""
572 body = cls.client.update_port(port['id'],
573 **kwargs)
574 return body['port']
575
576 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300577 def _create_router_with_client(
578 cls, client, router_name=None, admin_state_up=False,
579 external_network_id=None, enable_snat=None, **kwargs
580 ):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000581 ext_gw_info = {}
582 if external_network_id:
583 ext_gw_info['network_id'] = external_network_id
YAMAMOTO Takashi9bd4f972017-06-20 12:49:30 +0900584 if enable_snat is not None:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000585 ext_gw_info['enable_snat'] = enable_snat
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300586 body = client.create_router(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000587 router_name, external_gateway_info=ext_gw_info,
588 admin_state_up=admin_state_up, **kwargs)
589 router = body['router']
590 cls.routers.append(router)
591 return router
592
593 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300594 def create_router(cls, *args, **kwargs):
595 return cls._create_router_with_client(cls.client, *args, **kwargs)
596
597 @classmethod
598 def create_admin_router(cls, *args, **kwargs):
rajat294495c042017-06-28 15:37:16 +0530599 return cls._create_router_with_client(cls.os_admin.network_client,
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300600 *args, **kwargs)
601
602 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200603 def create_floatingip(cls, external_network_id=None, port=None,
604 client=None, **kwargs):
605 """Creates a floating IP.
606
607 Create a floating IP and schedule it for later deletion.
608 If a client is passed, then it is used for deleting the IP too.
609
610 :param external_network_id: network ID where to create
611 By default this is 'CONF.network.public_network_id'.
612
613 :param port: port to bind floating IP to
614 This is translated to 'port_id=port['id']'
615 By default it is None.
616
617 :param client: network client to be used for creating and cleaning up
618 the floating IP.
619
620 :param **kwargs: additional creation parameters to be forwarded to
621 networking server.
622 """
623
624 client = client or cls.client
625 external_network_id = (external_network_id or
626 cls.external_network_id)
627
628 if port:
Federico Ressi47f6ae42018-09-24 16:19:14 +0200629 port_id = kwargs.setdefault('port_id', port['id'])
630 if port_id != port['id']:
631 message = "Port ID specified twice: {!s} != {!s}".format(
632 port_id, port['id'])
633 raise ValueError(message)
Federico Ressia69dcd52018-07-06 09:45:34 +0200634
635 fip = client.create_floatingip(external_network_id,
636 **kwargs)['floatingip']
637
638 # save client to be used later in cls.delete_floatingip
639 # for final cleanup
640 fip['client'] = client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000641 cls.floating_ips.append(fip)
642 return fip
643
644 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200645 def delete_floatingip(cls, floating_ip, client=None):
646 """Delete floating IP
647
648 :param client: Client to be used
649 If client is not given it will use the client used to create
650 the floating IP, or cls.client if unknown.
651 """
652
653 client = client or floating_ip.get('client') or cls.client
654 client.delete_floatingip(floating_ip['id'])
655
656 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200657 def create_port_forwarding(cls, fip_id, internal_port_id,
658 internal_port, external_port,
659 internal_ip_address=None, protocol="tcp",
660 client=None):
661 """Creates a port forwarding.
662
663 Create a port forwarding and schedule it for later deletion.
664 If a client is passed, then it is used for deleting the PF too.
665
666 :param fip_id: The ID of the floating IP address.
667
668 :param internal_port_id: The ID of the Neutron port associated to
669 the floating IP port forwarding.
670
671 :param internal_port: The TCP/UDP/other protocol port number of the
672 Neutron port fixed IP address associated to the floating ip
673 port forwarding.
674
675 :param external_port: The TCP/UDP/other protocol port number of
676 the port forwarding floating IP address.
677
678 :param internal_ip_address: The fixed IPv4 address of the Neutron
679 port associated to the floating IP port forwarding.
680
681 :param protocol: The IP protocol used in the floating IP port
682 forwarding.
683
684 :param client: network client to be used for creating and cleaning up
685 the floating IP port forwarding.
686 """
687
688 client = client or cls.client
689
690 pf = client.create_port_forwarding(
691 fip_id, internal_port_id, internal_port, external_port,
692 internal_ip_address, protocol)['port_forwarding']
693
694 # save ID of floating IP associated with port forwarding for final
695 # cleanup
696 pf['floatingip_id'] = fip_id
697
698 # save client to be used later in cls.delete_port_forwarding
699 # for final cleanup
700 pf['client'] = client
701 cls.port_forwardings.append(pf)
702 return pf
703
704 @classmethod
Flavio Fernandesa1952c62020-10-02 06:39:08 -0400705 def update_port_forwarding(cls, fip_id, pf_id, client=None, **kwargs):
706 """Wrapper utility for update_port_forwarding."""
707 client = client or cls.client
708 return client.update_port_forwarding(fip_id, pf_id, **kwargs)
709
710 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200711 def delete_port_forwarding(cls, pf, client=None):
712 """Delete port forwarding
713
714 :param client: Client to be used
715 If client is not given it will use the client used to create
716 the port forwarding, or cls.client if unknown.
717 """
718
719 client = client or pf.get('client') or cls.client
720 client.delete_port_forwarding(pf['floatingip_id'], pf['id'])
721
722 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000723 def create_router_interface(cls, router_id, subnet_id):
724 """Wrapper utility that returns a router interface."""
725 interface = cls.client.add_router_interface_with_subnet_id(
726 router_id, subnet_id)
727 return interface
728
729 @classmethod
Bence Romsics46bd3af2019-09-13 10:52:41 +0200730 def add_extra_routes_atomic(cls, *args, **kwargs):
731 return cls.client.add_extra_routes_atomic(*args, **kwargs)
732
733 @classmethod
734 def remove_extra_routes_atomic(cls, *args, **kwargs):
735 return cls.client.remove_extra_routes_atomic(*args, **kwargs)
736
737 @classmethod
Sławek Kapłońskiff294062016-12-04 15:00:54 +0000738 def get_supported_qos_rule_types(cls):
739 body = cls.client.list_qos_rule_types()
740 return [rule_type['type'] for rule_type in body['rule_types']]
741
742 @classmethod
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +0200743 def create_qos_policy(cls, name, description=None, shared=False,
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000744 project_id=None, is_default=False):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000745 """Wrapper utility that returns a test QoS policy."""
746 body = cls.admin_client.create_qos_policy(
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000747 name, description, shared, project_id, is_default)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000748 qos_policy = body['policy']
749 cls.qos_policies.append(qos_policy)
750 return qos_policy
751
752 @classmethod
Sławek Kapłoński153f3452017-03-24 22:04:53 +0000753 def create_qos_bandwidth_limit_rule(cls, policy_id, max_kbps,
754 max_burst_kbps,
Chandan Kumarc125fd12017-11-15 19:41:01 +0530755 direction=const.EGRESS_DIRECTION):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000756 """Wrapper utility that returns a test QoS bandwidth limit rule."""
757 body = cls.admin_client.create_bandwidth_limit_rule(
Sławek Kapłoński153f3452017-03-24 22:04:53 +0000758 policy_id, max_kbps, max_burst_kbps, direction)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000759 qos_rule = body['bandwidth_limit_rule']
760 cls.qos_rules.append(qos_rule)
761 return qos_rule
762
763 @classmethod
Lajos Katona2f904652018-08-23 14:04:56 +0200764 def create_qos_minimum_bandwidth_rule(cls, policy_id, min_kbps,
765 direction=const.EGRESS_DIRECTION):
766 """Wrapper utility that creates and returns a QoS min bw rule."""
767 body = cls.admin_client.create_minimum_bandwidth_rule(
768 policy_id, direction, min_kbps)
769 qos_rule = body['minimum_bandwidth_rule']
770 cls.qos_rules.append(qos_rule)
771 return qos_rule
772
773 @classmethod
Jakub Libosvar83704832017-12-06 16:02:28 +0000774 def delete_router(cls, router, client=None):
775 client = client or cls.client
Aditya Vaja49819a72018-11-26 14:20:10 -0800776 if 'routes' in router:
777 client.remove_router_extra_routes(router['id'])
Jakub Libosvar83704832017-12-06 16:02:28 +0000778 body = client.list_router_interfaces(router['id'])
Chandan Kumarc125fd12017-11-15 19:41:01 +0530779 interfaces = [port for port in body['ports']
780 if port['device_owner'] in const.ROUTER_INTERFACE_OWNERS]
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000781 for i in interfaces:
782 try:
Jakub Libosvar83704832017-12-06 16:02:28 +0000783 client.remove_router_interface_with_subnet_id(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000784 router['id'], i['fixed_ips'][0]['subnet_id'])
785 except lib_exc.NotFound:
786 pass
Jakub Libosvar83704832017-12-06 16:02:28 +0000787 client.delete_router(router['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000788
789 @classmethod
790 def create_address_scope(cls, name, is_admin=False, **kwargs):
791 if is_admin:
792 body = cls.admin_client.create_address_scope(name=name, **kwargs)
793 cls.admin_address_scopes.append(body['address_scope'])
794 else:
795 body = cls.client.create_address_scope(name=name, **kwargs)
796 cls.address_scopes.append(body['address_scope'])
797 return body['address_scope']
798
799 @classmethod
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200800 def create_subnetpool(cls, name, is_admin=False, client=None, **kwargs):
801 if client is None:
802 client = cls.admin_client if is_admin else cls.client
803
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000804 if is_admin:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200805 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000806 cls.admin_subnetpools.append(body['subnetpool'])
807 else:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200808 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000809 cls.subnetpools.append(body['subnetpool'])
810 return body['subnetpool']
811
Chandan Kumarc125fd12017-11-15 19:41:01 +0530812 @classmethod
813 def create_project(cls, name=None, description=None):
814 test_project = name or data_utils.rand_name('test_project_')
815 test_description = description or data_utils.rand_name('desc_')
816 project = cls.identity_admin_client.create_project(
817 name=test_project,
818 description=test_description)['project']
819 cls.projects.append(project)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000820 # Create a project will create a default security group.
Dongcan Ye2de722e2018-07-04 11:01:37 +0000821 sgs_list = cls.admin_client.list_security_groups(
822 tenant_id=project['id'])['security_groups']
Federico Ressi4c590d72018-10-10 14:01:08 +0200823 for security_group in sgs_list:
824 # Make sure delete_security_group method will use
825 # the admin client for this group
826 security_group['client'] = cls.admin_client
827 cls.security_groups.append(security_group)
Chandan Kumarc125fd12017-11-15 19:41:01 +0530828 return project
829
830 @classmethod
Federico Ressi4c590d72018-10-10 14:01:08 +0200831 def create_security_group(cls, name=None, project=None, client=None,
832 **kwargs):
833 if project:
834 client = client or cls.admin_client
835 project_id = kwargs.setdefault('project_id', project['id'])
836 tenant_id = kwargs.setdefault('tenant_id', project['id'])
837 if project_id != project['id'] or tenant_id != project['id']:
838 raise ValueError('Project ID specified multiple times')
839 else:
840 client = client or cls.client
841
842 name = name or data_utils.rand_name(cls.__name__)
843 security_group = client.create_security_group(name=name, **kwargs)[
844 'security_group']
845 security_group['client'] = client
846 cls.security_groups.append(security_group)
847 return security_group
848
849 @classmethod
850 def delete_security_group(cls, security_group, client=None):
851 client = client or security_group.get('client') or cls.client
852 client.delete_security_group(security_group['id'])
853
854 @classmethod
855 def create_security_group_rule(cls, security_group=None, project=None,
856 client=None, ip_version=None, **kwargs):
857 if project:
858 client = client or cls.admin_client
859 project_id = kwargs.setdefault('project_id', project['id'])
860 tenant_id = kwargs.setdefault('tenant_id', project['id'])
861 if project_id != project['id'] or tenant_id != project['id']:
862 raise ValueError('Project ID specified multiple times')
863
864 if 'security_group_id' not in kwargs:
865 security_group = (security_group or
866 cls.get_security_group(client=client))
867
868 if security_group:
869 client = client or security_group.get('client')
870 security_group_id = kwargs.setdefault('security_group_id',
871 security_group['id'])
872 if security_group_id != security_group['id']:
873 raise ValueError('Security group ID specified multiple times.')
874
875 ip_version = ip_version or cls._ip_version
876 default_params = (
877 constants.DEFAULT_SECURITY_GROUP_RULE_PARAMS[ip_version])
878 for key, value in default_params.items():
879 kwargs.setdefault(key, value)
880
881 client = client or cls.client
882 return client.create_security_group_rule(**kwargs)[
883 'security_group_rule']
884
885 @classmethod
886 def get_security_group(cls, name='default', client=None):
887 client = client or cls.client
888 security_groups = client.list_security_groups()['security_groups']
889 for security_group in security_groups:
890 if security_group['name'] == name:
891 return security_group
892 raise ValueError("No such security group named {!r}".format(name))
Chandan Kumarc125fd12017-11-15 19:41:01 +0530893
Federico Ressiab286e42018-06-19 09:52:10 +0200894 @classmethod
895 def create_keypair(cls, client=None, name=None, **kwargs):
896 client = client or cls.os_primary.keypairs_client
897 name = name or data_utils.rand_name('keypair-test')
898 keypair = client.create_keypair(name=name, **kwargs)['keypair']
899
900 # save client for later cleanup
901 keypair['client'] = client
902 cls.keypairs.append(keypair)
903 return keypair
904
905 @classmethod
906 def delete_keypair(cls, keypair, client=None):
907 client = (client or keypair.get('client') or
908 cls.os_primary.keypairs_client)
909 client.delete_keypair(keypair_name=keypair['name'])
910
Federico Ressi82e83e32018-07-03 14:19:55 +0200911 @classmethod
912 def create_trunk(cls, port=None, subports=None, client=None, **kwargs):
913 """Create network trunk
914
915 :param port: dictionary containing parent port ID (port['id'])
916 :param client: client to be used for connecting to networking service
917 :param **kwargs: extra parameters to be forwarded to network service
918
919 :returns: dictionary containing created trunk details
920 """
921 client = client or cls.client
922
923 if port:
924 kwargs['port_id'] = port['id']
925
926 trunk = client.create_trunk(subports=subports, **kwargs)['trunk']
927 # Save client reference for later deletion
928 trunk['client'] = client
929 cls.trunks.append(trunk)
930 return trunk
931
932 @classmethod
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800933 def delete_trunk(cls, trunk, client=None, detach_parent_port=True):
Federico Ressi82e83e32018-07-03 14:19:55 +0200934 """Delete network trunk
935
936 :param trunk: dictionary containing trunk ID (trunk['id'])
937
938 :param client: client to be used for connecting to networking service
939 """
940 client = client or trunk.get('client') or cls.client
941 trunk.update(client.show_trunk(trunk['id'])['trunk'])
942
943 if not trunk['admin_state_up']:
944 # Cannot touch trunk before admin_state_up is True
945 client.update_trunk(trunk['id'], admin_state_up=True)
946 if trunk['sub_ports']:
947 # Removes trunk ports before deleting it
948 cls._try_delete_resource(client.remove_subports, trunk['id'],
949 trunk['sub_ports'])
950
951 # we have to detach the interface from the server before
952 # the trunk can be deleted.
953 parent_port = {'id': trunk['port_id']}
954
955 def is_parent_port_detached():
956 parent_port.update(client.show_port(parent_port['id'])['port'])
957 return not parent_port['device_id']
958
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800959 if detach_parent_port and not is_parent_port_detached():
Federico Ressi82e83e32018-07-03 14:19:55 +0200960 # this could probably happen when trunk is deleted and parent port
961 # has been assigned to a VM that is still running. Here we are
962 # assuming that device_id points to such VM.
963 cls.os_primary.compute.InterfacesClient().delete_interface(
964 parent_port['device_id'], parent_port['id'])
965 utils.wait_until_true(is_parent_port_detached)
966
967 client.delete_trunk(trunk['id'])
968
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200969 @classmethod
970 def create_conntrack_helper(cls, router_id, helper, protocol, port,
971 client=None):
972 """Create a conntrack helper
973
974 Create a conntrack helper and schedule it for later deletion. If a
975 client is passed, then it is used for deleteing the CTH too.
976
977 :param router_id: The ID of the Neutron router associated to the
978 conntrack helper.
979
980 :param helper: The conntrack helper module alias
981
982 :param protocol: The conntrack helper IP protocol used in the conntrack
983 helper.
984
985 :param port: The conntrack helper IP protocol port number for the
986 conntrack helper.
987
988 :param client: network client to be used for creating and cleaning up
989 the conntrack helper.
990 """
991
992 client = client or cls.client
993
994 cth = client.create_conntrack_helper(router_id, helper, protocol,
995 port)['conntrack_helper']
996
997 # save ID of router associated with conntrack helper for final cleanup
998 cth['router_id'] = router_id
999
1000 # save client to be used later in cls.delete_conntrack_helper for final
1001 # cleanup
1002 cth['client'] = client
1003 cls.conntrack_helpers.append(cth)
1004 return cth
1005
1006 @classmethod
1007 def delete_conntrack_helper(cls, cth, client=None):
1008 """Delete conntrack helper
1009
1010 :param client: Client to be used
1011 If client is not given it will use the client used to create the
1012 conntrack helper, or cls.client if unknown.
1013 """
1014
1015 client = client or cth.get('client') or cls.client
1016 client.delete_conntrack_helper(cth['router_id'], cth['id'])
1017
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001018
1019class BaseAdminNetworkTest(BaseNetworkTest):
1020
1021 credentials = ['primary', 'admin']
1022
1023 @classmethod
1024 def setup_clients(cls):
1025 super(BaseAdminNetworkTest, cls).setup_clients()
fumihiko kakumaa216fc12017-07-14 10:43:29 +09001026 cls.admin_client = cls.os_admin.network_client
Jakub Libosvarf5758012017-08-15 13:45:30 +00001027 cls.identity_admin_client = cls.os_admin.projects_client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001028
1029 @classmethod
1030 def create_metering_label(cls, name, description):
1031 """Wrapper utility that returns a test metering label."""
1032 body = cls.admin_client.create_metering_label(
1033 description=description,
1034 name=data_utils.rand_name("metering-label"))
1035 metering_label = body['metering_label']
1036 cls.metering_labels.append(metering_label)
1037 return metering_label
1038
1039 @classmethod
1040 def create_metering_label_rule(cls, remote_ip_prefix, direction,
1041 metering_label_id):
1042 """Wrapper utility that returns a test metering label rule."""
1043 body = cls.admin_client.create_metering_label_rule(
1044 remote_ip_prefix=remote_ip_prefix, direction=direction,
1045 metering_label_id=metering_label_id)
1046 metering_label_rule = body['metering_label_rule']
1047 cls.metering_label_rules.append(metering_label_rule)
1048 return metering_label_rule
1049
1050 @classmethod
Kailun Qineaaf9782018-12-20 04:45:01 +08001051 def create_network_segment_range(cls, name, shared,
1052 project_id, network_type,
1053 physical_network, minimum,
1054 maximum):
1055 """Wrapper utility that returns a test network segment range."""
1056 network_segment_range_args = {'name': name,
1057 'shared': shared,
1058 'project_id': project_id,
1059 'network_type': network_type,
1060 'physical_network': physical_network,
1061 'minimum': minimum,
1062 'maximum': maximum}
1063 body = cls.admin_client.create_network_segment_range(
1064 **network_segment_range_args)
1065 network_segment_range = body['network_segment_range']
1066 cls.network_segment_ranges.append(network_segment_range)
1067 return network_segment_range
1068
1069 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001070 def create_flavor(cls, name, description, service_type):
1071 """Wrapper utility that returns a test flavor."""
1072 body = cls.admin_client.create_flavor(
1073 description=description, service_type=service_type,
1074 name=name)
1075 flavor = body['flavor']
1076 cls.flavors.append(flavor)
1077 return flavor
1078
1079 @classmethod
1080 def create_service_profile(cls, description, metainfo, driver):
1081 """Wrapper utility that returns a test service profile."""
1082 body = cls.admin_client.create_service_profile(
1083 driver=driver, metainfo=metainfo, description=description)
1084 service_profile = body['service_profile']
1085 cls.service_profiles.append(service_profile)
1086 return service_profile
1087
1088 @classmethod
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001089 def create_log(cls, name, description=None,
1090 resource_type='security_group', resource_id=None,
1091 target_id=None, event='ALL', enabled=True):
1092 """Wrapper utility that returns a test log object."""
1093 log_args = {'name': name,
1094 'description': description,
1095 'resource_type': resource_type,
1096 'resource_id': resource_id,
1097 'target_id': target_id,
1098 'event': event,
1099 'enabled': enabled}
1100 body = cls.admin_client.create_log(**log_args)
1101 log_object = body['log']
1102 cls.log_objects.append(log_object)
1103 return log_object
1104
1105 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001106 def get_unused_ip(cls, net_id, ip_version=None):
Gary Kotton011345f2016-06-15 08:04:31 -07001107 """Get an unused ip address in a allocation pool of net"""
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001108 body = cls.admin_client.list_ports(network_id=net_id)
1109 ports = body['ports']
1110 used_ips = []
1111 for port in ports:
1112 used_ips.extend(
1113 [fixed_ip['ip_address'] for fixed_ip in port['fixed_ips']])
1114 body = cls.admin_client.list_subnets(network_id=net_id)
1115 subnets = body['subnets']
1116
1117 for subnet in subnets:
1118 if ip_version and subnet['ip_version'] != ip_version:
1119 continue
1120 cidr = subnet['cidr']
1121 allocation_pools = subnet['allocation_pools']
1122 iterators = []
1123 if allocation_pools:
1124 for allocation_pool in allocation_pools:
1125 iterators.append(netaddr.iter_iprange(
1126 allocation_pool['start'], allocation_pool['end']))
1127 else:
1128 net = netaddr.IPNetwork(cidr)
1129
1130 def _iterip():
1131 for ip in net:
1132 if ip not in (net.network, net.broadcast):
1133 yield ip
1134 iterators.append(iter(_iterip()))
1135
1136 for iterator in iterators:
1137 for ip in iterator:
1138 if str(ip) not in used_ips:
1139 return str(ip)
1140
1141 message = (
1142 "net(%s) has no usable IP address in allocation pools" % net_id)
1143 raise exceptions.InvalidConfiguration(message)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001144
Lajos Katona2f904652018-08-23 14:04:56 +02001145 @classmethod
1146 def create_provider_network(cls, physnet_name, start_segmentation_id,
1147 max_attempts=30):
1148 segmentation_id = start_segmentation_id
Lajos Katona7eb67252019-01-14 12:55:35 +01001149 for attempts in range(max_attempts):
Lajos Katona2f904652018-08-23 14:04:56 +02001150 try:
Lajos Katona7eb67252019-01-14 12:55:35 +01001151 return cls.create_network(
Lajos Katona2f904652018-08-23 14:04:56 +02001152 name=data_utils.rand_name('test_net'),
1153 shared=True,
1154 provider_network_type='vlan',
1155 provider_physical_network=physnet_name,
1156 provider_segmentation_id=segmentation_id)
Lajos Katona2f904652018-08-23 14:04:56 +02001157 except lib_exc.Conflict:
Lajos Katona2f904652018-08-23 14:04:56 +02001158 segmentation_id += 1
1159 if segmentation_id > 4095:
1160 raise lib_exc.TempestException(
1161 "No free segmentation id was found for provider "
1162 "network creation!")
1163 time.sleep(CONF.network.build_interval)
Lajos Katona7eb67252019-01-14 12:55:35 +01001164 LOG.exception("Failed to create provider network after "
1165 "%d attempts", max_attempts)
1166 raise lib_exc.TimeoutException
Lajos Katona2f904652018-08-23 14:04:56 +02001167
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001168
Sławek Kapłońskiff294062016-12-04 15:00:54 +00001169def require_qos_rule_type(rule_type):
1170 def decorator(f):
1171 @functools.wraps(f)
1172 def wrapper(self, *func_args, **func_kwargs):
1173 if rule_type not in self.get_supported_qos_rule_types():
1174 raise self.skipException(
1175 "%s rule type is required." % rule_type)
1176 return f(self, *func_args, **func_kwargs)
1177 return wrapper
1178 return decorator
1179
1180
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001181def _require_sorting(f):
1182 @functools.wraps(f)
1183 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301184 if not tutils.is_extension_enabled("sorting", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001185 self.skipTest('Sorting feature is required')
1186 return f(self, *args, **kwargs)
1187 return inner
1188
1189
1190def _require_pagination(f):
1191 @functools.wraps(f)
1192 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301193 if not tutils.is_extension_enabled("pagination", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001194 self.skipTest('Pagination feature is required')
1195 return f(self, *args, **kwargs)
1196 return inner
1197
1198
1199class BaseSearchCriteriaTest(BaseNetworkTest):
1200
1201 # This should be defined by subclasses to reflect resource name to test
1202 resource = None
1203
Armando Migliaccio57581c62016-07-01 10:13:19 -07001204 field = 'name'
1205
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001206 # NOTE(ihrachys): some names, like those starting with an underscore (_)
1207 # are sorted differently depending on whether the plugin implements native
1208 # sorting support, or not. So we avoid any such cases here, sticking to
1209 # alphanumeric. Also test a case when there are multiple resources with the
1210 # same name
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001211 resource_names = ('test1', 'abc1', 'test10', '123test') + ('test1',)
1212
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001213 force_tenant_isolation = True
1214
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001215 list_kwargs = {}
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001216
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001217 list_as_admin = False
1218
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001219 def assertSameOrder(self, original, actual):
1220 # gracefully handle iterators passed
1221 original = list(original)
1222 actual = list(actual)
1223 self.assertEqual(len(original), len(actual))
1224 for expected, res in zip(original, actual):
Armando Migliaccio57581c62016-07-01 10:13:19 -07001225 self.assertEqual(expected[self.field], res[self.field])
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001226
1227 @utils.classproperty
1228 def plural_name(self):
1229 return '%ss' % self.resource
1230
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001231 @property
1232 def list_client(self):
1233 return self.admin_client if self.list_as_admin else self.client
1234
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001235 def list_method(self, *args, **kwargs):
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001236 method = getattr(self.list_client, 'list_%s' % self.plural_name)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001237 kwargs.update(self.list_kwargs)
1238 return method(*args, **kwargs)
1239
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001240 def get_bare_url(self, url):
1241 base_url = self.client.base_url
zheng.yong74e760a2019-05-22 14:16:14 +08001242 base_url_normalized = utils.normalize_url(base_url)
1243 url_normalized = utils.normalize_url(url)
1244 self.assertTrue(url_normalized.startswith(base_url_normalized))
1245 return url_normalized[len(base_url_normalized):]
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001246
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001247 @classmethod
1248 def _extract_resources(cls, body):
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001249 return body[cls.plural_name]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001250
1251 def _test_list_sorts(self, direction):
1252 sort_args = {
1253 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001254 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001255 }
1256 body = self.list_method(**sort_args)
1257 resources = self._extract_resources(body)
1258 self.assertNotEmpty(
1259 resources, "%s list returned is empty" % self.resource)
Armando Migliaccio57581c62016-07-01 10:13:19 -07001260 retrieved_names = [res[self.field] for res in resources]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001261 expected = sorted(retrieved_names)
1262 if direction == constants.SORT_DIRECTION_DESC:
1263 expected = list(reversed(expected))
1264 self.assertEqual(expected, retrieved_names)
1265
1266 @_require_sorting
1267 def _test_list_sorts_asc(self):
1268 self._test_list_sorts(constants.SORT_DIRECTION_ASC)
1269
1270 @_require_sorting
1271 def _test_list_sorts_desc(self):
1272 self._test_list_sorts(constants.SORT_DIRECTION_DESC)
1273
1274 @_require_pagination
1275 def _test_list_pagination(self):
1276 for limit in range(1, len(self.resource_names) + 1):
1277 pagination_args = {
1278 'limit': limit,
1279 }
1280 body = self.list_method(**pagination_args)
1281 resources = self._extract_resources(body)
1282 self.assertEqual(limit, len(resources))
1283
1284 @_require_pagination
1285 def _test_list_no_pagination_limit_0(self):
1286 pagination_args = {
1287 'limit': 0,
1288 }
1289 body = self.list_method(**pagination_args)
1290 resources = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001291 self.assertGreaterEqual(len(resources), len(self.resource_names))
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001292
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001293 def _test_list_pagination_iteratively(self, lister):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001294 # first, collect all resources for later comparison
1295 sort_args = {
1296 'sort_dir': constants.SORT_DIRECTION_ASC,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001297 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001298 }
1299 body = self.list_method(**sort_args)
1300 expected_resources = self._extract_resources(body)
1301 self.assertNotEmpty(expected_resources)
1302
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001303 resources = lister(
1304 len(expected_resources), sort_args
1305 )
1306
1307 # finally, compare that the list retrieved in one go is identical to
1308 # the one containing pagination results
1309 self.assertSameOrder(expected_resources, resources)
1310
1311 def _list_all_with_marker(self, niterations, sort_args):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001312 # paginate resources one by one, using last fetched resource as a
1313 # marker
1314 resources = []
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001315 for i in range(niterations):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001316 pagination_args = sort_args.copy()
1317 pagination_args['limit'] = 1
1318 if resources:
1319 pagination_args['marker'] = resources[-1]['id']
1320 body = self.list_method(**pagination_args)
1321 resources_ = self._extract_resources(body)
1322 self.assertEqual(1, len(resources_))
1323 resources.extend(resources_)
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001324 return resources
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001325
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001326 @_require_pagination
1327 @_require_sorting
1328 def _test_list_pagination_with_marker(self):
1329 self._test_list_pagination_iteratively(self._list_all_with_marker)
1330
1331 def _list_all_with_hrefs(self, niterations, sort_args):
1332 # paginate resources one by one, using next href links
1333 resources = []
1334 prev_links = {}
1335
1336 for i in range(niterations):
1337 if prev_links:
1338 uri = self.get_bare_url(prev_links['next'])
1339 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001340 sort_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001341 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001342 self.plural_name, limit=1, **sort_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001343 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001344 self.plural_name, uri
1345 )
1346 resources_ = self._extract_resources(body)
1347 self.assertEqual(1, len(resources_))
1348 resources.extend(resources_)
1349
1350 # The last element is empty and does not contain 'next' link
1351 uri = self.get_bare_url(prev_links['next'])
1352 prev_links, body = self.client.get_uri_with_links(
1353 self.plural_name, uri
1354 )
1355 self.assertNotIn('next', prev_links)
1356
1357 # Now walk backwards and compare results
1358 resources2 = []
1359 for i in range(niterations):
1360 uri = self.get_bare_url(prev_links['previous'])
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001361 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001362 self.plural_name, uri
1363 )
1364 resources_ = self._extract_resources(body)
1365 self.assertEqual(1, len(resources_))
1366 resources2.extend(resources_)
1367
1368 self.assertSameOrder(resources, reversed(resources2))
1369
1370 return resources
1371
1372 @_require_pagination
1373 @_require_sorting
1374 def _test_list_pagination_with_href_links(self):
1375 self._test_list_pagination_iteratively(self._list_all_with_hrefs)
1376
1377 @_require_pagination
1378 @_require_sorting
1379 def _test_list_pagination_page_reverse_with_href_links(
1380 self, direction=constants.SORT_DIRECTION_ASC):
1381 pagination_args = {
1382 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001383 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001384 }
1385 body = self.list_method(**pagination_args)
1386 expected_resources = self._extract_resources(body)
1387
1388 page_size = 2
1389 pagination_args['limit'] = page_size
1390
1391 prev_links = {}
1392 resources = []
1393 num_resources = len(expected_resources)
1394 niterations = int(math.ceil(float(num_resources) / page_size))
1395 for i in range(niterations):
1396 if prev_links:
1397 uri = self.get_bare_url(prev_links['previous'])
1398 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001399 pagination_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001400 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001401 self.plural_name, page_reverse=True, **pagination_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001402 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001403 self.plural_name, uri
1404 )
1405 resources_ = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001406 self.assertGreaterEqual(page_size, len(resources_))
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001407 resources.extend(reversed(resources_))
1408
1409 self.assertSameOrder(expected_resources, reversed(resources))
1410
1411 @_require_pagination
1412 @_require_sorting
1413 def _test_list_pagination_page_reverse_asc(self):
1414 self._test_list_pagination_page_reverse(
1415 direction=constants.SORT_DIRECTION_ASC)
1416
1417 @_require_pagination
1418 @_require_sorting
1419 def _test_list_pagination_page_reverse_desc(self):
1420 self._test_list_pagination_page_reverse(
1421 direction=constants.SORT_DIRECTION_DESC)
1422
1423 def _test_list_pagination_page_reverse(self, direction):
1424 pagination_args = {
1425 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001426 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001427 'limit': 3,
1428 }
1429 body = self.list_method(**pagination_args)
1430 expected_resources = self._extract_resources(body)
1431
1432 pagination_args['limit'] -= 1
1433 pagination_args['marker'] = expected_resources[-1]['id']
1434 pagination_args['page_reverse'] = True
1435 body = self.list_method(**pagination_args)
1436
1437 self.assertSameOrder(
1438 # the last entry is not included in 2nd result when used as a
1439 # marker
1440 expected_resources[:-1],
1441 self._extract_resources(body))
Victor Morales1be97b42016-09-05 08:50:06 -05001442
Hongbin Lu54f55922018-07-12 19:05:39 +00001443 @tutils.requires_ext(extension="filter-validation", service="network")
1444 def _test_list_validation_filters(
1445 self, validation_args, filter_is_valid=True):
1446 if not filter_is_valid:
1447 self.assertRaises(lib_exc.BadRequest, self.list_method,
1448 **validation_args)
1449 else:
1450 body = self.list_method(**validation_args)
1451 resources = self._extract_resources(body)
1452 for resource in resources:
1453 self.assertIn(resource['name'], self.resource_names)