blob: 3c7441ac8549b80faa9abfcdd30734b87b0f6952 [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)
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600128 cls.address_groups = []
129 cls.admin_address_groups = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000130 cls.address_scopes = []
131 cls.admin_address_scopes = []
132 cls.subnetpools = []
133 cls.admin_subnetpools = []
Itzik Brownbac51dc2016-10-31 12:25:04 +0000134 cls.security_groups = []
Dongcan Ye2de722e2018-07-04 11:01:37 +0000135 cls.admin_security_groups = []
Chandan Kumarc125fd12017-11-15 19:41:01 +0530136 cls.projects = []
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700137 cls.log_objects = []
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200138 cls.reserved_subnet_cidrs = set()
Federico Ressiab286e42018-06-19 09:52:10 +0200139 cls.keypairs = []
Federico Ressi82e83e32018-07-03 14:19:55 +0200140 cls.trunks = []
Kailun Qineaaf9782018-12-20 04:45:01 +0800141 cls.network_segment_ranges = []
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200142 cls.conntrack_helpers = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000143
144 @classmethod
yangjianfeng23e40c22020-11-22 08:42:18 +0000145 def reserve_external_subnet_cidrs(cls):
146 client = cls.os_admin.network_client
147 ext_nets = client.list_networks(
148 **{"router:external": True})['networks']
149 for ext_net in ext_nets:
150 ext_subnets = client.list_subnets(
151 network_id=ext_net['id'])['subnets']
152 for ext_subnet in ext_subnets:
153 cls.reserve_subnet_cidr(ext_subnet['cidr'])
154
155 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000156 def resource_cleanup(cls):
157 if CONF.service_available.neutron:
Federico Ressi82e83e32018-07-03 14:19:55 +0200158 # Clean up trunks
159 for trunk in cls.trunks:
160 cls._try_delete_resource(cls.delete_trunk, trunk)
161
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200162 # Clean up port forwardings
163 for pf in cls.port_forwardings:
164 cls._try_delete_resource(cls.delete_port_forwarding, pf)
165
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000166 # Clean up floating IPs
167 for floating_ip in cls.floating_ips:
Federico Ressia69dcd52018-07-06 09:45:34 +0200168 cls._try_delete_resource(cls.delete_floatingip, floating_ip)
169
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200170 # Clean up conntrack helpers
171 for cth in cls.conntrack_helpers:
172 cls._try_delete_resource(cls.delete_conntrack_helper, cth)
173
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000174 # Clean up routers
175 for router in cls.routers:
176 cls._try_delete_resource(cls.delete_router,
177 router)
178 # Clean up metering label rules
179 for metering_label_rule in cls.metering_label_rules:
180 cls._try_delete_resource(
181 cls.admin_client.delete_metering_label_rule,
182 metering_label_rule['id'])
183 # Clean up metering labels
184 for metering_label in cls.metering_labels:
185 cls._try_delete_resource(
186 cls.admin_client.delete_metering_label,
187 metering_label['id'])
188 # Clean up flavors
189 for flavor in cls.flavors:
190 cls._try_delete_resource(
191 cls.admin_client.delete_flavor,
192 flavor['id'])
193 # Clean up service profiles
194 for service_profile in cls.service_profiles:
195 cls._try_delete_resource(
196 cls.admin_client.delete_service_profile,
197 service_profile['id'])
198 # Clean up ports
199 for port in cls.ports:
200 cls._try_delete_resource(cls.client.delete_port,
201 port['id'])
202 # Clean up subnets
203 for subnet in cls.subnets:
204 cls._try_delete_resource(cls.client.delete_subnet,
205 subnet['id'])
Kevin Bentonba3651c2017-09-01 17:13:01 -0700206 # Clean up admin subnets
207 for subnet in cls.admin_subnets:
208 cls._try_delete_resource(cls.admin_client.delete_subnet,
209 subnet['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000210 # Clean up networks
211 for network in cls.networks:
Federico Ressi61b564e2018-07-06 08:10:31 +0200212 cls._try_delete_resource(cls.delete_network, network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000213
Miguel Lavalle124378b2016-09-21 16:41:47 -0500214 # Clean up admin networks
215 for network in cls.admin_networks:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000216 cls._try_delete_resource(cls.admin_client.delete_network,
217 network['id'])
218
Itzik Brownbac51dc2016-10-31 12:25:04 +0000219 # Clean up security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200220 for security_group in cls.security_groups:
221 cls._try_delete_resource(cls.delete_security_group,
222 security_group)
Itzik Brownbac51dc2016-10-31 12:25:04 +0000223
Dongcan Ye2de722e2018-07-04 11:01:37 +0000224 # Clean up admin security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200225 for security_group in cls.admin_security_groups:
226 cls._try_delete_resource(cls.delete_security_group,
227 security_group,
228 client=cls.admin_client)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000229
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000230 for subnetpool in cls.subnetpools:
231 cls._try_delete_resource(cls.client.delete_subnetpool,
232 subnetpool['id'])
233
234 for subnetpool in cls.admin_subnetpools:
235 cls._try_delete_resource(cls.admin_client.delete_subnetpool,
236 subnetpool['id'])
237
238 for address_scope in cls.address_scopes:
239 cls._try_delete_resource(cls.client.delete_address_scope,
240 address_scope['id'])
241
242 for address_scope in cls.admin_address_scopes:
243 cls._try_delete_resource(
244 cls.admin_client.delete_address_scope,
245 address_scope['id'])
246
Chandan Kumarc125fd12017-11-15 19:41:01 +0530247 for project in cls.projects:
248 cls._try_delete_resource(
249 cls.identity_admin_client.delete_project,
250 project['id'])
251
Sławek Kapłońskie100c4d2017-08-23 21:18:34 +0000252 # Clean up QoS rules
253 for qos_rule in cls.qos_rules:
254 cls._try_delete_resource(cls.admin_client.delete_qos_rule,
255 qos_rule['id'])
256 # Clean up QoS policies
257 # as all networks and ports are already removed, QoS policies
258 # shouldn't be "in use"
259 for qos_policy in cls.qos_policies:
260 cls._try_delete_resource(cls.admin_client.delete_qos_policy,
261 qos_policy['id'])
262
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700263 # Clean up log_objects
264 for log_object in cls.log_objects:
265 cls._try_delete_resource(cls.admin_client.delete_log,
266 log_object['id'])
267
Federico Ressiab286e42018-06-19 09:52:10 +0200268 for keypair in cls.keypairs:
269 cls._try_delete_resource(cls.delete_keypair, keypair)
270
Kailun Qineaaf9782018-12-20 04:45:01 +0800271 # Clean up network_segment_ranges
272 for network_segment_range in cls.network_segment_ranges:
273 cls._try_delete_resource(
274 cls.admin_client.delete_network_segment_range,
275 network_segment_range['id'])
276
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000277 super(BaseNetworkTest, cls).resource_cleanup()
278
279 @classmethod
280 def _try_delete_resource(cls, delete_callable, *args, **kwargs):
281 """Cleanup resources in case of test-failure
282
283 Some resources are explicitly deleted by the test.
284 If the test failed to delete a resource, this method will execute
285 the appropriate delete methods. Otherwise, the method ignores NotFound
286 exceptions thrown for resources that were correctly deleted by the
287 test.
288
289 :param delete_callable: delete method
290 :param args: arguments for delete method
291 :param kwargs: keyword arguments for delete method
292 """
293 try:
294 delete_callable(*args, **kwargs)
295 # if resource is not found, this means it was deleted in the test
296 except lib_exc.NotFound:
297 pass
298
299 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200300 def create_network(cls, network_name=None, client=None, external=None,
301 shared=None, provider_network_type=None,
302 provider_physical_network=None,
303 provider_segmentation_id=None, **kwargs):
304 """Create a network.
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000305
Federico Ressi61b564e2018-07-06 08:10:31 +0200306 When client is not provider and admin_client is attribute is not None
307 (for example when using BaseAdminNetworkTest base class) and using any
308 of the convenience parameters (external, shared, provider_network_type,
309 provider_physical_network and provider_segmentation_id) it silently
310 uses admin_client. If the network is not shared then it uses the same
311 project_id as regular client.
312
313 :param network_name: Human-readable name of the network
314
315 :param client: client to be used for connecting to network service
316
317 :param external: indicates whether the network has an external routing
318 facility that's not managed by the networking service.
319
320 :param shared: indicates whether this resource is shared across all
321 projects. By default, only administrative users can change this value.
322 If True and admin_client attribute is not None, then the network is
323 created under administrative project.
324
325 :param provider_network_type: the type of physical network that this
326 network should be mapped to. For example, 'flat', 'vlan', 'vxlan', or
327 'gre'. Valid values depend on a networking back-end.
328
329 :param provider_physical_network: the physical network where this
330 network should be implemented. The Networking API v2.0 does not provide
331 a way to list available physical networks. For example, the Open
332 vSwitch plug-in configuration file defines a symbolic name that maps to
333 specific bridges on each compute host.
334
335 :param provider_segmentation_id: The ID of the isolated segment on the
336 physical network. The network_type attribute defines the segmentation
337 model. For example, if the network_type value is 'vlan', this ID is a
338 vlan identifier. If the network_type value is 'gre', this ID is a gre
339 key.
340
341 :param **kwargs: extra parameters to be forwarded to network service
342 """
343
344 name = (network_name or kwargs.pop('name', None) or
345 data_utils.rand_name('test-network-'))
346
347 # translate convenience parameters
348 admin_client_required = False
349 if provider_network_type:
350 admin_client_required = True
351 kwargs['provider:network_type'] = provider_network_type
352 if provider_physical_network:
353 admin_client_required = True
354 kwargs['provider:physical_network'] = provider_physical_network
355 if provider_segmentation_id:
356 admin_client_required = True
357 kwargs['provider:segmentation_id'] = provider_segmentation_id
358 if external is not None:
359 admin_client_required = True
360 kwargs['router:external'] = bool(external)
361 if shared is not None:
362 admin_client_required = True
363 kwargs['shared'] = bool(shared)
364
365 if not client:
366 if admin_client_required and cls.admin_client:
367 # For convenience silently switch to admin client
368 client = cls.admin_client
369 if not shared:
370 # Keep this network visible from current project
371 project_id = (kwargs.get('project_id') or
372 kwargs.get('tenant_id') or
373 cls.client.tenant_id)
374 kwargs.update(project_id=project_id, tenant_id=project_id)
375 else:
376 # Use default client
377 client = cls.client
378
379 network = client.create_network(name=name, **kwargs)['network']
380 network['client'] = client
381 cls.networks.append(network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000382 return network
383
384 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200385 def delete_network(cls, network, client=None):
386 client = client or network.get('client') or cls.client
387 client.delete_network(network['id'])
388
389 @classmethod
390 def create_shared_network(cls, network_name=None, **kwargs):
391 return cls.create_network(name=network_name, shared=True, **kwargs)
Miguel Lavalle124378b2016-09-21 16:41:47 -0500392
393 @classmethod
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200394 def create_subnet(cls, network, gateway='', cidr=None, mask_bits=None,
Federico Ressi98f20ec2018-05-11 06:09:49 +0200395 ip_version=None, client=None, reserve_cidr=True,
396 **kwargs):
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200397 """Wrapper utility that returns a test subnet.
398
399 Convenient wrapper for client.create_subnet method. It reserves and
400 allocates CIDRs to avoid creating overlapping subnets.
401
402 :param network: network where to create the subnet
403 network['id'] must contain the ID of the network
404
405 :param gateway: gateway IP address
406 It can be a str or a netaddr.IPAddress
407 If gateway is not given, then it will use default address for
408 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 +0200409 if gateway is given as None then no gateway will be assigned
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200410
411 :param cidr: CIDR of the subnet to create
412 It can be either None, a str or a netaddr.IPNetwork instance
413
414 :param mask_bits: CIDR prefix length
415 It can be either None or a numeric value.
416 If cidr parameter is given then mask_bits is used to determinate a
417 sequence of valid CIDR to use as generated.
418 Please see netaddr.IPNetwork.subnet method documentation[1]
419
420 :param ip_version: ip version of generated subnet CIDRs
421 It can be None, IP_VERSION_4 or IP_VERSION_6
422 It has to match given either given CIDR and gateway
423
424 :param ip_version: numeric value (either IP_VERSION_4 or IP_VERSION_6)
425 this value must match CIDR and gateway IP versions if any of them is
426 given
427
428 :param client: client to be used to connect to network service
429
Federico Ressi98f20ec2018-05-11 06:09:49 +0200430 :param reserve_cidr: if True then it reserves assigned CIDR to avoid
431 using the same CIDR for further subnets in the scope of the same
432 test case class
433
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200434 :param **kwargs: optional parameters to be forwarded to wrapped method
435
436 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
437 """
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000438
439 # allow tests to use admin client
440 if not client:
441 client = cls.client
442
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200443 if gateway:
444 gateway_ip = netaddr.IPAddress(gateway)
445 if ip_version:
446 if ip_version != gateway_ip.version:
447 raise ValueError(
448 "Gateway IP version doesn't match IP version")
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000449 else:
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200450 ip_version = gateway_ip.version
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200451 else:
452 ip_version = ip_version or cls._ip_version
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200453
454 for subnet_cidr in cls.get_subnet_cidrs(
455 ip_version=ip_version, cidr=cidr, mask_bits=mask_bits):
Federico Ressi98f20ec2018-05-11 06:09:49 +0200456 if gateway is not None:
457 kwargs['gateway_ip'] = str(gateway or (subnet_cidr.ip + 1))
Slawek Kaplonski21f53422018-11-02 16:02:09 +0100458 else:
459 kwargs['gateway_ip'] = None
Federico Ressi98f20ec2018-05-11 06:09:49 +0200460 try:
461 body = client.create_subnet(
462 network_id=network['id'],
463 cidr=str(subnet_cidr),
464 ip_version=subnet_cidr.version,
465 **kwargs)
466 break
467 except lib_exc.BadRequest as e:
468 if 'overlaps with another subnet' not in str(e):
469 raise
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000470 else:
471 message = 'Available CIDR for subnet creation could not be found'
472 raise ValueError(message)
473 subnet = body['subnet']
Kevin Bentonba3651c2017-09-01 17:13:01 -0700474 if client is cls.client:
475 cls.subnets.append(subnet)
476 else:
477 cls.admin_subnets.append(subnet)
Federico Ressi98f20ec2018-05-11 06:09:49 +0200478 if reserve_cidr:
479 cls.reserve_subnet_cidr(subnet_cidr)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000480 return subnet
481
482 @classmethod
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200483 def reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
484 """Reserve given subnet CIDR making sure it is not used by create_subnet
485
486 :param addr: the CIDR address to be reserved
487 It can be a str or netaddr.IPNetwork instance
488
489 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
490 parameters
491 """
492
493 if not cls.try_reserve_subnet_cidr(addr, **ipnetwork_kwargs):
Bernard Cafarellic3bec862020-09-10 13:59:49 +0200494 raise ValueError('Subnet CIDR already reserved: {0!r}'.format(
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200495 addr))
496
497 @classmethod
498 def try_reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
499 """Reserve given subnet CIDR if it hasn't been reserved before
500
501 :param addr: the CIDR address to be reserved
502 It can be a str or netaddr.IPNetwork instance
503
504 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
505 parameters
506
507 :return: True if it wasn't reserved before, False elsewhere.
508 """
509
510 subnet_cidr = netaddr.IPNetwork(addr, **ipnetwork_kwargs)
511 if subnet_cidr in cls.reserved_subnet_cidrs:
512 return False
513 else:
514 cls.reserved_subnet_cidrs.add(subnet_cidr)
515 return True
516
517 @classmethod
518 def get_subnet_cidrs(
519 cls, cidr=None, mask_bits=None, ip_version=None):
520 """Iterate over a sequence of unused subnet CIDR for IP version
521
522 :param cidr: CIDR of the subnet to create
523 It can be either None, a str or a netaddr.IPNetwork instance
524
525 :param mask_bits: CIDR prefix length
526 It can be either None or a numeric value.
527 If cidr parameter is given then mask_bits is used to determinate a
528 sequence of valid CIDR to use as generated.
529 Please see netaddr.IPNetwork.subnet method documentation[1]
530
531 :param ip_version: ip version of generated subnet CIDRs
532 It can be None, IP_VERSION_4 or IP_VERSION_6
533 It has to match given CIDR if given
534
535 :return: iterator over reserved CIDRs of type netaddr.IPNetwork
536
537 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
538 """
539
540 if cidr:
541 # Generate subnet CIDRs starting from given CIDR
542 # checking it is of requested IP version
543 cidr = netaddr.IPNetwork(cidr, version=ip_version)
544 else:
545 # Generate subnet CIDRs starting from configured values
546 ip_version = ip_version or cls._ip_version
547 if ip_version == const.IP_VERSION_4:
548 mask_bits = mask_bits or config.safe_get_config_value(
549 'network', 'project_network_mask_bits')
550 cidr = netaddr.IPNetwork(config.safe_get_config_value(
551 'network', 'project_network_cidr'))
552 elif ip_version == const.IP_VERSION_6:
553 mask_bits = config.safe_get_config_value(
554 'network', 'project_network_v6_mask_bits')
555 cidr = netaddr.IPNetwork(config.safe_get_config_value(
556 'network', 'project_network_v6_cidr'))
557 else:
558 raise ValueError('Invalid IP version: {!r}'.format(ip_version))
559
560 if mask_bits:
561 subnet_cidrs = cidr.subnet(mask_bits)
562 else:
563 subnet_cidrs = iter([cidr])
564
565 for subnet_cidr in subnet_cidrs:
566 if subnet_cidr not in cls.reserved_subnet_cidrs:
567 yield subnet_cidr
568
569 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000570 def create_port(cls, network, **kwargs):
571 """Wrapper utility that returns a test port."""
Edan Davidd75e48e2018-01-03 02:49:52 -0500572 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
573 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
Glenn Van de Water5d9b1402020-09-16 15:14:14 +0200574 if CONF.network.port_profile and 'binding:profile' not in kwargs:
575 kwargs['binding:profile'] = CONF.network.port_profile
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000576 body = cls.client.create_port(network_id=network['id'],
577 **kwargs)
578 port = body['port']
579 cls.ports.append(port)
580 return port
581
582 @classmethod
583 def update_port(cls, port, **kwargs):
584 """Wrapper utility that updates a test port."""
585 body = cls.client.update_port(port['id'],
586 **kwargs)
587 return body['port']
588
589 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300590 def _create_router_with_client(
591 cls, client, router_name=None, admin_state_up=False,
592 external_network_id=None, enable_snat=None, **kwargs
593 ):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000594 ext_gw_info = {}
595 if external_network_id:
596 ext_gw_info['network_id'] = external_network_id
YAMAMOTO Takashi9bd4f972017-06-20 12:49:30 +0900597 if enable_snat is not None:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000598 ext_gw_info['enable_snat'] = enable_snat
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300599 body = client.create_router(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000600 router_name, external_gateway_info=ext_gw_info,
601 admin_state_up=admin_state_up, **kwargs)
602 router = body['router']
603 cls.routers.append(router)
604 return router
605
606 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300607 def create_router(cls, *args, **kwargs):
608 return cls._create_router_with_client(cls.client, *args, **kwargs)
609
610 @classmethod
611 def create_admin_router(cls, *args, **kwargs):
rajat294495c042017-06-28 15:37:16 +0530612 return cls._create_router_with_client(cls.os_admin.network_client,
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300613 *args, **kwargs)
614
615 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200616 def create_floatingip(cls, external_network_id=None, port=None,
617 client=None, **kwargs):
618 """Creates a floating IP.
619
620 Create a floating IP and schedule it for later deletion.
621 If a client is passed, then it is used for deleting the IP too.
622
623 :param external_network_id: network ID where to create
624 By default this is 'CONF.network.public_network_id'.
625
626 :param port: port to bind floating IP to
627 This is translated to 'port_id=port['id']'
628 By default it is None.
629
630 :param client: network client to be used for creating and cleaning up
631 the floating IP.
632
633 :param **kwargs: additional creation parameters to be forwarded to
634 networking server.
635 """
636
637 client = client or cls.client
638 external_network_id = (external_network_id or
639 cls.external_network_id)
640
641 if port:
Federico Ressi47f6ae42018-09-24 16:19:14 +0200642 port_id = kwargs.setdefault('port_id', port['id'])
643 if port_id != port['id']:
644 message = "Port ID specified twice: {!s} != {!s}".format(
645 port_id, port['id'])
646 raise ValueError(message)
Federico Ressia69dcd52018-07-06 09:45:34 +0200647
648 fip = client.create_floatingip(external_network_id,
649 **kwargs)['floatingip']
650
651 # save client to be used later in cls.delete_floatingip
652 # for final cleanup
653 fip['client'] = client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000654 cls.floating_ips.append(fip)
655 return fip
656
657 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200658 def delete_floatingip(cls, floating_ip, client=None):
659 """Delete floating IP
660
661 :param client: Client to be used
662 If client is not given it will use the client used to create
663 the floating IP, or cls.client if unknown.
664 """
665
666 client = client or floating_ip.get('client') or cls.client
667 client.delete_floatingip(floating_ip['id'])
668
669 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200670 def create_port_forwarding(cls, fip_id, internal_port_id,
671 internal_port, external_port,
672 internal_ip_address=None, protocol="tcp",
673 client=None):
674 """Creates a port forwarding.
675
676 Create a port forwarding and schedule it for later deletion.
677 If a client is passed, then it is used for deleting the PF too.
678
679 :param fip_id: The ID of the floating IP address.
680
681 :param internal_port_id: The ID of the Neutron port associated to
682 the floating IP port forwarding.
683
684 :param internal_port: The TCP/UDP/other protocol port number of the
685 Neutron port fixed IP address associated to the floating ip
686 port forwarding.
687
688 :param external_port: The TCP/UDP/other protocol port number of
689 the port forwarding floating IP address.
690
691 :param internal_ip_address: The fixed IPv4 address of the Neutron
692 port associated to the floating IP port forwarding.
693
694 :param protocol: The IP protocol used in the floating IP port
695 forwarding.
696
697 :param client: network client to be used for creating and cleaning up
698 the floating IP port forwarding.
699 """
700
701 client = client or cls.client
702
703 pf = client.create_port_forwarding(
704 fip_id, internal_port_id, internal_port, external_port,
705 internal_ip_address, protocol)['port_forwarding']
706
707 # save ID of floating IP associated with port forwarding for final
708 # cleanup
709 pf['floatingip_id'] = fip_id
710
711 # save client to be used later in cls.delete_port_forwarding
712 # for final cleanup
713 pf['client'] = client
714 cls.port_forwardings.append(pf)
715 return pf
716
717 @classmethod
Flavio Fernandesa1952c62020-10-02 06:39:08 -0400718 def update_port_forwarding(cls, fip_id, pf_id, client=None, **kwargs):
719 """Wrapper utility for update_port_forwarding."""
720 client = client or cls.client
721 return client.update_port_forwarding(fip_id, pf_id, **kwargs)
722
723 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200724 def delete_port_forwarding(cls, pf, client=None):
725 """Delete port forwarding
726
727 :param client: Client to be used
728 If client is not given it will use the client used to create
729 the port forwarding, or cls.client if unknown.
730 """
731
732 client = client or pf.get('client') or cls.client
733 client.delete_port_forwarding(pf['floatingip_id'], pf['id'])
734
735 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000736 def create_router_interface(cls, router_id, subnet_id):
737 """Wrapper utility that returns a router interface."""
738 interface = cls.client.add_router_interface_with_subnet_id(
739 router_id, subnet_id)
740 return interface
741
742 @classmethod
Bence Romsics46bd3af2019-09-13 10:52:41 +0200743 def add_extra_routes_atomic(cls, *args, **kwargs):
744 return cls.client.add_extra_routes_atomic(*args, **kwargs)
745
746 @classmethod
747 def remove_extra_routes_atomic(cls, *args, **kwargs):
748 return cls.client.remove_extra_routes_atomic(*args, **kwargs)
749
750 @classmethod
Sławek Kapłońskiff294062016-12-04 15:00:54 +0000751 def get_supported_qos_rule_types(cls):
752 body = cls.client.list_qos_rule_types()
753 return [rule_type['type'] for rule_type in body['rule_types']]
754
755 @classmethod
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +0200756 def create_qos_policy(cls, name, description=None, shared=False,
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000757 project_id=None, is_default=False):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000758 """Wrapper utility that returns a test QoS policy."""
759 body = cls.admin_client.create_qos_policy(
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000760 name, description, shared, project_id, is_default)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000761 qos_policy = body['policy']
762 cls.qos_policies.append(qos_policy)
763 return qos_policy
764
765 @classmethod
elajkatdbb0b482021-05-04 17:20:07 +0200766 def create_qos_dscp_marking_rule(cls, policy_id, dscp_mark):
767 """Wrapper utility that creates and returns a QoS dscp rule."""
768 body = cls.admin_client.create_dscp_marking_rule(
769 policy_id, dscp_mark)
770 qos_rule = body['dscp_marking_rule']
771 cls.qos_rules.append(qos_rule)
772 return qos_rule
773
774 @classmethod
Jakub Libosvar83704832017-12-06 16:02:28 +0000775 def delete_router(cls, router, client=None):
776 client = client or cls.client
Aditya Vaja49819a72018-11-26 14:20:10 -0800777 if 'routes' in router:
778 client.remove_router_extra_routes(router['id'])
Jakub Libosvar83704832017-12-06 16:02:28 +0000779 body = client.list_router_interfaces(router['id'])
Chandan Kumarc125fd12017-11-15 19:41:01 +0530780 interfaces = [port for port in body['ports']
781 if port['device_owner'] in const.ROUTER_INTERFACE_OWNERS]
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000782 for i in interfaces:
783 try:
Jakub Libosvar83704832017-12-06 16:02:28 +0000784 client.remove_router_interface_with_subnet_id(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000785 router['id'], i['fixed_ips'][0]['subnet_id'])
786 except lib_exc.NotFound:
787 pass
Jakub Libosvar83704832017-12-06 16:02:28 +0000788 client.delete_router(router['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000789
790 @classmethod
791 def create_address_scope(cls, name, is_admin=False, **kwargs):
792 if is_admin:
793 body = cls.admin_client.create_address_scope(name=name, **kwargs)
794 cls.admin_address_scopes.append(body['address_scope'])
795 else:
796 body = cls.client.create_address_scope(name=name, **kwargs)
797 cls.address_scopes.append(body['address_scope'])
798 return body['address_scope']
799
800 @classmethod
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200801 def create_subnetpool(cls, name, is_admin=False, client=None, **kwargs):
802 if client is None:
803 client = cls.admin_client if is_admin else cls.client
804
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000805 if is_admin:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200806 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000807 cls.admin_subnetpools.append(body['subnetpool'])
808 else:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200809 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000810 cls.subnetpools.append(body['subnetpool'])
811 return body['subnetpool']
812
Chandan Kumarc125fd12017-11-15 19:41:01 +0530813 @classmethod
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600814 def create_address_group(cls, name, is_admin=False, **kwargs):
815 if is_admin:
816 body = cls.admin_client.create_address_group(name=name, **kwargs)
817 cls.admin_address_groups.append(body['address_group'])
818 else:
819 body = cls.client.create_address_group(name=name, **kwargs)
820 cls.address_groups.append(body['address_group'])
821 return body['address_group']
822
823 @classmethod
Chandan Kumarc125fd12017-11-15 19:41:01 +0530824 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])
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600889 if ('remote_address_group_id' in kwargs and 'remote_ip_prefix' in
890 default_params):
891 default_params.pop('remote_ip_prefix')
Federico Ressi4c590d72018-10-10 14:01:08 +0200892 for key, value in default_params.items():
893 kwargs.setdefault(key, value)
894
895 client = client or cls.client
896 return client.create_security_group_rule(**kwargs)[
897 'security_group_rule']
898
899 @classmethod
900 def get_security_group(cls, name='default', client=None):
901 client = client or cls.client
902 security_groups = client.list_security_groups()['security_groups']
903 for security_group in security_groups:
904 if security_group['name'] == name:
905 return security_group
906 raise ValueError("No such security group named {!r}".format(name))
Chandan Kumarc125fd12017-11-15 19:41:01 +0530907
Federico Ressiab286e42018-06-19 09:52:10 +0200908 @classmethod
909 def create_keypair(cls, client=None, name=None, **kwargs):
910 client = client or cls.os_primary.keypairs_client
911 name = name or data_utils.rand_name('keypair-test')
912 keypair = client.create_keypair(name=name, **kwargs)['keypair']
913
914 # save client for later cleanup
915 keypair['client'] = client
916 cls.keypairs.append(keypair)
917 return keypair
918
919 @classmethod
920 def delete_keypair(cls, keypair, client=None):
921 client = (client or keypair.get('client') or
922 cls.os_primary.keypairs_client)
923 client.delete_keypair(keypair_name=keypair['name'])
924
Federico Ressi82e83e32018-07-03 14:19:55 +0200925 @classmethod
926 def create_trunk(cls, port=None, subports=None, client=None, **kwargs):
927 """Create network trunk
928
929 :param port: dictionary containing parent port ID (port['id'])
930 :param client: client to be used for connecting to networking service
931 :param **kwargs: extra parameters to be forwarded to network service
932
933 :returns: dictionary containing created trunk details
934 """
935 client = client or cls.client
936
937 if port:
938 kwargs['port_id'] = port['id']
939
940 trunk = client.create_trunk(subports=subports, **kwargs)['trunk']
941 # Save client reference for later deletion
942 trunk['client'] = client
943 cls.trunks.append(trunk)
944 return trunk
945
946 @classmethod
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800947 def delete_trunk(cls, trunk, client=None, detach_parent_port=True):
Federico Ressi82e83e32018-07-03 14:19:55 +0200948 """Delete network trunk
949
950 :param trunk: dictionary containing trunk ID (trunk['id'])
951
952 :param client: client to be used for connecting to networking service
953 """
954 client = client or trunk.get('client') or cls.client
955 trunk.update(client.show_trunk(trunk['id'])['trunk'])
956
957 if not trunk['admin_state_up']:
958 # Cannot touch trunk before admin_state_up is True
959 client.update_trunk(trunk['id'], admin_state_up=True)
960 if trunk['sub_ports']:
961 # Removes trunk ports before deleting it
962 cls._try_delete_resource(client.remove_subports, trunk['id'],
963 trunk['sub_ports'])
964
965 # we have to detach the interface from the server before
966 # the trunk can be deleted.
967 parent_port = {'id': trunk['port_id']}
968
969 def is_parent_port_detached():
970 parent_port.update(client.show_port(parent_port['id'])['port'])
971 return not parent_port['device_id']
972
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800973 if detach_parent_port and not is_parent_port_detached():
Federico Ressi82e83e32018-07-03 14:19:55 +0200974 # this could probably happen when trunk is deleted and parent port
975 # has been assigned to a VM that is still running. Here we are
976 # assuming that device_id points to such VM.
977 cls.os_primary.compute.InterfacesClient().delete_interface(
978 parent_port['device_id'], parent_port['id'])
979 utils.wait_until_true(is_parent_port_detached)
980
981 client.delete_trunk(trunk['id'])
982
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200983 @classmethod
984 def create_conntrack_helper(cls, router_id, helper, protocol, port,
985 client=None):
986 """Create a conntrack helper
987
988 Create a conntrack helper and schedule it for later deletion. If a
989 client is passed, then it is used for deleteing the CTH too.
990
991 :param router_id: The ID of the Neutron router associated to the
992 conntrack helper.
993
994 :param helper: The conntrack helper module alias
995
996 :param protocol: The conntrack helper IP protocol used in the conntrack
997 helper.
998
999 :param port: The conntrack helper IP protocol port number for the
1000 conntrack helper.
1001
1002 :param client: network client to be used for creating and cleaning up
1003 the conntrack helper.
1004 """
1005
1006 client = client or cls.client
1007
1008 cth = client.create_conntrack_helper(router_id, helper, protocol,
1009 port)['conntrack_helper']
1010
1011 # save ID of router associated with conntrack helper for final cleanup
1012 cth['router_id'] = router_id
1013
1014 # save client to be used later in cls.delete_conntrack_helper for final
1015 # cleanup
1016 cth['client'] = client
1017 cls.conntrack_helpers.append(cth)
1018 return cth
1019
1020 @classmethod
1021 def delete_conntrack_helper(cls, cth, client=None):
1022 """Delete conntrack helper
1023
1024 :param client: Client to be used
1025 If client is not given it will use the client used to create the
1026 conntrack helper, or cls.client if unknown.
1027 """
1028
1029 client = client or cth.get('client') or cls.client
1030 client.delete_conntrack_helper(cth['router_id'], cth['id'])
1031
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001032
1033class BaseAdminNetworkTest(BaseNetworkTest):
1034
1035 credentials = ['primary', 'admin']
1036
1037 @classmethod
1038 def setup_clients(cls):
1039 super(BaseAdminNetworkTest, cls).setup_clients()
fumihiko kakumaa216fc12017-07-14 10:43:29 +09001040 cls.admin_client = cls.os_admin.network_client
Jakub Libosvarf5758012017-08-15 13:45:30 +00001041 cls.identity_admin_client = cls.os_admin.projects_client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001042
1043 @classmethod
1044 def create_metering_label(cls, name, description):
1045 """Wrapper utility that returns a test metering label."""
1046 body = cls.admin_client.create_metering_label(
1047 description=description,
1048 name=data_utils.rand_name("metering-label"))
1049 metering_label = body['metering_label']
1050 cls.metering_labels.append(metering_label)
1051 return metering_label
1052
1053 @classmethod
1054 def create_metering_label_rule(cls, remote_ip_prefix, direction,
1055 metering_label_id):
1056 """Wrapper utility that returns a test metering label rule."""
1057 body = cls.admin_client.create_metering_label_rule(
1058 remote_ip_prefix=remote_ip_prefix, direction=direction,
1059 metering_label_id=metering_label_id)
1060 metering_label_rule = body['metering_label_rule']
1061 cls.metering_label_rules.append(metering_label_rule)
1062 return metering_label_rule
1063
1064 @classmethod
Kailun Qineaaf9782018-12-20 04:45:01 +08001065 def create_network_segment_range(cls, name, shared,
1066 project_id, network_type,
1067 physical_network, minimum,
1068 maximum):
1069 """Wrapper utility that returns a test network segment range."""
1070 network_segment_range_args = {'name': name,
1071 'shared': shared,
1072 'project_id': project_id,
1073 'network_type': network_type,
1074 'physical_network': physical_network,
1075 'minimum': minimum,
1076 'maximum': maximum}
1077 body = cls.admin_client.create_network_segment_range(
1078 **network_segment_range_args)
1079 network_segment_range = body['network_segment_range']
1080 cls.network_segment_ranges.append(network_segment_range)
1081 return network_segment_range
1082
1083 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001084 def create_flavor(cls, name, description, service_type):
1085 """Wrapper utility that returns a test flavor."""
1086 body = cls.admin_client.create_flavor(
1087 description=description, service_type=service_type,
1088 name=name)
1089 flavor = body['flavor']
1090 cls.flavors.append(flavor)
1091 return flavor
1092
1093 @classmethod
1094 def create_service_profile(cls, description, metainfo, driver):
1095 """Wrapper utility that returns a test service profile."""
1096 body = cls.admin_client.create_service_profile(
1097 driver=driver, metainfo=metainfo, description=description)
1098 service_profile = body['service_profile']
1099 cls.service_profiles.append(service_profile)
1100 return service_profile
1101
1102 @classmethod
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001103 def create_log(cls, name, description=None,
1104 resource_type='security_group', resource_id=None,
1105 target_id=None, event='ALL', enabled=True):
1106 """Wrapper utility that returns a test log object."""
1107 log_args = {'name': name,
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001108 'resource_type': resource_type,
1109 'resource_id': resource_id,
1110 'target_id': target_id,
1111 'event': event,
1112 'enabled': enabled}
Slawek Kaplonskid9fe3022021-08-11 15:25:16 +02001113 if description:
1114 log_args['description'] = description
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001115 body = cls.admin_client.create_log(**log_args)
1116 log_object = body['log']
1117 cls.log_objects.append(log_object)
1118 return log_object
1119
1120 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001121 def get_unused_ip(cls, net_id, ip_version=None):
Gary Kotton011345f2016-06-15 08:04:31 -07001122 """Get an unused ip address in a allocation pool of net"""
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001123 body = cls.admin_client.list_ports(network_id=net_id)
1124 ports = body['ports']
1125 used_ips = []
1126 for port in ports:
1127 used_ips.extend(
1128 [fixed_ip['ip_address'] for fixed_ip in port['fixed_ips']])
1129 body = cls.admin_client.list_subnets(network_id=net_id)
1130 subnets = body['subnets']
1131
1132 for subnet in subnets:
1133 if ip_version and subnet['ip_version'] != ip_version:
1134 continue
1135 cidr = subnet['cidr']
1136 allocation_pools = subnet['allocation_pools']
1137 iterators = []
1138 if allocation_pools:
1139 for allocation_pool in allocation_pools:
1140 iterators.append(netaddr.iter_iprange(
1141 allocation_pool['start'], allocation_pool['end']))
1142 else:
1143 net = netaddr.IPNetwork(cidr)
1144
1145 def _iterip():
1146 for ip in net:
1147 if ip not in (net.network, net.broadcast):
1148 yield ip
1149 iterators.append(iter(_iterip()))
1150
1151 for iterator in iterators:
1152 for ip in iterator:
1153 if str(ip) not in used_ips:
1154 return str(ip)
1155
1156 message = (
1157 "net(%s) has no usable IP address in allocation pools" % net_id)
1158 raise exceptions.InvalidConfiguration(message)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001159
Lajos Katona2f904652018-08-23 14:04:56 +02001160 @classmethod
1161 def create_provider_network(cls, physnet_name, start_segmentation_id,
1162 max_attempts=30):
1163 segmentation_id = start_segmentation_id
Lajos Katona7eb67252019-01-14 12:55:35 +01001164 for attempts in range(max_attempts):
Lajos Katona2f904652018-08-23 14:04:56 +02001165 try:
Lajos Katona7eb67252019-01-14 12:55:35 +01001166 return cls.create_network(
Lajos Katona2f904652018-08-23 14:04:56 +02001167 name=data_utils.rand_name('test_net'),
1168 shared=True,
1169 provider_network_type='vlan',
1170 provider_physical_network=physnet_name,
1171 provider_segmentation_id=segmentation_id)
Lajos Katona2f904652018-08-23 14:04:56 +02001172 except lib_exc.Conflict:
Lajos Katona2f904652018-08-23 14:04:56 +02001173 segmentation_id += 1
1174 if segmentation_id > 4095:
1175 raise lib_exc.TempestException(
1176 "No free segmentation id was found for provider "
1177 "network creation!")
1178 time.sleep(CONF.network.build_interval)
Lajos Katona7eb67252019-01-14 12:55:35 +01001179 LOG.exception("Failed to create provider network after "
1180 "%d attempts", max_attempts)
1181 raise lib_exc.TimeoutException
Lajos Katona2f904652018-08-23 14:04:56 +02001182
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001183
Sławek Kapłońskiff294062016-12-04 15:00:54 +00001184def require_qos_rule_type(rule_type):
1185 def decorator(f):
1186 @functools.wraps(f)
1187 def wrapper(self, *func_args, **func_kwargs):
1188 if rule_type not in self.get_supported_qos_rule_types():
1189 raise self.skipException(
1190 "%s rule type is required." % rule_type)
1191 return f(self, *func_args, **func_kwargs)
1192 return wrapper
1193 return decorator
1194
1195
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001196def _require_sorting(f):
1197 @functools.wraps(f)
1198 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301199 if not tutils.is_extension_enabled("sorting", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001200 self.skipTest('Sorting feature is required')
1201 return f(self, *args, **kwargs)
1202 return inner
1203
1204
1205def _require_pagination(f):
1206 @functools.wraps(f)
1207 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301208 if not tutils.is_extension_enabled("pagination", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001209 self.skipTest('Pagination feature is required')
1210 return f(self, *args, **kwargs)
1211 return inner
1212
1213
1214class BaseSearchCriteriaTest(BaseNetworkTest):
1215
1216 # This should be defined by subclasses to reflect resource name to test
1217 resource = None
1218
Armando Migliaccio57581c62016-07-01 10:13:19 -07001219 field = 'name'
1220
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001221 # NOTE(ihrachys): some names, like those starting with an underscore (_)
1222 # are sorted differently depending on whether the plugin implements native
1223 # sorting support, or not. So we avoid any such cases here, sticking to
1224 # alphanumeric. Also test a case when there are multiple resources with the
1225 # same name
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001226 resource_names = ('test1', 'abc1', 'test10', '123test') + ('test1',)
1227
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001228 force_tenant_isolation = True
1229
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001230 list_kwargs = {}
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001231
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001232 list_as_admin = False
1233
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001234 def assertSameOrder(self, original, actual):
1235 # gracefully handle iterators passed
1236 original = list(original)
1237 actual = list(actual)
1238 self.assertEqual(len(original), len(actual))
1239 for expected, res in zip(original, actual):
Armando Migliaccio57581c62016-07-01 10:13:19 -07001240 self.assertEqual(expected[self.field], res[self.field])
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001241
1242 @utils.classproperty
1243 def plural_name(self):
1244 return '%ss' % self.resource
1245
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001246 @property
1247 def list_client(self):
1248 return self.admin_client if self.list_as_admin else self.client
1249
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001250 def list_method(self, *args, **kwargs):
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001251 method = getattr(self.list_client, 'list_%s' % self.plural_name)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001252 kwargs.update(self.list_kwargs)
1253 return method(*args, **kwargs)
1254
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001255 def get_bare_url(self, url):
1256 base_url = self.client.base_url
zheng.yong74e760a2019-05-22 14:16:14 +08001257 base_url_normalized = utils.normalize_url(base_url)
1258 url_normalized = utils.normalize_url(url)
1259 self.assertTrue(url_normalized.startswith(base_url_normalized))
1260 return url_normalized[len(base_url_normalized):]
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001261
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001262 @classmethod
1263 def _extract_resources(cls, body):
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001264 return body[cls.plural_name]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001265
1266 def _test_list_sorts(self, direction):
1267 sort_args = {
1268 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001269 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001270 }
1271 body = self.list_method(**sort_args)
1272 resources = self._extract_resources(body)
1273 self.assertNotEmpty(
1274 resources, "%s list returned is empty" % self.resource)
Armando Migliaccio57581c62016-07-01 10:13:19 -07001275 retrieved_names = [res[self.field] for res in resources]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001276 expected = sorted(retrieved_names)
1277 if direction == constants.SORT_DIRECTION_DESC:
1278 expected = list(reversed(expected))
1279 self.assertEqual(expected, retrieved_names)
1280
1281 @_require_sorting
1282 def _test_list_sorts_asc(self):
1283 self._test_list_sorts(constants.SORT_DIRECTION_ASC)
1284
1285 @_require_sorting
1286 def _test_list_sorts_desc(self):
1287 self._test_list_sorts(constants.SORT_DIRECTION_DESC)
1288
1289 @_require_pagination
1290 def _test_list_pagination(self):
1291 for limit in range(1, len(self.resource_names) + 1):
1292 pagination_args = {
1293 'limit': limit,
1294 }
1295 body = self.list_method(**pagination_args)
1296 resources = self._extract_resources(body)
1297 self.assertEqual(limit, len(resources))
1298
1299 @_require_pagination
1300 def _test_list_no_pagination_limit_0(self):
1301 pagination_args = {
1302 'limit': 0,
1303 }
1304 body = self.list_method(**pagination_args)
1305 resources = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001306 self.assertGreaterEqual(len(resources), len(self.resource_names))
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001307
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001308 def _test_list_pagination_iteratively(self, lister):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001309 # first, collect all resources for later comparison
1310 sort_args = {
1311 'sort_dir': constants.SORT_DIRECTION_ASC,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001312 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001313 }
1314 body = self.list_method(**sort_args)
1315 expected_resources = self._extract_resources(body)
1316 self.assertNotEmpty(expected_resources)
1317
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001318 resources = lister(
1319 len(expected_resources), sort_args
1320 )
1321
1322 # finally, compare that the list retrieved in one go is identical to
1323 # the one containing pagination results
1324 self.assertSameOrder(expected_resources, resources)
1325
1326 def _list_all_with_marker(self, niterations, sort_args):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001327 # paginate resources one by one, using last fetched resource as a
1328 # marker
1329 resources = []
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001330 for i in range(niterations):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001331 pagination_args = sort_args.copy()
1332 pagination_args['limit'] = 1
1333 if resources:
1334 pagination_args['marker'] = resources[-1]['id']
1335 body = self.list_method(**pagination_args)
1336 resources_ = self._extract_resources(body)
1337 self.assertEqual(1, len(resources_))
1338 resources.extend(resources_)
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001339 return resources
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001340
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001341 @_require_pagination
1342 @_require_sorting
1343 def _test_list_pagination_with_marker(self):
1344 self._test_list_pagination_iteratively(self._list_all_with_marker)
1345
1346 def _list_all_with_hrefs(self, niterations, sort_args):
1347 # paginate resources one by one, using next href links
1348 resources = []
1349 prev_links = {}
1350
1351 for i in range(niterations):
1352 if prev_links:
1353 uri = self.get_bare_url(prev_links['next'])
1354 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001355 sort_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001356 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001357 self.plural_name, limit=1, **sort_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001358 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001359 self.plural_name, uri
1360 )
1361 resources_ = self._extract_resources(body)
1362 self.assertEqual(1, len(resources_))
1363 resources.extend(resources_)
1364
1365 # The last element is empty and does not contain 'next' link
1366 uri = self.get_bare_url(prev_links['next'])
1367 prev_links, body = self.client.get_uri_with_links(
1368 self.plural_name, uri
1369 )
1370 self.assertNotIn('next', prev_links)
1371
1372 # Now walk backwards and compare results
1373 resources2 = []
1374 for i in range(niterations):
1375 uri = self.get_bare_url(prev_links['previous'])
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001376 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001377 self.plural_name, uri
1378 )
1379 resources_ = self._extract_resources(body)
1380 self.assertEqual(1, len(resources_))
1381 resources2.extend(resources_)
1382
1383 self.assertSameOrder(resources, reversed(resources2))
1384
1385 return resources
1386
1387 @_require_pagination
1388 @_require_sorting
1389 def _test_list_pagination_with_href_links(self):
1390 self._test_list_pagination_iteratively(self._list_all_with_hrefs)
1391
1392 @_require_pagination
1393 @_require_sorting
1394 def _test_list_pagination_page_reverse_with_href_links(
1395 self, direction=constants.SORT_DIRECTION_ASC):
1396 pagination_args = {
1397 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001398 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001399 }
1400 body = self.list_method(**pagination_args)
1401 expected_resources = self._extract_resources(body)
1402
1403 page_size = 2
1404 pagination_args['limit'] = page_size
1405
1406 prev_links = {}
1407 resources = []
1408 num_resources = len(expected_resources)
1409 niterations = int(math.ceil(float(num_resources) / page_size))
1410 for i in range(niterations):
1411 if prev_links:
1412 uri = self.get_bare_url(prev_links['previous'])
1413 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001414 pagination_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001415 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001416 self.plural_name, page_reverse=True, **pagination_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001417 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001418 self.plural_name, uri
1419 )
1420 resources_ = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001421 self.assertGreaterEqual(page_size, len(resources_))
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001422 resources.extend(reversed(resources_))
1423
1424 self.assertSameOrder(expected_resources, reversed(resources))
1425
1426 @_require_pagination
1427 @_require_sorting
1428 def _test_list_pagination_page_reverse_asc(self):
1429 self._test_list_pagination_page_reverse(
1430 direction=constants.SORT_DIRECTION_ASC)
1431
1432 @_require_pagination
1433 @_require_sorting
1434 def _test_list_pagination_page_reverse_desc(self):
1435 self._test_list_pagination_page_reverse(
1436 direction=constants.SORT_DIRECTION_DESC)
1437
1438 def _test_list_pagination_page_reverse(self, direction):
1439 pagination_args = {
1440 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001441 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001442 'limit': 3,
1443 }
1444 body = self.list_method(**pagination_args)
1445 expected_resources = self._extract_resources(body)
1446
1447 pagination_args['limit'] -= 1
1448 pagination_args['marker'] = expected_resources[-1]['id']
1449 pagination_args['page_reverse'] = True
1450 body = self.list_method(**pagination_args)
1451
1452 self.assertSameOrder(
1453 # the last entry is not included in 2nd result when used as a
1454 # marker
1455 expected_resources[:-1],
1456 self._extract_resources(body))
Victor Morales1be97b42016-09-05 08:50:06 -05001457
Hongbin Lu54f55922018-07-12 19:05:39 +00001458 @tutils.requires_ext(extension="filter-validation", service="network")
1459 def _test_list_validation_filters(
1460 self, validation_args, filter_is_valid=True):
1461 if not filter_is_valid:
1462 self.assertRaises(lib_exc.BadRequest, self.list_method,
1463 **validation_args)
1464 else:
1465 body = self.list_method(**validation_args)
1466 resources = self._extract_resources(body)
1467 for resource in resources:
1468 self.assertIn(resource['name'], self.resource_names)