blob: dbb64466fda8854a468096233000855534f5cf68 [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
Vasyl Saienko4d5b1112021-10-22 14:34:41 +0300109 # NOTE(vsaienko): when using static accounts we need
110 # to fill *_id information like project_id, user_id
111 # by authenticating in keystone
112 cls.client.auth_provider.get_token()
113
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000114 @classmethod
115 def resource_setup(cls):
116 super(BaseNetworkTest, cls).resource_setup()
117
118 cls.networks = []
Miguel Lavalle124378b2016-09-21 16:41:47 -0500119 cls.admin_networks = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000120 cls.subnets = []
Kevin Bentonba3651c2017-09-01 17:13:01 -0700121 cls.admin_subnets = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000122 cls.ports = []
123 cls.routers = []
124 cls.floating_ips = []
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200125 cls.port_forwardings = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000126 cls.metering_labels = []
127 cls.service_profiles = []
128 cls.flavors = []
129 cls.metering_label_rules = []
130 cls.qos_rules = []
131 cls.qos_policies = []
132 cls.ethertype = "IPv" + str(cls._ip_version)
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600133 cls.address_groups = []
134 cls.admin_address_groups = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000135 cls.address_scopes = []
136 cls.admin_address_scopes = []
137 cls.subnetpools = []
138 cls.admin_subnetpools = []
Itzik Brownbac51dc2016-10-31 12:25:04 +0000139 cls.security_groups = []
Dongcan Ye2de722e2018-07-04 11:01:37 +0000140 cls.admin_security_groups = []
Chandan Kumarc125fd12017-11-15 19:41:01 +0530141 cls.projects = []
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700142 cls.log_objects = []
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200143 cls.reserved_subnet_cidrs = set()
Federico Ressiab286e42018-06-19 09:52:10 +0200144 cls.keypairs = []
Federico Ressi82e83e32018-07-03 14:19:55 +0200145 cls.trunks = []
Kailun Qineaaf9782018-12-20 04:45:01 +0800146 cls.network_segment_ranges = []
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200147 cls.conntrack_helpers = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000148
149 @classmethod
yangjianfeng23e40c22020-11-22 08:42:18 +0000150 def reserve_external_subnet_cidrs(cls):
151 client = cls.os_admin.network_client
152 ext_nets = client.list_networks(
153 **{"router:external": True})['networks']
154 for ext_net in ext_nets:
155 ext_subnets = client.list_subnets(
156 network_id=ext_net['id'])['subnets']
157 for ext_subnet in ext_subnets:
158 cls.reserve_subnet_cidr(ext_subnet['cidr'])
159
160 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000161 def resource_cleanup(cls):
162 if CONF.service_available.neutron:
Federico Ressi82e83e32018-07-03 14:19:55 +0200163 # Clean up trunks
164 for trunk in cls.trunks:
165 cls._try_delete_resource(cls.delete_trunk, trunk)
166
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200167 # Clean up port forwardings
168 for pf in cls.port_forwardings:
169 cls._try_delete_resource(cls.delete_port_forwarding, pf)
170
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000171 # Clean up floating IPs
172 for floating_ip in cls.floating_ips:
Federico Ressia69dcd52018-07-06 09:45:34 +0200173 cls._try_delete_resource(cls.delete_floatingip, floating_ip)
174
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200175 # Clean up conntrack helpers
176 for cth in cls.conntrack_helpers:
177 cls._try_delete_resource(cls.delete_conntrack_helper, cth)
178
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000179 # Clean up routers
180 for router in cls.routers:
181 cls._try_delete_resource(cls.delete_router,
182 router)
183 # Clean up metering label rules
184 for metering_label_rule in cls.metering_label_rules:
185 cls._try_delete_resource(
186 cls.admin_client.delete_metering_label_rule,
187 metering_label_rule['id'])
188 # Clean up metering labels
189 for metering_label in cls.metering_labels:
190 cls._try_delete_resource(
191 cls.admin_client.delete_metering_label,
192 metering_label['id'])
193 # Clean up flavors
194 for flavor in cls.flavors:
195 cls._try_delete_resource(
196 cls.admin_client.delete_flavor,
197 flavor['id'])
198 # Clean up service profiles
199 for service_profile in cls.service_profiles:
200 cls._try_delete_resource(
201 cls.admin_client.delete_service_profile,
202 service_profile['id'])
203 # Clean up ports
204 for port in cls.ports:
205 cls._try_delete_resource(cls.client.delete_port,
206 port['id'])
207 # Clean up subnets
208 for subnet in cls.subnets:
209 cls._try_delete_resource(cls.client.delete_subnet,
210 subnet['id'])
Kevin Bentonba3651c2017-09-01 17:13:01 -0700211 # Clean up admin subnets
212 for subnet in cls.admin_subnets:
213 cls._try_delete_resource(cls.admin_client.delete_subnet,
214 subnet['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000215 # Clean up networks
216 for network in cls.networks:
Federico Ressi61b564e2018-07-06 08:10:31 +0200217 cls._try_delete_resource(cls.delete_network, network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000218
Miguel Lavalle124378b2016-09-21 16:41:47 -0500219 # Clean up admin networks
220 for network in cls.admin_networks:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000221 cls._try_delete_resource(cls.admin_client.delete_network,
222 network['id'])
223
Itzik Brownbac51dc2016-10-31 12:25:04 +0000224 # Clean up security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200225 for security_group in cls.security_groups:
226 cls._try_delete_resource(cls.delete_security_group,
227 security_group)
Itzik Brownbac51dc2016-10-31 12:25:04 +0000228
Dongcan Ye2de722e2018-07-04 11:01:37 +0000229 # Clean up admin security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200230 for security_group in cls.admin_security_groups:
231 cls._try_delete_resource(cls.delete_security_group,
232 security_group,
233 client=cls.admin_client)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000234
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000235 for subnetpool in cls.subnetpools:
236 cls._try_delete_resource(cls.client.delete_subnetpool,
237 subnetpool['id'])
238
239 for subnetpool in cls.admin_subnetpools:
240 cls._try_delete_resource(cls.admin_client.delete_subnetpool,
241 subnetpool['id'])
242
243 for address_scope in cls.address_scopes:
244 cls._try_delete_resource(cls.client.delete_address_scope,
245 address_scope['id'])
246
247 for address_scope in cls.admin_address_scopes:
248 cls._try_delete_resource(
249 cls.admin_client.delete_address_scope,
250 address_scope['id'])
251
Chandan Kumarc125fd12017-11-15 19:41:01 +0530252 for project in cls.projects:
253 cls._try_delete_resource(
254 cls.identity_admin_client.delete_project,
255 project['id'])
256
Sławek Kapłońskie100c4d2017-08-23 21:18:34 +0000257 # Clean up QoS rules
258 for qos_rule in cls.qos_rules:
259 cls._try_delete_resource(cls.admin_client.delete_qos_rule,
260 qos_rule['id'])
261 # Clean up QoS policies
262 # as all networks and ports are already removed, QoS policies
263 # shouldn't be "in use"
264 for qos_policy in cls.qos_policies:
265 cls._try_delete_resource(cls.admin_client.delete_qos_policy,
266 qos_policy['id'])
267
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700268 # Clean up log_objects
269 for log_object in cls.log_objects:
270 cls._try_delete_resource(cls.admin_client.delete_log,
271 log_object['id'])
272
Federico Ressiab286e42018-06-19 09:52:10 +0200273 for keypair in cls.keypairs:
274 cls._try_delete_resource(cls.delete_keypair, keypair)
275
Kailun Qineaaf9782018-12-20 04:45:01 +0800276 # Clean up network_segment_ranges
277 for network_segment_range in cls.network_segment_ranges:
278 cls._try_delete_resource(
279 cls.admin_client.delete_network_segment_range,
280 network_segment_range['id'])
281
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000282 super(BaseNetworkTest, cls).resource_cleanup()
283
284 @classmethod
285 def _try_delete_resource(cls, delete_callable, *args, **kwargs):
286 """Cleanup resources in case of test-failure
287
288 Some resources are explicitly deleted by the test.
289 If the test failed to delete a resource, this method will execute
290 the appropriate delete methods. Otherwise, the method ignores NotFound
291 exceptions thrown for resources that were correctly deleted by the
292 test.
293
294 :param delete_callable: delete method
295 :param args: arguments for delete method
296 :param kwargs: keyword arguments for delete method
297 """
298 try:
299 delete_callable(*args, **kwargs)
300 # if resource is not found, this means it was deleted in the test
301 except lib_exc.NotFound:
302 pass
303
304 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200305 def create_network(cls, network_name=None, client=None, external=None,
306 shared=None, provider_network_type=None,
307 provider_physical_network=None,
308 provider_segmentation_id=None, **kwargs):
309 """Create a network.
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000310
Federico Ressi61b564e2018-07-06 08:10:31 +0200311 When client is not provider and admin_client is attribute is not None
312 (for example when using BaseAdminNetworkTest base class) and using any
313 of the convenience parameters (external, shared, provider_network_type,
314 provider_physical_network and provider_segmentation_id) it silently
315 uses admin_client. If the network is not shared then it uses the same
316 project_id as regular client.
317
318 :param network_name: Human-readable name of the network
319
320 :param client: client to be used for connecting to network service
321
322 :param external: indicates whether the network has an external routing
323 facility that's not managed by the networking service.
324
325 :param shared: indicates whether this resource is shared across all
326 projects. By default, only administrative users can change this value.
327 If True and admin_client attribute is not None, then the network is
328 created under administrative project.
329
330 :param provider_network_type: the type of physical network that this
331 network should be mapped to. For example, 'flat', 'vlan', 'vxlan', or
332 'gre'. Valid values depend on a networking back-end.
333
334 :param provider_physical_network: the physical network where this
335 network should be implemented. The Networking API v2.0 does not provide
336 a way to list available physical networks. For example, the Open
337 vSwitch plug-in configuration file defines a symbolic name that maps to
338 specific bridges on each compute host.
339
340 :param provider_segmentation_id: The ID of the isolated segment on the
341 physical network. The network_type attribute defines the segmentation
342 model. For example, if the network_type value is 'vlan', this ID is a
343 vlan identifier. If the network_type value is 'gre', this ID is a gre
344 key.
345
346 :param **kwargs: extra parameters to be forwarded to network service
347 """
348
349 name = (network_name or kwargs.pop('name', None) or
350 data_utils.rand_name('test-network-'))
351
352 # translate convenience parameters
353 admin_client_required = False
354 if provider_network_type:
355 admin_client_required = True
356 kwargs['provider:network_type'] = provider_network_type
357 if provider_physical_network:
358 admin_client_required = True
359 kwargs['provider:physical_network'] = provider_physical_network
360 if provider_segmentation_id:
361 admin_client_required = True
362 kwargs['provider:segmentation_id'] = provider_segmentation_id
363 if external is not None:
364 admin_client_required = True
365 kwargs['router:external'] = bool(external)
366 if shared is not None:
367 admin_client_required = True
368 kwargs['shared'] = bool(shared)
369
370 if not client:
371 if admin_client_required and cls.admin_client:
372 # For convenience silently switch to admin client
373 client = cls.admin_client
374 if not shared:
375 # Keep this network visible from current project
376 project_id = (kwargs.get('project_id') or
377 kwargs.get('tenant_id') or
378 cls.client.tenant_id)
379 kwargs.update(project_id=project_id, tenant_id=project_id)
380 else:
381 # Use default client
382 client = cls.client
383
384 network = client.create_network(name=name, **kwargs)['network']
385 network['client'] = client
386 cls.networks.append(network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000387 return network
388
389 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200390 def delete_network(cls, network, client=None):
391 client = client or network.get('client') or cls.client
392 client.delete_network(network['id'])
393
394 @classmethod
395 def create_shared_network(cls, network_name=None, **kwargs):
396 return cls.create_network(name=network_name, shared=True, **kwargs)
Miguel Lavalle124378b2016-09-21 16:41:47 -0500397
398 @classmethod
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200399 def create_subnet(cls, network, gateway='', cidr=None, mask_bits=None,
Federico Ressi98f20ec2018-05-11 06:09:49 +0200400 ip_version=None, client=None, reserve_cidr=True,
401 **kwargs):
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200402 """Wrapper utility that returns a test subnet.
403
404 Convenient wrapper for client.create_subnet method. It reserves and
405 allocates CIDRs to avoid creating overlapping subnets.
406
407 :param network: network where to create the subnet
408 network['id'] must contain the ID of the network
409
410 :param gateway: gateway IP address
411 It can be a str or a netaddr.IPAddress
412 If gateway is not given, then it will use default address for
413 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 +0200414 if gateway is given as None then no gateway will be assigned
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200415
416 :param cidr: CIDR of the subnet to create
417 It can be either None, a str or a netaddr.IPNetwork instance
418
419 :param mask_bits: CIDR prefix length
420 It can be either None or a numeric value.
421 If cidr parameter is given then mask_bits is used to determinate a
422 sequence of valid CIDR to use as generated.
423 Please see netaddr.IPNetwork.subnet method documentation[1]
424
425 :param ip_version: ip version of generated subnet CIDRs
426 It can be None, IP_VERSION_4 or IP_VERSION_6
427 It has to match given either given CIDR and gateway
428
429 :param ip_version: numeric value (either IP_VERSION_4 or IP_VERSION_6)
430 this value must match CIDR and gateway IP versions if any of them is
431 given
432
433 :param client: client to be used to connect to network service
434
Federico Ressi98f20ec2018-05-11 06:09:49 +0200435 :param reserve_cidr: if True then it reserves assigned CIDR to avoid
436 using the same CIDR for further subnets in the scope of the same
437 test case class
438
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200439 :param **kwargs: optional parameters to be forwarded to wrapped method
440
441 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
442 """
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000443
444 # allow tests to use admin client
445 if not client:
446 client = cls.client
447
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200448 if gateway:
449 gateway_ip = netaddr.IPAddress(gateway)
450 if ip_version:
451 if ip_version != gateway_ip.version:
452 raise ValueError(
453 "Gateway IP version doesn't match IP version")
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000454 else:
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200455 ip_version = gateway_ip.version
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200456 else:
457 ip_version = ip_version or cls._ip_version
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200458
459 for subnet_cidr in cls.get_subnet_cidrs(
460 ip_version=ip_version, cidr=cidr, mask_bits=mask_bits):
Federico Ressi98f20ec2018-05-11 06:09:49 +0200461 if gateway is not None:
462 kwargs['gateway_ip'] = str(gateway or (subnet_cidr.ip + 1))
Slawek Kaplonski21f53422018-11-02 16:02:09 +0100463 else:
464 kwargs['gateway_ip'] = None
Federico Ressi98f20ec2018-05-11 06:09:49 +0200465 try:
466 body = client.create_subnet(
467 network_id=network['id'],
468 cidr=str(subnet_cidr),
469 ip_version=subnet_cidr.version,
470 **kwargs)
471 break
472 except lib_exc.BadRequest as e:
473 if 'overlaps with another subnet' not in str(e):
474 raise
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000475 else:
476 message = 'Available CIDR for subnet creation could not be found'
477 raise ValueError(message)
478 subnet = body['subnet']
Kevin Bentonba3651c2017-09-01 17:13:01 -0700479 if client is cls.client:
480 cls.subnets.append(subnet)
481 else:
482 cls.admin_subnets.append(subnet)
Federico Ressi98f20ec2018-05-11 06:09:49 +0200483 if reserve_cidr:
484 cls.reserve_subnet_cidr(subnet_cidr)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000485 return subnet
486
487 @classmethod
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200488 def reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
489 """Reserve given subnet CIDR making sure it is not used by create_subnet
490
491 :param addr: the CIDR address to be reserved
492 It can be a str or netaddr.IPNetwork instance
493
494 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
495 parameters
496 """
497
498 if not cls.try_reserve_subnet_cidr(addr, **ipnetwork_kwargs):
Bernard Cafarellic3bec862020-09-10 13:59:49 +0200499 raise ValueError('Subnet CIDR already reserved: {0!r}'.format(
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200500 addr))
501
502 @classmethod
503 def try_reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
504 """Reserve given subnet CIDR if it hasn't been reserved before
505
506 :param addr: the CIDR address to be reserved
507 It can be a str or netaddr.IPNetwork instance
508
509 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
510 parameters
511
512 :return: True if it wasn't reserved before, False elsewhere.
513 """
514
515 subnet_cidr = netaddr.IPNetwork(addr, **ipnetwork_kwargs)
516 if subnet_cidr in cls.reserved_subnet_cidrs:
517 return False
518 else:
519 cls.reserved_subnet_cidrs.add(subnet_cidr)
520 return True
521
522 @classmethod
523 def get_subnet_cidrs(
524 cls, cidr=None, mask_bits=None, ip_version=None):
525 """Iterate over a sequence of unused subnet CIDR for IP version
526
527 :param cidr: CIDR of the subnet to create
528 It can be either None, a str or a netaddr.IPNetwork instance
529
530 :param mask_bits: CIDR prefix length
531 It can be either None or a numeric value.
532 If cidr parameter is given then mask_bits is used to determinate a
533 sequence of valid CIDR to use as generated.
534 Please see netaddr.IPNetwork.subnet method documentation[1]
535
536 :param ip_version: ip version of generated subnet CIDRs
537 It can be None, IP_VERSION_4 or IP_VERSION_6
538 It has to match given CIDR if given
539
540 :return: iterator over reserved CIDRs of type netaddr.IPNetwork
541
542 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
543 """
544
545 if cidr:
546 # Generate subnet CIDRs starting from given CIDR
547 # checking it is of requested IP version
548 cidr = netaddr.IPNetwork(cidr, version=ip_version)
549 else:
550 # Generate subnet CIDRs starting from configured values
551 ip_version = ip_version or cls._ip_version
552 if ip_version == const.IP_VERSION_4:
553 mask_bits = mask_bits or config.safe_get_config_value(
554 'network', 'project_network_mask_bits')
555 cidr = netaddr.IPNetwork(config.safe_get_config_value(
556 'network', 'project_network_cidr'))
557 elif ip_version == const.IP_VERSION_6:
558 mask_bits = config.safe_get_config_value(
559 'network', 'project_network_v6_mask_bits')
560 cidr = netaddr.IPNetwork(config.safe_get_config_value(
561 'network', 'project_network_v6_cidr'))
562 else:
563 raise ValueError('Invalid IP version: {!r}'.format(ip_version))
564
565 if mask_bits:
566 subnet_cidrs = cidr.subnet(mask_bits)
567 else:
568 subnet_cidrs = iter([cidr])
569
570 for subnet_cidr in subnet_cidrs:
571 if subnet_cidr not in cls.reserved_subnet_cidrs:
572 yield subnet_cidr
573
574 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000575 def create_port(cls, network, **kwargs):
576 """Wrapper utility that returns a test port."""
Edan Davidd75e48e2018-01-03 02:49:52 -0500577 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
578 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
Glenn Van de Water5d9b1402020-09-16 15:14:14 +0200579 if CONF.network.port_profile and 'binding:profile' not in kwargs:
580 kwargs['binding:profile'] = CONF.network.port_profile
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000581 body = cls.client.create_port(network_id=network['id'],
582 **kwargs)
583 port = body['port']
584 cls.ports.append(port)
585 return port
586
587 @classmethod
588 def update_port(cls, port, **kwargs):
589 """Wrapper utility that updates a test port."""
590 body = cls.client.update_port(port['id'],
591 **kwargs)
592 return body['port']
593
594 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300595 def _create_router_with_client(
596 cls, client, router_name=None, admin_state_up=False,
597 external_network_id=None, enable_snat=None, **kwargs
598 ):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000599 ext_gw_info = {}
600 if external_network_id:
601 ext_gw_info['network_id'] = external_network_id
YAMAMOTO Takashi9bd4f972017-06-20 12:49:30 +0900602 if enable_snat is not None:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000603 ext_gw_info['enable_snat'] = enable_snat
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300604 body = client.create_router(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000605 router_name, external_gateway_info=ext_gw_info,
606 admin_state_up=admin_state_up, **kwargs)
607 router = body['router']
608 cls.routers.append(router)
609 return router
610
611 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300612 def create_router(cls, *args, **kwargs):
613 return cls._create_router_with_client(cls.client, *args, **kwargs)
614
615 @classmethod
616 def create_admin_router(cls, *args, **kwargs):
rajat294495c042017-06-28 15:37:16 +0530617 return cls._create_router_with_client(cls.os_admin.network_client,
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300618 *args, **kwargs)
619
620 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200621 def create_floatingip(cls, external_network_id=None, port=None,
622 client=None, **kwargs):
623 """Creates a floating IP.
624
625 Create a floating IP and schedule it for later deletion.
626 If a client is passed, then it is used for deleting the IP too.
627
628 :param external_network_id: network ID where to create
629 By default this is 'CONF.network.public_network_id'.
630
631 :param port: port to bind floating IP to
632 This is translated to 'port_id=port['id']'
633 By default it is None.
634
635 :param client: network client to be used for creating and cleaning up
636 the floating IP.
637
638 :param **kwargs: additional creation parameters to be forwarded to
639 networking server.
640 """
641
642 client = client or cls.client
643 external_network_id = (external_network_id or
644 cls.external_network_id)
645
646 if port:
Federico Ressi47f6ae42018-09-24 16:19:14 +0200647 port_id = kwargs.setdefault('port_id', port['id'])
648 if port_id != port['id']:
649 message = "Port ID specified twice: {!s} != {!s}".format(
650 port_id, port['id'])
651 raise ValueError(message)
Federico Ressia69dcd52018-07-06 09:45:34 +0200652
653 fip = client.create_floatingip(external_network_id,
654 **kwargs)['floatingip']
655
656 # save client to be used later in cls.delete_floatingip
657 # for final cleanup
658 fip['client'] = client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000659 cls.floating_ips.append(fip)
660 return fip
661
662 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200663 def delete_floatingip(cls, floating_ip, client=None):
664 """Delete floating IP
665
666 :param client: Client to be used
667 If client is not given it will use the client used to create
668 the floating IP, or cls.client if unknown.
669 """
670
671 client = client or floating_ip.get('client') or cls.client
672 client.delete_floatingip(floating_ip['id'])
673
674 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200675 def create_port_forwarding(cls, fip_id, internal_port_id,
676 internal_port, external_port,
677 internal_ip_address=None, protocol="tcp",
678 client=None):
679 """Creates a port forwarding.
680
681 Create a port forwarding and schedule it for later deletion.
682 If a client is passed, then it is used for deleting the PF too.
683
684 :param fip_id: The ID of the floating IP address.
685
686 :param internal_port_id: The ID of the Neutron port associated to
687 the floating IP port forwarding.
688
689 :param internal_port: The TCP/UDP/other protocol port number of the
690 Neutron port fixed IP address associated to the floating ip
691 port forwarding.
692
693 :param external_port: The TCP/UDP/other protocol port number of
694 the port forwarding floating IP address.
695
696 :param internal_ip_address: The fixed IPv4 address of the Neutron
697 port associated to the floating IP port forwarding.
698
699 :param protocol: The IP protocol used in the floating IP port
700 forwarding.
701
702 :param client: network client to be used for creating and cleaning up
703 the floating IP port forwarding.
704 """
705
706 client = client or cls.client
707
708 pf = client.create_port_forwarding(
709 fip_id, internal_port_id, internal_port, external_port,
710 internal_ip_address, protocol)['port_forwarding']
711
712 # save ID of floating IP associated with port forwarding for final
713 # cleanup
714 pf['floatingip_id'] = fip_id
715
716 # save client to be used later in cls.delete_port_forwarding
717 # for final cleanup
718 pf['client'] = client
719 cls.port_forwardings.append(pf)
720 return pf
721
722 @classmethod
Flavio Fernandesa1952c62020-10-02 06:39:08 -0400723 def update_port_forwarding(cls, fip_id, pf_id, client=None, **kwargs):
724 """Wrapper utility for update_port_forwarding."""
725 client = client or cls.client
726 return client.update_port_forwarding(fip_id, pf_id, **kwargs)
727
728 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200729 def delete_port_forwarding(cls, pf, client=None):
730 """Delete port forwarding
731
732 :param client: Client to be used
733 If client is not given it will use the client used to create
734 the port forwarding, or cls.client if unknown.
735 """
736
737 client = client or pf.get('client') or cls.client
738 client.delete_port_forwarding(pf['floatingip_id'], pf['id'])
739
740 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000741 def create_router_interface(cls, router_id, subnet_id):
742 """Wrapper utility that returns a router interface."""
743 interface = cls.client.add_router_interface_with_subnet_id(
744 router_id, subnet_id)
745 return interface
746
747 @classmethod
Bence Romsics46bd3af2019-09-13 10:52:41 +0200748 def add_extra_routes_atomic(cls, *args, **kwargs):
749 return cls.client.add_extra_routes_atomic(*args, **kwargs)
750
751 @classmethod
752 def remove_extra_routes_atomic(cls, *args, **kwargs):
753 return cls.client.remove_extra_routes_atomic(*args, **kwargs)
754
755 @classmethod
Sławek Kapłońskiff294062016-12-04 15:00:54 +0000756 def get_supported_qos_rule_types(cls):
757 body = cls.client.list_qos_rule_types()
758 return [rule_type['type'] for rule_type in body['rule_types']]
759
760 @classmethod
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +0200761 def create_qos_policy(cls, name, description=None, shared=False,
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000762 project_id=None, is_default=False):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000763 """Wrapper utility that returns a test QoS policy."""
764 body = cls.admin_client.create_qos_policy(
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000765 name, description, shared, project_id, is_default)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000766 qos_policy = body['policy']
767 cls.qos_policies.append(qos_policy)
768 return qos_policy
769
770 @classmethod
elajkatdbb0b482021-05-04 17:20:07 +0200771 def create_qos_dscp_marking_rule(cls, policy_id, dscp_mark):
772 """Wrapper utility that creates and returns a QoS dscp rule."""
773 body = cls.admin_client.create_dscp_marking_rule(
774 policy_id, dscp_mark)
775 qos_rule = body['dscp_marking_rule']
776 cls.qos_rules.append(qos_rule)
777 return qos_rule
778
779 @classmethod
Jakub Libosvar83704832017-12-06 16:02:28 +0000780 def delete_router(cls, router, client=None):
781 client = client or cls.client
Aditya Vaja49819a72018-11-26 14:20:10 -0800782 if 'routes' in router:
783 client.remove_router_extra_routes(router['id'])
Jakub Libosvar83704832017-12-06 16:02:28 +0000784 body = client.list_router_interfaces(router['id'])
Chandan Kumarc125fd12017-11-15 19:41:01 +0530785 interfaces = [port for port in body['ports']
786 if port['device_owner'] in const.ROUTER_INTERFACE_OWNERS]
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000787 for i in interfaces:
788 try:
Jakub Libosvar83704832017-12-06 16:02:28 +0000789 client.remove_router_interface_with_subnet_id(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000790 router['id'], i['fixed_ips'][0]['subnet_id'])
791 except lib_exc.NotFound:
792 pass
Jakub Libosvar83704832017-12-06 16:02:28 +0000793 client.delete_router(router['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000794
795 @classmethod
796 def create_address_scope(cls, name, is_admin=False, **kwargs):
797 if is_admin:
798 body = cls.admin_client.create_address_scope(name=name, **kwargs)
799 cls.admin_address_scopes.append(body['address_scope'])
800 else:
801 body = cls.client.create_address_scope(name=name, **kwargs)
802 cls.address_scopes.append(body['address_scope'])
803 return body['address_scope']
804
805 @classmethod
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200806 def create_subnetpool(cls, name, is_admin=False, client=None, **kwargs):
807 if client is None:
808 client = cls.admin_client if is_admin else cls.client
809
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000810 if is_admin:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200811 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000812 cls.admin_subnetpools.append(body['subnetpool'])
813 else:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200814 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000815 cls.subnetpools.append(body['subnetpool'])
816 return body['subnetpool']
817
Chandan Kumarc125fd12017-11-15 19:41:01 +0530818 @classmethod
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600819 def create_address_group(cls, name, is_admin=False, **kwargs):
820 if is_admin:
821 body = cls.admin_client.create_address_group(name=name, **kwargs)
822 cls.admin_address_groups.append(body['address_group'])
823 else:
824 body = cls.client.create_address_group(name=name, **kwargs)
825 cls.address_groups.append(body['address_group'])
826 return body['address_group']
827
828 @classmethod
Chandan Kumarc125fd12017-11-15 19:41:01 +0530829 def create_project(cls, name=None, description=None):
830 test_project = name or data_utils.rand_name('test_project_')
831 test_description = description or data_utils.rand_name('desc_')
832 project = cls.identity_admin_client.create_project(
833 name=test_project,
834 description=test_description)['project']
835 cls.projects.append(project)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000836 # Create a project will create a default security group.
Dongcan Ye2de722e2018-07-04 11:01:37 +0000837 sgs_list = cls.admin_client.list_security_groups(
838 tenant_id=project['id'])['security_groups']
Federico Ressi4c590d72018-10-10 14:01:08 +0200839 for security_group in sgs_list:
840 # Make sure delete_security_group method will use
841 # the admin client for this group
842 security_group['client'] = cls.admin_client
843 cls.security_groups.append(security_group)
Chandan Kumarc125fd12017-11-15 19:41:01 +0530844 return project
845
846 @classmethod
Federico Ressi4c590d72018-10-10 14:01:08 +0200847 def create_security_group(cls, name=None, project=None, client=None,
848 **kwargs):
849 if project:
850 client = client or cls.admin_client
851 project_id = kwargs.setdefault('project_id', project['id'])
852 tenant_id = kwargs.setdefault('tenant_id', project['id'])
853 if project_id != project['id'] or tenant_id != project['id']:
854 raise ValueError('Project ID specified multiple times')
855 else:
856 client = client or cls.client
857
858 name = name or data_utils.rand_name(cls.__name__)
859 security_group = client.create_security_group(name=name, **kwargs)[
860 'security_group']
861 security_group['client'] = client
862 cls.security_groups.append(security_group)
863 return security_group
864
865 @classmethod
866 def delete_security_group(cls, security_group, client=None):
867 client = client or security_group.get('client') or cls.client
868 client.delete_security_group(security_group['id'])
869
870 @classmethod
871 def create_security_group_rule(cls, security_group=None, project=None,
872 client=None, ip_version=None, **kwargs):
873 if project:
874 client = client or cls.admin_client
875 project_id = kwargs.setdefault('project_id', project['id'])
876 tenant_id = kwargs.setdefault('tenant_id', project['id'])
877 if project_id != project['id'] or tenant_id != project['id']:
878 raise ValueError('Project ID specified multiple times')
879
880 if 'security_group_id' not in kwargs:
881 security_group = (security_group or
882 cls.get_security_group(client=client))
883
884 if security_group:
885 client = client or security_group.get('client')
886 security_group_id = kwargs.setdefault('security_group_id',
887 security_group['id'])
888 if security_group_id != security_group['id']:
889 raise ValueError('Security group ID specified multiple times.')
890
891 ip_version = ip_version or cls._ip_version
892 default_params = (
893 constants.DEFAULT_SECURITY_GROUP_RULE_PARAMS[ip_version])
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600894 if ('remote_address_group_id' in kwargs and 'remote_ip_prefix' in
895 default_params):
896 default_params.pop('remote_ip_prefix')
Federico Ressi4c590d72018-10-10 14:01:08 +0200897 for key, value in default_params.items():
898 kwargs.setdefault(key, value)
899
900 client = client or cls.client
901 return client.create_security_group_rule(**kwargs)[
902 'security_group_rule']
903
904 @classmethod
905 def get_security_group(cls, name='default', client=None):
906 client = client or cls.client
907 security_groups = client.list_security_groups()['security_groups']
908 for security_group in security_groups:
909 if security_group['name'] == name:
910 return security_group
911 raise ValueError("No such security group named {!r}".format(name))
Chandan Kumarc125fd12017-11-15 19:41:01 +0530912
Federico Ressiab286e42018-06-19 09:52:10 +0200913 @classmethod
914 def create_keypair(cls, client=None, name=None, **kwargs):
915 client = client or cls.os_primary.keypairs_client
916 name = name or data_utils.rand_name('keypair-test')
917 keypair = client.create_keypair(name=name, **kwargs)['keypair']
918
919 # save client for later cleanup
920 keypair['client'] = client
921 cls.keypairs.append(keypair)
922 return keypair
923
924 @classmethod
925 def delete_keypair(cls, keypair, client=None):
926 client = (client or keypair.get('client') or
927 cls.os_primary.keypairs_client)
928 client.delete_keypair(keypair_name=keypair['name'])
929
Federico Ressi82e83e32018-07-03 14:19:55 +0200930 @classmethod
931 def create_trunk(cls, port=None, subports=None, client=None, **kwargs):
932 """Create network trunk
933
934 :param port: dictionary containing parent port ID (port['id'])
935 :param client: client to be used for connecting to networking service
936 :param **kwargs: extra parameters to be forwarded to network service
937
938 :returns: dictionary containing created trunk details
939 """
940 client = client or cls.client
941
942 if port:
943 kwargs['port_id'] = port['id']
944
945 trunk = client.create_trunk(subports=subports, **kwargs)['trunk']
946 # Save client reference for later deletion
947 trunk['client'] = client
948 cls.trunks.append(trunk)
949 return trunk
950
951 @classmethod
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800952 def delete_trunk(cls, trunk, client=None, detach_parent_port=True):
Federico Ressi82e83e32018-07-03 14:19:55 +0200953 """Delete network trunk
954
955 :param trunk: dictionary containing trunk ID (trunk['id'])
956
957 :param client: client to be used for connecting to networking service
958 """
959 client = client or trunk.get('client') or cls.client
960 trunk.update(client.show_trunk(trunk['id'])['trunk'])
961
962 if not trunk['admin_state_up']:
963 # Cannot touch trunk before admin_state_up is True
964 client.update_trunk(trunk['id'], admin_state_up=True)
965 if trunk['sub_ports']:
966 # Removes trunk ports before deleting it
967 cls._try_delete_resource(client.remove_subports, trunk['id'],
968 trunk['sub_ports'])
969
970 # we have to detach the interface from the server before
971 # the trunk can be deleted.
972 parent_port = {'id': trunk['port_id']}
973
974 def is_parent_port_detached():
975 parent_port.update(client.show_port(parent_port['id'])['port'])
976 return not parent_port['device_id']
977
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800978 if detach_parent_port and not is_parent_port_detached():
Federico Ressi82e83e32018-07-03 14:19:55 +0200979 # this could probably happen when trunk is deleted and parent port
980 # has been assigned to a VM that is still running. Here we are
981 # assuming that device_id points to such VM.
982 cls.os_primary.compute.InterfacesClient().delete_interface(
983 parent_port['device_id'], parent_port['id'])
984 utils.wait_until_true(is_parent_port_detached)
985
986 client.delete_trunk(trunk['id'])
987
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200988 @classmethod
989 def create_conntrack_helper(cls, router_id, helper, protocol, port,
990 client=None):
991 """Create a conntrack helper
992
993 Create a conntrack helper and schedule it for later deletion. If a
994 client is passed, then it is used for deleteing the CTH too.
995
996 :param router_id: The ID of the Neutron router associated to the
997 conntrack helper.
998
999 :param helper: The conntrack helper module alias
1000
1001 :param protocol: The conntrack helper IP protocol used in the conntrack
1002 helper.
1003
1004 :param port: The conntrack helper IP protocol port number for the
1005 conntrack helper.
1006
1007 :param client: network client to be used for creating and cleaning up
1008 the conntrack helper.
1009 """
1010
1011 client = client or cls.client
1012
1013 cth = client.create_conntrack_helper(router_id, helper, protocol,
1014 port)['conntrack_helper']
1015
1016 # save ID of router associated with conntrack helper for final cleanup
1017 cth['router_id'] = router_id
1018
1019 # save client to be used later in cls.delete_conntrack_helper for final
1020 # cleanup
1021 cth['client'] = client
1022 cls.conntrack_helpers.append(cth)
1023 return cth
1024
1025 @classmethod
1026 def delete_conntrack_helper(cls, cth, client=None):
1027 """Delete conntrack helper
1028
1029 :param client: Client to be used
1030 If client is not given it will use the client used to create the
1031 conntrack helper, or cls.client if unknown.
1032 """
1033
1034 client = client or cth.get('client') or cls.client
1035 client.delete_conntrack_helper(cth['router_id'], cth['id'])
1036
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001037
1038class BaseAdminNetworkTest(BaseNetworkTest):
1039
1040 credentials = ['primary', 'admin']
1041
1042 @classmethod
1043 def setup_clients(cls):
1044 super(BaseAdminNetworkTest, cls).setup_clients()
fumihiko kakumaa216fc12017-07-14 10:43:29 +09001045 cls.admin_client = cls.os_admin.network_client
Jakub Libosvarf5758012017-08-15 13:45:30 +00001046 cls.identity_admin_client = cls.os_admin.projects_client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001047
1048 @classmethod
1049 def create_metering_label(cls, name, description):
1050 """Wrapper utility that returns a test metering label."""
1051 body = cls.admin_client.create_metering_label(
1052 description=description,
1053 name=data_utils.rand_name("metering-label"))
1054 metering_label = body['metering_label']
1055 cls.metering_labels.append(metering_label)
1056 return metering_label
1057
1058 @classmethod
1059 def create_metering_label_rule(cls, remote_ip_prefix, direction,
1060 metering_label_id):
1061 """Wrapper utility that returns a test metering label rule."""
1062 body = cls.admin_client.create_metering_label_rule(
1063 remote_ip_prefix=remote_ip_prefix, direction=direction,
1064 metering_label_id=metering_label_id)
1065 metering_label_rule = body['metering_label_rule']
1066 cls.metering_label_rules.append(metering_label_rule)
1067 return metering_label_rule
1068
1069 @classmethod
Kailun Qineaaf9782018-12-20 04:45:01 +08001070 def create_network_segment_range(cls, name, shared,
1071 project_id, network_type,
1072 physical_network, minimum,
1073 maximum):
1074 """Wrapper utility that returns a test network segment range."""
1075 network_segment_range_args = {'name': name,
1076 'shared': shared,
1077 'project_id': project_id,
1078 'network_type': network_type,
1079 'physical_network': physical_network,
1080 'minimum': minimum,
1081 'maximum': maximum}
1082 body = cls.admin_client.create_network_segment_range(
1083 **network_segment_range_args)
1084 network_segment_range = body['network_segment_range']
1085 cls.network_segment_ranges.append(network_segment_range)
1086 return network_segment_range
1087
1088 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001089 def create_flavor(cls, name, description, service_type):
1090 """Wrapper utility that returns a test flavor."""
1091 body = cls.admin_client.create_flavor(
1092 description=description, service_type=service_type,
1093 name=name)
1094 flavor = body['flavor']
1095 cls.flavors.append(flavor)
1096 return flavor
1097
1098 @classmethod
1099 def create_service_profile(cls, description, metainfo, driver):
1100 """Wrapper utility that returns a test service profile."""
1101 body = cls.admin_client.create_service_profile(
1102 driver=driver, metainfo=metainfo, description=description)
1103 service_profile = body['service_profile']
1104 cls.service_profiles.append(service_profile)
1105 return service_profile
1106
1107 @classmethod
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001108 def create_log(cls, name, description=None,
1109 resource_type='security_group', resource_id=None,
1110 target_id=None, event='ALL', enabled=True):
1111 """Wrapper utility that returns a test log object."""
1112 log_args = {'name': name,
1113 'description': description,
1114 'resource_type': resource_type,
1115 'resource_id': resource_id,
1116 'target_id': target_id,
1117 'event': event,
1118 'enabled': enabled}
1119 body = cls.admin_client.create_log(**log_args)
1120 log_object = body['log']
1121 cls.log_objects.append(log_object)
1122 return log_object
1123
1124 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001125 def get_unused_ip(cls, net_id, ip_version=None):
Gary Kotton011345f2016-06-15 08:04:31 -07001126 """Get an unused ip address in a allocation pool of net"""
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001127 body = cls.admin_client.list_ports(network_id=net_id)
1128 ports = body['ports']
1129 used_ips = []
1130 for port in ports:
1131 used_ips.extend(
1132 [fixed_ip['ip_address'] for fixed_ip in port['fixed_ips']])
1133 body = cls.admin_client.list_subnets(network_id=net_id)
1134 subnets = body['subnets']
1135
1136 for subnet in subnets:
1137 if ip_version and subnet['ip_version'] != ip_version:
1138 continue
1139 cidr = subnet['cidr']
1140 allocation_pools = subnet['allocation_pools']
1141 iterators = []
1142 if allocation_pools:
1143 for allocation_pool in allocation_pools:
1144 iterators.append(netaddr.iter_iprange(
1145 allocation_pool['start'], allocation_pool['end']))
1146 else:
1147 net = netaddr.IPNetwork(cidr)
1148
1149 def _iterip():
1150 for ip in net:
1151 if ip not in (net.network, net.broadcast):
1152 yield ip
1153 iterators.append(iter(_iterip()))
1154
1155 for iterator in iterators:
1156 for ip in iterator:
1157 if str(ip) not in used_ips:
1158 return str(ip)
1159
1160 message = (
1161 "net(%s) has no usable IP address in allocation pools" % net_id)
1162 raise exceptions.InvalidConfiguration(message)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001163
Lajos Katona2f904652018-08-23 14:04:56 +02001164 @classmethod
1165 def create_provider_network(cls, physnet_name, start_segmentation_id,
1166 max_attempts=30):
1167 segmentation_id = start_segmentation_id
Lajos Katona7eb67252019-01-14 12:55:35 +01001168 for attempts in range(max_attempts):
Lajos Katona2f904652018-08-23 14:04:56 +02001169 try:
Lajos Katona7eb67252019-01-14 12:55:35 +01001170 return cls.create_network(
Lajos Katona2f904652018-08-23 14:04:56 +02001171 name=data_utils.rand_name('test_net'),
1172 shared=True,
1173 provider_network_type='vlan',
1174 provider_physical_network=physnet_name,
1175 provider_segmentation_id=segmentation_id)
Lajos Katona2f904652018-08-23 14:04:56 +02001176 except lib_exc.Conflict:
Lajos Katona2f904652018-08-23 14:04:56 +02001177 segmentation_id += 1
1178 if segmentation_id > 4095:
1179 raise lib_exc.TempestException(
1180 "No free segmentation id was found for provider "
1181 "network creation!")
1182 time.sleep(CONF.network.build_interval)
Lajos Katona7eb67252019-01-14 12:55:35 +01001183 LOG.exception("Failed to create provider network after "
1184 "%d attempts", max_attempts)
1185 raise lib_exc.TimeoutException
Lajos Katona2f904652018-08-23 14:04:56 +02001186
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001187
Sławek Kapłońskiff294062016-12-04 15:00:54 +00001188def require_qos_rule_type(rule_type):
1189 def decorator(f):
1190 @functools.wraps(f)
1191 def wrapper(self, *func_args, **func_kwargs):
1192 if rule_type not in self.get_supported_qos_rule_types():
1193 raise self.skipException(
1194 "%s rule type is required." % rule_type)
1195 return f(self, *func_args, **func_kwargs)
1196 return wrapper
1197 return decorator
1198
1199
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001200def _require_sorting(f):
1201 @functools.wraps(f)
1202 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301203 if not tutils.is_extension_enabled("sorting", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001204 self.skipTest('Sorting feature is required')
1205 return f(self, *args, **kwargs)
1206 return inner
1207
1208
1209def _require_pagination(f):
1210 @functools.wraps(f)
1211 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301212 if not tutils.is_extension_enabled("pagination", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001213 self.skipTest('Pagination feature is required')
1214 return f(self, *args, **kwargs)
1215 return inner
1216
1217
1218class BaseSearchCriteriaTest(BaseNetworkTest):
1219
1220 # This should be defined by subclasses to reflect resource name to test
1221 resource = None
1222
Armando Migliaccio57581c62016-07-01 10:13:19 -07001223 field = 'name'
1224
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001225 # NOTE(ihrachys): some names, like those starting with an underscore (_)
1226 # are sorted differently depending on whether the plugin implements native
1227 # sorting support, or not. So we avoid any such cases here, sticking to
1228 # alphanumeric. Also test a case when there are multiple resources with the
1229 # same name
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001230 resource_names = ('test1', 'abc1', 'test10', '123test') + ('test1',)
1231
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001232 force_tenant_isolation = True
1233
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001234 list_kwargs = {}
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001235
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001236 list_as_admin = False
1237
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001238 def assertSameOrder(self, original, actual):
1239 # gracefully handle iterators passed
1240 original = list(original)
1241 actual = list(actual)
1242 self.assertEqual(len(original), len(actual))
1243 for expected, res in zip(original, actual):
Armando Migliaccio57581c62016-07-01 10:13:19 -07001244 self.assertEqual(expected[self.field], res[self.field])
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001245
1246 @utils.classproperty
1247 def plural_name(self):
1248 return '%ss' % self.resource
1249
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001250 @property
1251 def list_client(self):
1252 return self.admin_client if self.list_as_admin else self.client
1253
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001254 def list_method(self, *args, **kwargs):
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001255 method = getattr(self.list_client, 'list_%s' % self.plural_name)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001256 kwargs.update(self.list_kwargs)
1257 return method(*args, **kwargs)
1258
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001259 def get_bare_url(self, url):
1260 base_url = self.client.base_url
zheng.yong74e760a2019-05-22 14:16:14 +08001261 base_url_normalized = utils.normalize_url(base_url)
1262 url_normalized = utils.normalize_url(url)
1263 self.assertTrue(url_normalized.startswith(base_url_normalized))
1264 return url_normalized[len(base_url_normalized):]
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001265
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001266 @classmethod
1267 def _extract_resources(cls, body):
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001268 return body[cls.plural_name]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001269
1270 def _test_list_sorts(self, direction):
1271 sort_args = {
1272 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001273 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001274 }
1275 body = self.list_method(**sort_args)
1276 resources = self._extract_resources(body)
1277 self.assertNotEmpty(
1278 resources, "%s list returned is empty" % self.resource)
Armando Migliaccio57581c62016-07-01 10:13:19 -07001279 retrieved_names = [res[self.field] for res in resources]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001280 expected = sorted(retrieved_names)
1281 if direction == constants.SORT_DIRECTION_DESC:
1282 expected = list(reversed(expected))
1283 self.assertEqual(expected, retrieved_names)
1284
1285 @_require_sorting
1286 def _test_list_sorts_asc(self):
1287 self._test_list_sorts(constants.SORT_DIRECTION_ASC)
1288
1289 @_require_sorting
1290 def _test_list_sorts_desc(self):
1291 self._test_list_sorts(constants.SORT_DIRECTION_DESC)
1292
1293 @_require_pagination
1294 def _test_list_pagination(self):
1295 for limit in range(1, len(self.resource_names) + 1):
1296 pagination_args = {
1297 'limit': limit,
1298 }
1299 body = self.list_method(**pagination_args)
1300 resources = self._extract_resources(body)
1301 self.assertEqual(limit, len(resources))
1302
1303 @_require_pagination
1304 def _test_list_no_pagination_limit_0(self):
1305 pagination_args = {
1306 'limit': 0,
1307 }
1308 body = self.list_method(**pagination_args)
1309 resources = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001310 self.assertGreaterEqual(len(resources), len(self.resource_names))
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001311
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001312 def _test_list_pagination_iteratively(self, lister):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001313 # first, collect all resources for later comparison
1314 sort_args = {
1315 'sort_dir': constants.SORT_DIRECTION_ASC,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001316 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001317 }
1318 body = self.list_method(**sort_args)
1319 expected_resources = self._extract_resources(body)
1320 self.assertNotEmpty(expected_resources)
1321
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001322 resources = lister(
1323 len(expected_resources), sort_args
1324 )
1325
1326 # finally, compare that the list retrieved in one go is identical to
1327 # the one containing pagination results
1328 self.assertSameOrder(expected_resources, resources)
1329
1330 def _list_all_with_marker(self, niterations, sort_args):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001331 # paginate resources one by one, using last fetched resource as a
1332 # marker
1333 resources = []
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001334 for i in range(niterations):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001335 pagination_args = sort_args.copy()
1336 pagination_args['limit'] = 1
1337 if resources:
1338 pagination_args['marker'] = resources[-1]['id']
1339 body = self.list_method(**pagination_args)
1340 resources_ = self._extract_resources(body)
1341 self.assertEqual(1, len(resources_))
1342 resources.extend(resources_)
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001343 return resources
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001344
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001345 @_require_pagination
1346 @_require_sorting
1347 def _test_list_pagination_with_marker(self):
1348 self._test_list_pagination_iteratively(self._list_all_with_marker)
1349
1350 def _list_all_with_hrefs(self, niterations, sort_args):
1351 # paginate resources one by one, using next href links
1352 resources = []
1353 prev_links = {}
1354
1355 for i in range(niterations):
1356 if prev_links:
1357 uri = self.get_bare_url(prev_links['next'])
1358 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001359 sort_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001360 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001361 self.plural_name, limit=1, **sort_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001362 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001363 self.plural_name, uri
1364 )
1365 resources_ = self._extract_resources(body)
1366 self.assertEqual(1, len(resources_))
1367 resources.extend(resources_)
1368
1369 # The last element is empty and does not contain 'next' link
1370 uri = self.get_bare_url(prev_links['next'])
1371 prev_links, body = self.client.get_uri_with_links(
1372 self.plural_name, uri
1373 )
1374 self.assertNotIn('next', prev_links)
1375
1376 # Now walk backwards and compare results
1377 resources2 = []
1378 for i in range(niterations):
1379 uri = self.get_bare_url(prev_links['previous'])
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001380 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001381 self.plural_name, uri
1382 )
1383 resources_ = self._extract_resources(body)
1384 self.assertEqual(1, len(resources_))
1385 resources2.extend(resources_)
1386
1387 self.assertSameOrder(resources, reversed(resources2))
1388
1389 return resources
1390
1391 @_require_pagination
1392 @_require_sorting
1393 def _test_list_pagination_with_href_links(self):
1394 self._test_list_pagination_iteratively(self._list_all_with_hrefs)
1395
1396 @_require_pagination
1397 @_require_sorting
1398 def _test_list_pagination_page_reverse_with_href_links(
1399 self, direction=constants.SORT_DIRECTION_ASC):
1400 pagination_args = {
1401 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001402 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001403 }
1404 body = self.list_method(**pagination_args)
1405 expected_resources = self._extract_resources(body)
1406
1407 page_size = 2
1408 pagination_args['limit'] = page_size
1409
1410 prev_links = {}
1411 resources = []
1412 num_resources = len(expected_resources)
1413 niterations = int(math.ceil(float(num_resources) / page_size))
1414 for i in range(niterations):
1415 if prev_links:
1416 uri = self.get_bare_url(prev_links['previous'])
1417 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001418 pagination_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001419 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001420 self.plural_name, page_reverse=True, **pagination_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001421 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001422 self.plural_name, uri
1423 )
1424 resources_ = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001425 self.assertGreaterEqual(page_size, len(resources_))
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001426 resources.extend(reversed(resources_))
1427
1428 self.assertSameOrder(expected_resources, reversed(resources))
1429
1430 @_require_pagination
1431 @_require_sorting
1432 def _test_list_pagination_page_reverse_asc(self):
1433 self._test_list_pagination_page_reverse(
1434 direction=constants.SORT_DIRECTION_ASC)
1435
1436 @_require_pagination
1437 @_require_sorting
1438 def _test_list_pagination_page_reverse_desc(self):
1439 self._test_list_pagination_page_reverse(
1440 direction=constants.SORT_DIRECTION_DESC)
1441
1442 def _test_list_pagination_page_reverse(self, direction):
1443 pagination_args = {
1444 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001445 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001446 'limit': 3,
1447 }
1448 body = self.list_method(**pagination_args)
1449 expected_resources = self._extract_resources(body)
1450
1451 pagination_args['limit'] -= 1
1452 pagination_args['marker'] = expected_resources[-1]['id']
1453 pagination_args['page_reverse'] = True
1454 body = self.list_method(**pagination_args)
1455
1456 self.assertSameOrder(
1457 # the last entry is not included in 2nd result when used as a
1458 # marker
1459 expected_resources[:-1],
1460 self._extract_resources(body))
Victor Morales1be97b42016-09-05 08:50:06 -05001461
Hongbin Lu54f55922018-07-12 19:05:39 +00001462 @tutils.requires_ext(extension="filter-validation", service="network")
1463 def _test_list_validation_filters(
1464 self, validation_args, filter_is_valid=True):
1465 if not filter_is_valid:
1466 self.assertRaises(lib_exc.BadRequest, self.list_method,
1467 **validation_args)
1468 else:
1469 body = self.list_method(**validation_args)
1470 resources = self._extract_resources(body)
1471 for resource in resources:
1472 self.assertIn(resource['name'], self.resource_names)