blob: 4833c7177cc9fcbac834d19f3e03c359f8ecf5ad [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
yangjianfeng23e40c22020-11-22 08:42:18 +0000143 def reserve_external_subnet_cidrs(cls):
144 client = cls.os_admin.network_client
145 ext_nets = client.list_networks(
146 **{"router:external": True})['networks']
147 for ext_net in ext_nets:
148 ext_subnets = client.list_subnets(
149 network_id=ext_net['id'])['subnets']
150 for ext_subnet in ext_subnets:
151 cls.reserve_subnet_cidr(ext_subnet['cidr'])
152
153 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000154 def resource_cleanup(cls):
155 if CONF.service_available.neutron:
Federico Ressi82e83e32018-07-03 14:19:55 +0200156 # Clean up trunks
157 for trunk in cls.trunks:
158 cls._try_delete_resource(cls.delete_trunk, trunk)
159
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200160 # Clean up port forwardings
161 for pf in cls.port_forwardings:
162 cls._try_delete_resource(cls.delete_port_forwarding, pf)
163
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000164 # Clean up floating IPs
165 for floating_ip in cls.floating_ips:
Federico Ressia69dcd52018-07-06 09:45:34 +0200166 cls._try_delete_resource(cls.delete_floatingip, floating_ip)
167
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200168 # Clean up conntrack helpers
169 for cth in cls.conntrack_helpers:
170 cls._try_delete_resource(cls.delete_conntrack_helper, cth)
171
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000172 # Clean up routers
173 for router in cls.routers:
174 cls._try_delete_resource(cls.delete_router,
175 router)
176 # Clean up metering label rules
177 for metering_label_rule in cls.metering_label_rules:
178 cls._try_delete_resource(
179 cls.admin_client.delete_metering_label_rule,
180 metering_label_rule['id'])
181 # Clean up metering labels
182 for metering_label in cls.metering_labels:
183 cls._try_delete_resource(
184 cls.admin_client.delete_metering_label,
185 metering_label['id'])
186 # Clean up flavors
187 for flavor in cls.flavors:
188 cls._try_delete_resource(
189 cls.admin_client.delete_flavor,
190 flavor['id'])
191 # Clean up service profiles
192 for service_profile in cls.service_profiles:
193 cls._try_delete_resource(
194 cls.admin_client.delete_service_profile,
195 service_profile['id'])
196 # Clean up ports
197 for port in cls.ports:
198 cls._try_delete_resource(cls.client.delete_port,
199 port['id'])
200 # Clean up subnets
201 for subnet in cls.subnets:
202 cls._try_delete_resource(cls.client.delete_subnet,
203 subnet['id'])
Kevin Bentonba3651c2017-09-01 17:13:01 -0700204 # Clean up admin subnets
205 for subnet in cls.admin_subnets:
206 cls._try_delete_resource(cls.admin_client.delete_subnet,
207 subnet['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000208 # Clean up networks
209 for network in cls.networks:
Federico Ressi61b564e2018-07-06 08:10:31 +0200210 cls._try_delete_resource(cls.delete_network, network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000211
Miguel Lavalle124378b2016-09-21 16:41:47 -0500212 # Clean up admin networks
213 for network in cls.admin_networks:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000214 cls._try_delete_resource(cls.admin_client.delete_network,
215 network['id'])
216
Itzik Brownbac51dc2016-10-31 12:25:04 +0000217 # Clean up security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200218 for security_group in cls.security_groups:
219 cls._try_delete_resource(cls.delete_security_group,
220 security_group)
Itzik Brownbac51dc2016-10-31 12:25:04 +0000221
Dongcan Ye2de722e2018-07-04 11:01:37 +0000222 # Clean up admin security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200223 for security_group in cls.admin_security_groups:
224 cls._try_delete_resource(cls.delete_security_group,
225 security_group,
226 client=cls.admin_client)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000227
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000228 for subnetpool in cls.subnetpools:
229 cls._try_delete_resource(cls.client.delete_subnetpool,
230 subnetpool['id'])
231
232 for subnetpool in cls.admin_subnetpools:
233 cls._try_delete_resource(cls.admin_client.delete_subnetpool,
234 subnetpool['id'])
235
236 for address_scope in cls.address_scopes:
237 cls._try_delete_resource(cls.client.delete_address_scope,
238 address_scope['id'])
239
240 for address_scope in cls.admin_address_scopes:
241 cls._try_delete_resource(
242 cls.admin_client.delete_address_scope,
243 address_scope['id'])
244
Chandan Kumarc125fd12017-11-15 19:41:01 +0530245 for project in cls.projects:
246 cls._try_delete_resource(
247 cls.identity_admin_client.delete_project,
248 project['id'])
249
Sławek Kapłońskie100c4d2017-08-23 21:18:34 +0000250 # Clean up QoS rules
251 for qos_rule in cls.qos_rules:
252 cls._try_delete_resource(cls.admin_client.delete_qos_rule,
253 qos_rule['id'])
254 # Clean up QoS policies
255 # as all networks and ports are already removed, QoS policies
256 # shouldn't be "in use"
257 for qos_policy in cls.qos_policies:
258 cls._try_delete_resource(cls.admin_client.delete_qos_policy,
259 qos_policy['id'])
260
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700261 # Clean up log_objects
262 for log_object in cls.log_objects:
263 cls._try_delete_resource(cls.admin_client.delete_log,
264 log_object['id'])
265
Federico Ressiab286e42018-06-19 09:52:10 +0200266 for keypair in cls.keypairs:
267 cls._try_delete_resource(cls.delete_keypair, keypair)
268
Kailun Qineaaf9782018-12-20 04:45:01 +0800269 # Clean up network_segment_ranges
270 for network_segment_range in cls.network_segment_ranges:
271 cls._try_delete_resource(
272 cls.admin_client.delete_network_segment_range,
273 network_segment_range['id'])
274
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000275 super(BaseNetworkTest, cls).resource_cleanup()
276
277 @classmethod
278 def _try_delete_resource(cls, delete_callable, *args, **kwargs):
279 """Cleanup resources in case of test-failure
280
281 Some resources are explicitly deleted by the test.
282 If the test failed to delete a resource, this method will execute
283 the appropriate delete methods. Otherwise, the method ignores NotFound
284 exceptions thrown for resources that were correctly deleted by the
285 test.
286
287 :param delete_callable: delete method
288 :param args: arguments for delete method
289 :param kwargs: keyword arguments for delete method
290 """
291 try:
292 delete_callable(*args, **kwargs)
293 # if resource is not found, this means it was deleted in the test
294 except lib_exc.NotFound:
295 pass
296
297 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200298 def create_network(cls, network_name=None, client=None, external=None,
299 shared=None, provider_network_type=None,
300 provider_physical_network=None,
301 provider_segmentation_id=None, **kwargs):
302 """Create a network.
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000303
Federico Ressi61b564e2018-07-06 08:10:31 +0200304 When client is not provider and admin_client is attribute is not None
305 (for example when using BaseAdminNetworkTest base class) and using any
306 of the convenience parameters (external, shared, provider_network_type,
307 provider_physical_network and provider_segmentation_id) it silently
308 uses admin_client. If the network is not shared then it uses the same
309 project_id as regular client.
310
311 :param network_name: Human-readable name of the network
312
313 :param client: client to be used for connecting to network service
314
315 :param external: indicates whether the network has an external routing
316 facility that's not managed by the networking service.
317
318 :param shared: indicates whether this resource is shared across all
319 projects. By default, only administrative users can change this value.
320 If True and admin_client attribute is not None, then the network is
321 created under administrative project.
322
323 :param provider_network_type: the type of physical network that this
324 network should be mapped to. For example, 'flat', 'vlan', 'vxlan', or
325 'gre'. Valid values depend on a networking back-end.
326
327 :param provider_physical_network: the physical network where this
328 network should be implemented. The Networking API v2.0 does not provide
329 a way to list available physical networks. For example, the Open
330 vSwitch plug-in configuration file defines a symbolic name that maps to
331 specific bridges on each compute host.
332
333 :param provider_segmentation_id: The ID of the isolated segment on the
334 physical network. The network_type attribute defines the segmentation
335 model. For example, if the network_type value is 'vlan', this ID is a
336 vlan identifier. If the network_type value is 'gre', this ID is a gre
337 key.
338
339 :param **kwargs: extra parameters to be forwarded to network service
340 """
341
342 name = (network_name or kwargs.pop('name', None) or
343 data_utils.rand_name('test-network-'))
344
345 # translate convenience parameters
346 admin_client_required = False
347 if provider_network_type:
348 admin_client_required = True
349 kwargs['provider:network_type'] = provider_network_type
350 if provider_physical_network:
351 admin_client_required = True
352 kwargs['provider:physical_network'] = provider_physical_network
353 if provider_segmentation_id:
354 admin_client_required = True
355 kwargs['provider:segmentation_id'] = provider_segmentation_id
356 if external is not None:
357 admin_client_required = True
358 kwargs['router:external'] = bool(external)
359 if shared is not None:
360 admin_client_required = True
361 kwargs['shared'] = bool(shared)
362
363 if not client:
364 if admin_client_required and cls.admin_client:
365 # For convenience silently switch to admin client
366 client = cls.admin_client
367 if not shared:
368 # Keep this network visible from current project
369 project_id = (kwargs.get('project_id') or
370 kwargs.get('tenant_id') or
371 cls.client.tenant_id)
372 kwargs.update(project_id=project_id, tenant_id=project_id)
373 else:
374 # Use default client
375 client = cls.client
376
377 network = client.create_network(name=name, **kwargs)['network']
378 network['client'] = client
379 cls.networks.append(network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000380 return network
381
382 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200383 def delete_network(cls, network, client=None):
384 client = client or network.get('client') or cls.client
385 client.delete_network(network['id'])
386
387 @classmethod
388 def create_shared_network(cls, network_name=None, **kwargs):
389 return cls.create_network(name=network_name, shared=True, **kwargs)
Miguel Lavalle124378b2016-09-21 16:41:47 -0500390
391 @classmethod
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200392 def create_subnet(cls, network, gateway='', cidr=None, mask_bits=None,
Federico Ressi98f20ec2018-05-11 06:09:49 +0200393 ip_version=None, client=None, reserve_cidr=True,
394 **kwargs):
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200395 """Wrapper utility that returns a test subnet.
396
397 Convenient wrapper for client.create_subnet method. It reserves and
398 allocates CIDRs to avoid creating overlapping subnets.
399
400 :param network: network where to create the subnet
401 network['id'] must contain the ID of the network
402
403 :param gateway: gateway IP address
404 It can be a str or a netaddr.IPAddress
405 If gateway is not given, then it will use default address for
406 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 +0200407 if gateway is given as None then no gateway will be assigned
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200408
409 :param cidr: CIDR of the subnet to create
410 It can be either None, a str or a netaddr.IPNetwork instance
411
412 :param mask_bits: CIDR prefix length
413 It can be either None or a numeric value.
414 If cidr parameter is given then mask_bits is used to determinate a
415 sequence of valid CIDR to use as generated.
416 Please see netaddr.IPNetwork.subnet method documentation[1]
417
418 :param ip_version: ip version of generated subnet CIDRs
419 It can be None, IP_VERSION_4 or IP_VERSION_6
420 It has to match given either given CIDR and gateway
421
422 :param ip_version: numeric value (either IP_VERSION_4 or IP_VERSION_6)
423 this value must match CIDR and gateway IP versions if any of them is
424 given
425
426 :param client: client to be used to connect to network service
427
Federico Ressi98f20ec2018-05-11 06:09:49 +0200428 :param reserve_cidr: if True then it reserves assigned CIDR to avoid
429 using the same CIDR for further subnets in the scope of the same
430 test case class
431
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200432 :param **kwargs: optional parameters to be forwarded to wrapped method
433
434 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
435 """
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000436
437 # allow tests to use admin client
438 if not client:
439 client = cls.client
440
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200441 if gateway:
442 gateway_ip = netaddr.IPAddress(gateway)
443 if ip_version:
444 if ip_version != gateway_ip.version:
445 raise ValueError(
446 "Gateway IP version doesn't match IP version")
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000447 else:
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200448 ip_version = gateway_ip.version
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200449 else:
450 ip_version = ip_version or cls._ip_version
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200451
452 for subnet_cidr in cls.get_subnet_cidrs(
453 ip_version=ip_version, cidr=cidr, mask_bits=mask_bits):
Federico Ressi98f20ec2018-05-11 06:09:49 +0200454 if gateway is not None:
455 kwargs['gateway_ip'] = str(gateway or (subnet_cidr.ip + 1))
Slawek Kaplonski21f53422018-11-02 16:02:09 +0100456 else:
457 kwargs['gateway_ip'] = None
Federico Ressi98f20ec2018-05-11 06:09:49 +0200458 try:
459 body = client.create_subnet(
460 network_id=network['id'],
461 cidr=str(subnet_cidr),
462 ip_version=subnet_cidr.version,
463 **kwargs)
464 break
465 except lib_exc.BadRequest as e:
466 if 'overlaps with another subnet' not in str(e):
467 raise
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000468 else:
469 message = 'Available CIDR for subnet creation could not be found'
470 raise ValueError(message)
471 subnet = body['subnet']
Kevin Bentonba3651c2017-09-01 17:13:01 -0700472 if client is cls.client:
473 cls.subnets.append(subnet)
474 else:
475 cls.admin_subnets.append(subnet)
Federico Ressi98f20ec2018-05-11 06:09:49 +0200476 if reserve_cidr:
477 cls.reserve_subnet_cidr(subnet_cidr)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000478 return subnet
479
480 @classmethod
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200481 def reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
482 """Reserve given subnet CIDR making sure it is not used by create_subnet
483
484 :param addr: the CIDR address to be reserved
485 It can be a str or netaddr.IPNetwork instance
486
487 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
488 parameters
489 """
490
491 if not cls.try_reserve_subnet_cidr(addr, **ipnetwork_kwargs):
Bernard Cafarellic3bec862020-09-10 13:59:49 +0200492 raise ValueError('Subnet CIDR already reserved: {0!r}'.format(
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200493 addr))
494
495 @classmethod
496 def try_reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
497 """Reserve given subnet CIDR if it hasn't been reserved before
498
499 :param addr: the CIDR address to be reserved
500 It can be a str or netaddr.IPNetwork instance
501
502 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
503 parameters
504
505 :return: True if it wasn't reserved before, False elsewhere.
506 """
507
508 subnet_cidr = netaddr.IPNetwork(addr, **ipnetwork_kwargs)
509 if subnet_cidr in cls.reserved_subnet_cidrs:
510 return False
511 else:
512 cls.reserved_subnet_cidrs.add(subnet_cidr)
513 return True
514
515 @classmethod
516 def get_subnet_cidrs(
517 cls, cidr=None, mask_bits=None, ip_version=None):
518 """Iterate over a sequence of unused subnet CIDR for IP version
519
520 :param cidr: CIDR of the subnet to create
521 It can be either None, a str or a netaddr.IPNetwork instance
522
523 :param mask_bits: CIDR prefix length
524 It can be either None or a numeric value.
525 If cidr parameter is given then mask_bits is used to determinate a
526 sequence of valid CIDR to use as generated.
527 Please see netaddr.IPNetwork.subnet method documentation[1]
528
529 :param ip_version: ip version of generated subnet CIDRs
530 It can be None, IP_VERSION_4 or IP_VERSION_6
531 It has to match given CIDR if given
532
533 :return: iterator over reserved CIDRs of type netaddr.IPNetwork
534
535 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
536 """
537
538 if cidr:
539 # Generate subnet CIDRs starting from given CIDR
540 # checking it is of requested IP version
541 cidr = netaddr.IPNetwork(cidr, version=ip_version)
542 else:
543 # Generate subnet CIDRs starting from configured values
544 ip_version = ip_version or cls._ip_version
545 if ip_version == const.IP_VERSION_4:
546 mask_bits = mask_bits or config.safe_get_config_value(
547 'network', 'project_network_mask_bits')
548 cidr = netaddr.IPNetwork(config.safe_get_config_value(
549 'network', 'project_network_cidr'))
550 elif ip_version == const.IP_VERSION_6:
551 mask_bits = config.safe_get_config_value(
552 'network', 'project_network_v6_mask_bits')
553 cidr = netaddr.IPNetwork(config.safe_get_config_value(
554 'network', 'project_network_v6_cidr'))
555 else:
556 raise ValueError('Invalid IP version: {!r}'.format(ip_version))
557
558 if mask_bits:
559 subnet_cidrs = cidr.subnet(mask_bits)
560 else:
561 subnet_cidrs = iter([cidr])
562
563 for subnet_cidr in subnet_cidrs:
564 if subnet_cidr not in cls.reserved_subnet_cidrs:
565 yield subnet_cidr
566
567 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000568 def create_port(cls, network, **kwargs):
569 """Wrapper utility that returns a test port."""
Edan Davidd75e48e2018-01-03 02:49:52 -0500570 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
571 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
Glenn Van de Water5d9b1402020-09-16 15:14:14 +0200572 if CONF.network.port_profile and 'binding:profile' not in kwargs:
573 kwargs['binding:profile'] = CONF.network.port_profile
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000574 body = cls.client.create_port(network_id=network['id'],
575 **kwargs)
576 port = body['port']
577 cls.ports.append(port)
578 return port
579
580 @classmethod
581 def update_port(cls, port, **kwargs):
582 """Wrapper utility that updates a test port."""
583 body = cls.client.update_port(port['id'],
584 **kwargs)
585 return body['port']
586
587 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300588 def _create_router_with_client(
589 cls, client, router_name=None, admin_state_up=False,
590 external_network_id=None, enable_snat=None, **kwargs
591 ):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000592 ext_gw_info = {}
593 if external_network_id:
594 ext_gw_info['network_id'] = external_network_id
YAMAMOTO Takashi9bd4f972017-06-20 12:49:30 +0900595 if enable_snat is not None:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000596 ext_gw_info['enable_snat'] = enable_snat
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300597 body = client.create_router(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000598 router_name, external_gateway_info=ext_gw_info,
599 admin_state_up=admin_state_up, **kwargs)
600 router = body['router']
601 cls.routers.append(router)
602 return router
603
604 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300605 def create_router(cls, *args, **kwargs):
606 return cls._create_router_with_client(cls.client, *args, **kwargs)
607
608 @classmethod
609 def create_admin_router(cls, *args, **kwargs):
rajat294495c042017-06-28 15:37:16 +0530610 return cls._create_router_with_client(cls.os_admin.network_client,
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300611 *args, **kwargs)
612
613 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200614 def create_floatingip(cls, external_network_id=None, port=None,
615 client=None, **kwargs):
616 """Creates a floating IP.
617
618 Create a floating IP and schedule it for later deletion.
619 If a client is passed, then it is used for deleting the IP too.
620
621 :param external_network_id: network ID where to create
622 By default this is 'CONF.network.public_network_id'.
623
624 :param port: port to bind floating IP to
625 This is translated to 'port_id=port['id']'
626 By default it is None.
627
628 :param client: network client to be used for creating and cleaning up
629 the floating IP.
630
631 :param **kwargs: additional creation parameters to be forwarded to
632 networking server.
633 """
634
635 client = client or cls.client
636 external_network_id = (external_network_id or
637 cls.external_network_id)
638
639 if port:
Federico Ressi47f6ae42018-09-24 16:19:14 +0200640 port_id = kwargs.setdefault('port_id', port['id'])
641 if port_id != port['id']:
642 message = "Port ID specified twice: {!s} != {!s}".format(
643 port_id, port['id'])
644 raise ValueError(message)
Federico Ressia69dcd52018-07-06 09:45:34 +0200645
646 fip = client.create_floatingip(external_network_id,
647 **kwargs)['floatingip']
648
649 # save client to be used later in cls.delete_floatingip
650 # for final cleanup
651 fip['client'] = client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000652 cls.floating_ips.append(fip)
653 return fip
654
655 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200656 def delete_floatingip(cls, floating_ip, client=None):
657 """Delete floating IP
658
659 :param client: Client to be used
660 If client is not given it will use the client used to create
661 the floating IP, or cls.client if unknown.
662 """
663
664 client = client or floating_ip.get('client') or cls.client
665 client.delete_floatingip(floating_ip['id'])
666
667 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200668 def create_port_forwarding(cls, fip_id, internal_port_id,
669 internal_port, external_port,
670 internal_ip_address=None, protocol="tcp",
671 client=None):
672 """Creates a port forwarding.
673
674 Create a port forwarding and schedule it for later deletion.
675 If a client is passed, then it is used for deleting the PF too.
676
677 :param fip_id: The ID of the floating IP address.
678
679 :param internal_port_id: The ID of the Neutron port associated to
680 the floating IP port forwarding.
681
682 :param internal_port: The TCP/UDP/other protocol port number of the
683 Neutron port fixed IP address associated to the floating ip
684 port forwarding.
685
686 :param external_port: The TCP/UDP/other protocol port number of
687 the port forwarding floating IP address.
688
689 :param internal_ip_address: The fixed IPv4 address of the Neutron
690 port associated to the floating IP port forwarding.
691
692 :param protocol: The IP protocol used in the floating IP port
693 forwarding.
694
695 :param client: network client to be used for creating and cleaning up
696 the floating IP port forwarding.
697 """
698
699 client = client or cls.client
700
701 pf = client.create_port_forwarding(
702 fip_id, internal_port_id, internal_port, external_port,
703 internal_ip_address, protocol)['port_forwarding']
704
705 # save ID of floating IP associated with port forwarding for final
706 # cleanup
707 pf['floatingip_id'] = fip_id
708
709 # save client to be used later in cls.delete_port_forwarding
710 # for final cleanup
711 pf['client'] = client
712 cls.port_forwardings.append(pf)
713 return pf
714
715 @classmethod
Flavio Fernandesa1952c62020-10-02 06:39:08 -0400716 def update_port_forwarding(cls, fip_id, pf_id, client=None, **kwargs):
717 """Wrapper utility for update_port_forwarding."""
718 client = client or cls.client
719 return client.update_port_forwarding(fip_id, pf_id, **kwargs)
720
721 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200722 def delete_port_forwarding(cls, pf, client=None):
723 """Delete port forwarding
724
725 :param client: Client to be used
726 If client is not given it will use the client used to create
727 the port forwarding, or cls.client if unknown.
728 """
729
730 client = client or pf.get('client') or cls.client
731 client.delete_port_forwarding(pf['floatingip_id'], pf['id'])
732
733 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000734 def create_router_interface(cls, router_id, subnet_id):
735 """Wrapper utility that returns a router interface."""
736 interface = cls.client.add_router_interface_with_subnet_id(
737 router_id, subnet_id)
738 return interface
739
740 @classmethod
Bence Romsics46bd3af2019-09-13 10:52:41 +0200741 def add_extra_routes_atomic(cls, *args, **kwargs):
742 return cls.client.add_extra_routes_atomic(*args, **kwargs)
743
744 @classmethod
745 def remove_extra_routes_atomic(cls, *args, **kwargs):
746 return cls.client.remove_extra_routes_atomic(*args, **kwargs)
747
748 @classmethod
Sławek Kapłońskiff294062016-12-04 15:00:54 +0000749 def get_supported_qos_rule_types(cls):
750 body = cls.client.list_qos_rule_types()
751 return [rule_type['type'] for rule_type in body['rule_types']]
752
753 @classmethod
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +0200754 def create_qos_policy(cls, name, description=None, shared=False,
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000755 project_id=None, is_default=False):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000756 """Wrapper utility that returns a test QoS policy."""
757 body = cls.admin_client.create_qos_policy(
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000758 name, description, shared, project_id, is_default)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000759 qos_policy = body['policy']
760 cls.qos_policies.append(qos_policy)
761 return qos_policy
762
763 @classmethod
Sławek Kapłoński153f3452017-03-24 22:04:53 +0000764 def create_qos_bandwidth_limit_rule(cls, policy_id, max_kbps,
765 max_burst_kbps,
Chandan Kumarc125fd12017-11-15 19:41:01 +0530766 direction=const.EGRESS_DIRECTION):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000767 """Wrapper utility that returns a test QoS bandwidth limit rule."""
768 body = cls.admin_client.create_bandwidth_limit_rule(
Sławek Kapłoński153f3452017-03-24 22:04:53 +0000769 policy_id, max_kbps, max_burst_kbps, direction)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000770 qos_rule = body['bandwidth_limit_rule']
771 cls.qos_rules.append(qos_rule)
772 return qos_rule
773
774 @classmethod
Lajos Katona2f904652018-08-23 14:04:56 +0200775 def create_qos_minimum_bandwidth_rule(cls, policy_id, min_kbps,
776 direction=const.EGRESS_DIRECTION):
777 """Wrapper utility that creates and returns a QoS min bw rule."""
778 body = cls.admin_client.create_minimum_bandwidth_rule(
779 policy_id, direction, min_kbps)
780 qos_rule = body['minimum_bandwidth_rule']
781 cls.qos_rules.append(qos_rule)
782 return qos_rule
783
784 @classmethod
Jakub Libosvar83704832017-12-06 16:02:28 +0000785 def delete_router(cls, router, client=None):
786 client = client or cls.client
Aditya Vaja49819a72018-11-26 14:20:10 -0800787 if 'routes' in router:
788 client.remove_router_extra_routes(router['id'])
Jakub Libosvar83704832017-12-06 16:02:28 +0000789 body = client.list_router_interfaces(router['id'])
Chandan Kumarc125fd12017-11-15 19:41:01 +0530790 interfaces = [port for port in body['ports']
791 if port['device_owner'] in const.ROUTER_INTERFACE_OWNERS]
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000792 for i in interfaces:
793 try:
Jakub Libosvar83704832017-12-06 16:02:28 +0000794 client.remove_router_interface_with_subnet_id(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000795 router['id'], i['fixed_ips'][0]['subnet_id'])
796 except lib_exc.NotFound:
797 pass
Jakub Libosvar83704832017-12-06 16:02:28 +0000798 client.delete_router(router['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000799
800 @classmethod
801 def create_address_scope(cls, name, is_admin=False, **kwargs):
802 if is_admin:
803 body = cls.admin_client.create_address_scope(name=name, **kwargs)
804 cls.admin_address_scopes.append(body['address_scope'])
805 else:
806 body = cls.client.create_address_scope(name=name, **kwargs)
807 cls.address_scopes.append(body['address_scope'])
808 return body['address_scope']
809
810 @classmethod
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200811 def create_subnetpool(cls, name, is_admin=False, client=None, **kwargs):
812 if client is None:
813 client = cls.admin_client if is_admin else cls.client
814
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000815 if is_admin:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200816 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000817 cls.admin_subnetpools.append(body['subnetpool'])
818 else:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200819 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000820 cls.subnetpools.append(body['subnetpool'])
821 return body['subnetpool']
822
Chandan Kumarc125fd12017-11-15 19:41:01 +0530823 @classmethod
824 def create_project(cls, name=None, description=None):
825 test_project = name or data_utils.rand_name('test_project_')
826 test_description = description or data_utils.rand_name('desc_')
827 project = cls.identity_admin_client.create_project(
828 name=test_project,
829 description=test_description)['project']
830 cls.projects.append(project)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000831 # Create a project will create a default security group.
Dongcan Ye2de722e2018-07-04 11:01:37 +0000832 sgs_list = cls.admin_client.list_security_groups(
833 tenant_id=project['id'])['security_groups']
Federico Ressi4c590d72018-10-10 14:01:08 +0200834 for security_group in sgs_list:
835 # Make sure delete_security_group method will use
836 # the admin client for this group
837 security_group['client'] = cls.admin_client
838 cls.security_groups.append(security_group)
Chandan Kumarc125fd12017-11-15 19:41:01 +0530839 return project
840
841 @classmethod
Federico Ressi4c590d72018-10-10 14:01:08 +0200842 def create_security_group(cls, name=None, project=None, client=None,
843 **kwargs):
844 if project:
845 client = client or cls.admin_client
846 project_id = kwargs.setdefault('project_id', project['id'])
847 tenant_id = kwargs.setdefault('tenant_id', project['id'])
848 if project_id != project['id'] or tenant_id != project['id']:
849 raise ValueError('Project ID specified multiple times')
850 else:
851 client = client or cls.client
852
853 name = name or data_utils.rand_name(cls.__name__)
854 security_group = client.create_security_group(name=name, **kwargs)[
855 'security_group']
856 security_group['client'] = client
857 cls.security_groups.append(security_group)
858 return security_group
859
860 @classmethod
861 def delete_security_group(cls, security_group, client=None):
862 client = client or security_group.get('client') or cls.client
863 client.delete_security_group(security_group['id'])
864
865 @classmethod
866 def create_security_group_rule(cls, security_group=None, project=None,
867 client=None, ip_version=None, **kwargs):
868 if project:
869 client = client or cls.admin_client
870 project_id = kwargs.setdefault('project_id', project['id'])
871 tenant_id = kwargs.setdefault('tenant_id', project['id'])
872 if project_id != project['id'] or tenant_id != project['id']:
873 raise ValueError('Project ID specified multiple times')
874
875 if 'security_group_id' not in kwargs:
876 security_group = (security_group or
877 cls.get_security_group(client=client))
878
879 if security_group:
880 client = client or security_group.get('client')
881 security_group_id = kwargs.setdefault('security_group_id',
882 security_group['id'])
883 if security_group_id != security_group['id']:
884 raise ValueError('Security group ID specified multiple times.')
885
886 ip_version = ip_version or cls._ip_version
887 default_params = (
888 constants.DEFAULT_SECURITY_GROUP_RULE_PARAMS[ip_version])
889 for key, value in default_params.items():
890 kwargs.setdefault(key, value)
891
892 client = client or cls.client
893 return client.create_security_group_rule(**kwargs)[
894 'security_group_rule']
895
896 @classmethod
897 def get_security_group(cls, name='default', client=None):
898 client = client or cls.client
899 security_groups = client.list_security_groups()['security_groups']
900 for security_group in security_groups:
901 if security_group['name'] == name:
902 return security_group
903 raise ValueError("No such security group named {!r}".format(name))
Chandan Kumarc125fd12017-11-15 19:41:01 +0530904
Federico Ressiab286e42018-06-19 09:52:10 +0200905 @classmethod
906 def create_keypair(cls, client=None, name=None, **kwargs):
907 client = client or cls.os_primary.keypairs_client
908 name = name or data_utils.rand_name('keypair-test')
909 keypair = client.create_keypair(name=name, **kwargs)['keypair']
910
911 # save client for later cleanup
912 keypair['client'] = client
913 cls.keypairs.append(keypair)
914 return keypair
915
916 @classmethod
917 def delete_keypair(cls, keypair, client=None):
918 client = (client or keypair.get('client') or
919 cls.os_primary.keypairs_client)
920 client.delete_keypair(keypair_name=keypair['name'])
921
Federico Ressi82e83e32018-07-03 14:19:55 +0200922 @classmethod
923 def create_trunk(cls, port=None, subports=None, client=None, **kwargs):
924 """Create network trunk
925
926 :param port: dictionary containing parent port ID (port['id'])
927 :param client: client to be used for connecting to networking service
928 :param **kwargs: extra parameters to be forwarded to network service
929
930 :returns: dictionary containing created trunk details
931 """
932 client = client or cls.client
933
934 if port:
935 kwargs['port_id'] = port['id']
936
937 trunk = client.create_trunk(subports=subports, **kwargs)['trunk']
938 # Save client reference for later deletion
939 trunk['client'] = client
940 cls.trunks.append(trunk)
941 return trunk
942
943 @classmethod
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800944 def delete_trunk(cls, trunk, client=None, detach_parent_port=True):
Federico Ressi82e83e32018-07-03 14:19:55 +0200945 """Delete network trunk
946
947 :param trunk: dictionary containing trunk ID (trunk['id'])
948
949 :param client: client to be used for connecting to networking service
950 """
951 client = client or trunk.get('client') or cls.client
952 trunk.update(client.show_trunk(trunk['id'])['trunk'])
953
954 if not trunk['admin_state_up']:
955 # Cannot touch trunk before admin_state_up is True
956 client.update_trunk(trunk['id'], admin_state_up=True)
957 if trunk['sub_ports']:
958 # Removes trunk ports before deleting it
959 cls._try_delete_resource(client.remove_subports, trunk['id'],
960 trunk['sub_ports'])
961
962 # we have to detach the interface from the server before
963 # the trunk can be deleted.
964 parent_port = {'id': trunk['port_id']}
965
966 def is_parent_port_detached():
967 parent_port.update(client.show_port(parent_port['id'])['port'])
968 return not parent_port['device_id']
969
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800970 if detach_parent_port and not is_parent_port_detached():
Federico Ressi82e83e32018-07-03 14:19:55 +0200971 # this could probably happen when trunk is deleted and parent port
972 # has been assigned to a VM that is still running. Here we are
973 # assuming that device_id points to such VM.
974 cls.os_primary.compute.InterfacesClient().delete_interface(
975 parent_port['device_id'], parent_port['id'])
976 utils.wait_until_true(is_parent_port_detached)
977
978 client.delete_trunk(trunk['id'])
979
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200980 @classmethod
981 def create_conntrack_helper(cls, router_id, helper, protocol, port,
982 client=None):
983 """Create a conntrack helper
984
985 Create a conntrack helper and schedule it for later deletion. If a
986 client is passed, then it is used for deleteing the CTH too.
987
988 :param router_id: The ID of the Neutron router associated to the
989 conntrack helper.
990
991 :param helper: The conntrack helper module alias
992
993 :param protocol: The conntrack helper IP protocol used in the conntrack
994 helper.
995
996 :param port: The conntrack helper IP protocol port number for the
997 conntrack helper.
998
999 :param client: network client to be used for creating and cleaning up
1000 the conntrack helper.
1001 """
1002
1003 client = client or cls.client
1004
1005 cth = client.create_conntrack_helper(router_id, helper, protocol,
1006 port)['conntrack_helper']
1007
1008 # save ID of router associated with conntrack helper for final cleanup
1009 cth['router_id'] = router_id
1010
1011 # save client to be used later in cls.delete_conntrack_helper for final
1012 # cleanup
1013 cth['client'] = client
1014 cls.conntrack_helpers.append(cth)
1015 return cth
1016
1017 @classmethod
1018 def delete_conntrack_helper(cls, cth, client=None):
1019 """Delete conntrack helper
1020
1021 :param client: Client to be used
1022 If client is not given it will use the client used to create the
1023 conntrack helper, or cls.client if unknown.
1024 """
1025
1026 client = client or cth.get('client') or cls.client
1027 client.delete_conntrack_helper(cth['router_id'], cth['id'])
1028
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001029
1030class BaseAdminNetworkTest(BaseNetworkTest):
1031
1032 credentials = ['primary', 'admin']
1033
1034 @classmethod
1035 def setup_clients(cls):
1036 super(BaseAdminNetworkTest, cls).setup_clients()
fumihiko kakumaa216fc12017-07-14 10:43:29 +09001037 cls.admin_client = cls.os_admin.network_client
Jakub Libosvarf5758012017-08-15 13:45:30 +00001038 cls.identity_admin_client = cls.os_admin.projects_client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001039
1040 @classmethod
1041 def create_metering_label(cls, name, description):
1042 """Wrapper utility that returns a test metering label."""
1043 body = cls.admin_client.create_metering_label(
1044 description=description,
1045 name=data_utils.rand_name("metering-label"))
1046 metering_label = body['metering_label']
1047 cls.metering_labels.append(metering_label)
1048 return metering_label
1049
1050 @classmethod
1051 def create_metering_label_rule(cls, remote_ip_prefix, direction,
1052 metering_label_id):
1053 """Wrapper utility that returns a test metering label rule."""
1054 body = cls.admin_client.create_metering_label_rule(
1055 remote_ip_prefix=remote_ip_prefix, direction=direction,
1056 metering_label_id=metering_label_id)
1057 metering_label_rule = body['metering_label_rule']
1058 cls.metering_label_rules.append(metering_label_rule)
1059 return metering_label_rule
1060
1061 @classmethod
Kailun Qineaaf9782018-12-20 04:45:01 +08001062 def create_network_segment_range(cls, name, shared,
1063 project_id, network_type,
1064 physical_network, minimum,
1065 maximum):
1066 """Wrapper utility that returns a test network segment range."""
1067 network_segment_range_args = {'name': name,
1068 'shared': shared,
1069 'project_id': project_id,
1070 'network_type': network_type,
1071 'physical_network': physical_network,
1072 'minimum': minimum,
1073 'maximum': maximum}
1074 body = cls.admin_client.create_network_segment_range(
1075 **network_segment_range_args)
1076 network_segment_range = body['network_segment_range']
1077 cls.network_segment_ranges.append(network_segment_range)
1078 return network_segment_range
1079
1080 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001081 def create_flavor(cls, name, description, service_type):
1082 """Wrapper utility that returns a test flavor."""
1083 body = cls.admin_client.create_flavor(
1084 description=description, service_type=service_type,
1085 name=name)
1086 flavor = body['flavor']
1087 cls.flavors.append(flavor)
1088 return flavor
1089
1090 @classmethod
1091 def create_service_profile(cls, description, metainfo, driver):
1092 """Wrapper utility that returns a test service profile."""
1093 body = cls.admin_client.create_service_profile(
1094 driver=driver, metainfo=metainfo, description=description)
1095 service_profile = body['service_profile']
1096 cls.service_profiles.append(service_profile)
1097 return service_profile
1098
1099 @classmethod
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001100 def create_log(cls, name, description=None,
1101 resource_type='security_group', resource_id=None,
1102 target_id=None, event='ALL', enabled=True):
1103 """Wrapper utility that returns a test log object."""
1104 log_args = {'name': name,
1105 'description': description,
1106 'resource_type': resource_type,
1107 'resource_id': resource_id,
1108 'target_id': target_id,
1109 'event': event,
1110 'enabled': enabled}
1111 body = cls.admin_client.create_log(**log_args)
1112 log_object = body['log']
1113 cls.log_objects.append(log_object)
1114 return log_object
1115
1116 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001117 def get_unused_ip(cls, net_id, ip_version=None):
Gary Kotton011345f2016-06-15 08:04:31 -07001118 """Get an unused ip address in a allocation pool of net"""
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001119 body = cls.admin_client.list_ports(network_id=net_id)
1120 ports = body['ports']
1121 used_ips = []
1122 for port in ports:
1123 used_ips.extend(
1124 [fixed_ip['ip_address'] for fixed_ip in port['fixed_ips']])
1125 body = cls.admin_client.list_subnets(network_id=net_id)
1126 subnets = body['subnets']
1127
1128 for subnet in subnets:
1129 if ip_version and subnet['ip_version'] != ip_version:
1130 continue
1131 cidr = subnet['cidr']
1132 allocation_pools = subnet['allocation_pools']
1133 iterators = []
1134 if allocation_pools:
1135 for allocation_pool in allocation_pools:
1136 iterators.append(netaddr.iter_iprange(
1137 allocation_pool['start'], allocation_pool['end']))
1138 else:
1139 net = netaddr.IPNetwork(cidr)
1140
1141 def _iterip():
1142 for ip in net:
1143 if ip not in (net.network, net.broadcast):
1144 yield ip
1145 iterators.append(iter(_iterip()))
1146
1147 for iterator in iterators:
1148 for ip in iterator:
1149 if str(ip) not in used_ips:
1150 return str(ip)
1151
1152 message = (
1153 "net(%s) has no usable IP address in allocation pools" % net_id)
1154 raise exceptions.InvalidConfiguration(message)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001155
Lajos Katona2f904652018-08-23 14:04:56 +02001156 @classmethod
1157 def create_provider_network(cls, physnet_name, start_segmentation_id,
1158 max_attempts=30):
1159 segmentation_id = start_segmentation_id
Lajos Katona7eb67252019-01-14 12:55:35 +01001160 for attempts in range(max_attempts):
Lajos Katona2f904652018-08-23 14:04:56 +02001161 try:
Lajos Katona7eb67252019-01-14 12:55:35 +01001162 return cls.create_network(
Lajos Katona2f904652018-08-23 14:04:56 +02001163 name=data_utils.rand_name('test_net'),
1164 shared=True,
1165 provider_network_type='vlan',
1166 provider_physical_network=physnet_name,
1167 provider_segmentation_id=segmentation_id)
Lajos Katona2f904652018-08-23 14:04:56 +02001168 except lib_exc.Conflict:
Lajos Katona2f904652018-08-23 14:04:56 +02001169 segmentation_id += 1
1170 if segmentation_id > 4095:
1171 raise lib_exc.TempestException(
1172 "No free segmentation id was found for provider "
1173 "network creation!")
1174 time.sleep(CONF.network.build_interval)
Lajos Katona7eb67252019-01-14 12:55:35 +01001175 LOG.exception("Failed to create provider network after "
1176 "%d attempts", max_attempts)
1177 raise lib_exc.TimeoutException
Lajos Katona2f904652018-08-23 14:04:56 +02001178
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001179
Sławek Kapłońskiff294062016-12-04 15:00:54 +00001180def require_qos_rule_type(rule_type):
1181 def decorator(f):
1182 @functools.wraps(f)
1183 def wrapper(self, *func_args, **func_kwargs):
1184 if rule_type not in self.get_supported_qos_rule_types():
1185 raise self.skipException(
1186 "%s rule type is required." % rule_type)
1187 return f(self, *func_args, **func_kwargs)
1188 return wrapper
1189 return decorator
1190
1191
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001192def _require_sorting(f):
1193 @functools.wraps(f)
1194 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301195 if not tutils.is_extension_enabled("sorting", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001196 self.skipTest('Sorting feature is required')
1197 return f(self, *args, **kwargs)
1198 return inner
1199
1200
1201def _require_pagination(f):
1202 @functools.wraps(f)
1203 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301204 if not tutils.is_extension_enabled("pagination", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001205 self.skipTest('Pagination feature is required')
1206 return f(self, *args, **kwargs)
1207 return inner
1208
1209
1210class BaseSearchCriteriaTest(BaseNetworkTest):
1211
1212 # This should be defined by subclasses to reflect resource name to test
1213 resource = None
1214
Armando Migliaccio57581c62016-07-01 10:13:19 -07001215 field = 'name'
1216
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001217 # NOTE(ihrachys): some names, like those starting with an underscore (_)
1218 # are sorted differently depending on whether the plugin implements native
1219 # sorting support, or not. So we avoid any such cases here, sticking to
1220 # alphanumeric. Also test a case when there are multiple resources with the
1221 # same name
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001222 resource_names = ('test1', 'abc1', 'test10', '123test') + ('test1',)
1223
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001224 force_tenant_isolation = True
1225
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001226 list_kwargs = {}
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001227
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001228 list_as_admin = False
1229
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001230 def assertSameOrder(self, original, actual):
1231 # gracefully handle iterators passed
1232 original = list(original)
1233 actual = list(actual)
1234 self.assertEqual(len(original), len(actual))
1235 for expected, res in zip(original, actual):
Armando Migliaccio57581c62016-07-01 10:13:19 -07001236 self.assertEqual(expected[self.field], res[self.field])
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001237
1238 @utils.classproperty
1239 def plural_name(self):
1240 return '%ss' % self.resource
1241
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001242 @property
1243 def list_client(self):
1244 return self.admin_client if self.list_as_admin else self.client
1245
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001246 def list_method(self, *args, **kwargs):
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001247 method = getattr(self.list_client, 'list_%s' % self.plural_name)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001248 kwargs.update(self.list_kwargs)
1249 return method(*args, **kwargs)
1250
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001251 def get_bare_url(self, url):
1252 base_url = self.client.base_url
zheng.yong74e760a2019-05-22 14:16:14 +08001253 base_url_normalized = utils.normalize_url(base_url)
1254 url_normalized = utils.normalize_url(url)
1255 self.assertTrue(url_normalized.startswith(base_url_normalized))
1256 return url_normalized[len(base_url_normalized):]
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001257
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001258 @classmethod
1259 def _extract_resources(cls, body):
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001260 return body[cls.plural_name]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001261
1262 def _test_list_sorts(self, direction):
1263 sort_args = {
1264 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001265 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001266 }
1267 body = self.list_method(**sort_args)
1268 resources = self._extract_resources(body)
1269 self.assertNotEmpty(
1270 resources, "%s list returned is empty" % self.resource)
Armando Migliaccio57581c62016-07-01 10:13:19 -07001271 retrieved_names = [res[self.field] for res in resources]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001272 expected = sorted(retrieved_names)
1273 if direction == constants.SORT_DIRECTION_DESC:
1274 expected = list(reversed(expected))
1275 self.assertEqual(expected, retrieved_names)
1276
1277 @_require_sorting
1278 def _test_list_sorts_asc(self):
1279 self._test_list_sorts(constants.SORT_DIRECTION_ASC)
1280
1281 @_require_sorting
1282 def _test_list_sorts_desc(self):
1283 self._test_list_sorts(constants.SORT_DIRECTION_DESC)
1284
1285 @_require_pagination
1286 def _test_list_pagination(self):
1287 for limit in range(1, len(self.resource_names) + 1):
1288 pagination_args = {
1289 'limit': limit,
1290 }
1291 body = self.list_method(**pagination_args)
1292 resources = self._extract_resources(body)
1293 self.assertEqual(limit, len(resources))
1294
1295 @_require_pagination
1296 def _test_list_no_pagination_limit_0(self):
1297 pagination_args = {
1298 'limit': 0,
1299 }
1300 body = self.list_method(**pagination_args)
1301 resources = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001302 self.assertGreaterEqual(len(resources), len(self.resource_names))
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001303
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001304 def _test_list_pagination_iteratively(self, lister):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001305 # first, collect all resources for later comparison
1306 sort_args = {
1307 'sort_dir': constants.SORT_DIRECTION_ASC,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001308 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001309 }
1310 body = self.list_method(**sort_args)
1311 expected_resources = self._extract_resources(body)
1312 self.assertNotEmpty(expected_resources)
1313
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001314 resources = lister(
1315 len(expected_resources), sort_args
1316 )
1317
1318 # finally, compare that the list retrieved in one go is identical to
1319 # the one containing pagination results
1320 self.assertSameOrder(expected_resources, resources)
1321
1322 def _list_all_with_marker(self, niterations, sort_args):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001323 # paginate resources one by one, using last fetched resource as a
1324 # marker
1325 resources = []
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001326 for i in range(niterations):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001327 pagination_args = sort_args.copy()
1328 pagination_args['limit'] = 1
1329 if resources:
1330 pagination_args['marker'] = resources[-1]['id']
1331 body = self.list_method(**pagination_args)
1332 resources_ = self._extract_resources(body)
1333 self.assertEqual(1, len(resources_))
1334 resources.extend(resources_)
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001335 return resources
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001336
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001337 @_require_pagination
1338 @_require_sorting
1339 def _test_list_pagination_with_marker(self):
1340 self._test_list_pagination_iteratively(self._list_all_with_marker)
1341
1342 def _list_all_with_hrefs(self, niterations, sort_args):
1343 # paginate resources one by one, using next href links
1344 resources = []
1345 prev_links = {}
1346
1347 for i in range(niterations):
1348 if prev_links:
1349 uri = self.get_bare_url(prev_links['next'])
1350 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001351 sort_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001352 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001353 self.plural_name, limit=1, **sort_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001354 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001355 self.plural_name, uri
1356 )
1357 resources_ = self._extract_resources(body)
1358 self.assertEqual(1, len(resources_))
1359 resources.extend(resources_)
1360
1361 # The last element is empty and does not contain 'next' link
1362 uri = self.get_bare_url(prev_links['next'])
1363 prev_links, body = self.client.get_uri_with_links(
1364 self.plural_name, uri
1365 )
1366 self.assertNotIn('next', prev_links)
1367
1368 # Now walk backwards and compare results
1369 resources2 = []
1370 for i in range(niterations):
1371 uri = self.get_bare_url(prev_links['previous'])
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001372 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001373 self.plural_name, uri
1374 )
1375 resources_ = self._extract_resources(body)
1376 self.assertEqual(1, len(resources_))
1377 resources2.extend(resources_)
1378
1379 self.assertSameOrder(resources, reversed(resources2))
1380
1381 return resources
1382
1383 @_require_pagination
1384 @_require_sorting
1385 def _test_list_pagination_with_href_links(self):
1386 self._test_list_pagination_iteratively(self._list_all_with_hrefs)
1387
1388 @_require_pagination
1389 @_require_sorting
1390 def _test_list_pagination_page_reverse_with_href_links(
1391 self, direction=constants.SORT_DIRECTION_ASC):
1392 pagination_args = {
1393 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001394 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001395 }
1396 body = self.list_method(**pagination_args)
1397 expected_resources = self._extract_resources(body)
1398
1399 page_size = 2
1400 pagination_args['limit'] = page_size
1401
1402 prev_links = {}
1403 resources = []
1404 num_resources = len(expected_resources)
1405 niterations = int(math.ceil(float(num_resources) / page_size))
1406 for i in range(niterations):
1407 if prev_links:
1408 uri = self.get_bare_url(prev_links['previous'])
1409 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001410 pagination_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001411 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001412 self.plural_name, page_reverse=True, **pagination_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001413 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001414 self.plural_name, uri
1415 )
1416 resources_ = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001417 self.assertGreaterEqual(page_size, len(resources_))
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001418 resources.extend(reversed(resources_))
1419
1420 self.assertSameOrder(expected_resources, reversed(resources))
1421
1422 @_require_pagination
1423 @_require_sorting
1424 def _test_list_pagination_page_reverse_asc(self):
1425 self._test_list_pagination_page_reverse(
1426 direction=constants.SORT_DIRECTION_ASC)
1427
1428 @_require_pagination
1429 @_require_sorting
1430 def _test_list_pagination_page_reverse_desc(self):
1431 self._test_list_pagination_page_reverse(
1432 direction=constants.SORT_DIRECTION_DESC)
1433
1434 def _test_list_pagination_page_reverse(self, direction):
1435 pagination_args = {
1436 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001437 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001438 'limit': 3,
1439 }
1440 body = self.list_method(**pagination_args)
1441 expected_resources = self._extract_resources(body)
1442
1443 pagination_args['limit'] -= 1
1444 pagination_args['marker'] = expected_resources[-1]['id']
1445 pagination_args['page_reverse'] = True
1446 body = self.list_method(**pagination_args)
1447
1448 self.assertSameOrder(
1449 # the last entry is not included in 2nd result when used as a
1450 # marker
1451 expected_resources[:-1],
1452 self._extract_resources(body))
Victor Morales1be97b42016-09-05 08:50:06 -05001453
Hongbin Lu54f55922018-07-12 19:05:39 +00001454 @tutils.requires_ext(extension="filter-validation", service="network")
1455 def _test_list_validation_filters(
1456 self, validation_args, filter_is_valid=True):
1457 if not filter_is_valid:
1458 self.assertRaises(lib_exc.BadRequest, self.list_method,
1459 **validation_args)
1460 else:
1461 body = self.list_method(**validation_args)
1462 resources = self._extract_resources(body)
1463 for resource in resources:
1464 self.assertIn(resource['name'], self.resource_names)