blob: ecdd00aadcf44176a9c7b2855e6bbd1c2481dd60 [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,
1108 'description': description,
1109 'resource_type': resource_type,
1110 'resource_id': resource_id,
1111 'target_id': target_id,
1112 'event': event,
1113 'enabled': enabled}
1114 body = cls.admin_client.create_log(**log_args)
1115 log_object = body['log']
1116 cls.log_objects.append(log_object)
1117 return log_object
1118
1119 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001120 def get_unused_ip(cls, net_id, ip_version=None):
Gary Kotton011345f2016-06-15 08:04:31 -07001121 """Get an unused ip address in a allocation pool of net"""
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001122 body = cls.admin_client.list_ports(network_id=net_id)
1123 ports = body['ports']
1124 used_ips = []
1125 for port in ports:
1126 used_ips.extend(
1127 [fixed_ip['ip_address'] for fixed_ip in port['fixed_ips']])
1128 body = cls.admin_client.list_subnets(network_id=net_id)
1129 subnets = body['subnets']
1130
1131 for subnet in subnets:
1132 if ip_version and subnet['ip_version'] != ip_version:
1133 continue
1134 cidr = subnet['cidr']
1135 allocation_pools = subnet['allocation_pools']
1136 iterators = []
1137 if allocation_pools:
1138 for allocation_pool in allocation_pools:
1139 iterators.append(netaddr.iter_iprange(
1140 allocation_pool['start'], allocation_pool['end']))
1141 else:
1142 net = netaddr.IPNetwork(cidr)
1143
1144 def _iterip():
1145 for ip in net:
1146 if ip not in (net.network, net.broadcast):
1147 yield ip
1148 iterators.append(iter(_iterip()))
1149
1150 for iterator in iterators:
1151 for ip in iterator:
1152 if str(ip) not in used_ips:
1153 return str(ip)
1154
1155 message = (
1156 "net(%s) has no usable IP address in allocation pools" % net_id)
1157 raise exceptions.InvalidConfiguration(message)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001158
Lajos Katona2f904652018-08-23 14:04:56 +02001159 @classmethod
1160 def create_provider_network(cls, physnet_name, start_segmentation_id,
1161 max_attempts=30):
1162 segmentation_id = start_segmentation_id
Lajos Katona7eb67252019-01-14 12:55:35 +01001163 for attempts in range(max_attempts):
Lajos Katona2f904652018-08-23 14:04:56 +02001164 try:
Lajos Katona7eb67252019-01-14 12:55:35 +01001165 return cls.create_network(
Lajos Katona2f904652018-08-23 14:04:56 +02001166 name=data_utils.rand_name('test_net'),
1167 shared=True,
1168 provider_network_type='vlan',
1169 provider_physical_network=physnet_name,
1170 provider_segmentation_id=segmentation_id)
Lajos Katona2f904652018-08-23 14:04:56 +02001171 except lib_exc.Conflict:
Lajos Katona2f904652018-08-23 14:04:56 +02001172 segmentation_id += 1
1173 if segmentation_id > 4095:
1174 raise lib_exc.TempestException(
1175 "No free segmentation id was found for provider "
1176 "network creation!")
1177 time.sleep(CONF.network.build_interval)
Lajos Katona7eb67252019-01-14 12:55:35 +01001178 LOG.exception("Failed to create provider network after "
1179 "%d attempts", max_attempts)
1180 raise lib_exc.TimeoutException
Lajos Katona2f904652018-08-23 14:04:56 +02001181
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001182
Sławek Kapłońskiff294062016-12-04 15:00:54 +00001183def require_qos_rule_type(rule_type):
1184 def decorator(f):
1185 @functools.wraps(f)
1186 def wrapper(self, *func_args, **func_kwargs):
1187 if rule_type not in self.get_supported_qos_rule_types():
1188 raise self.skipException(
1189 "%s rule type is required." % rule_type)
1190 return f(self, *func_args, **func_kwargs)
1191 return wrapper
1192 return decorator
1193
1194
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001195def _require_sorting(f):
1196 @functools.wraps(f)
1197 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301198 if not tutils.is_extension_enabled("sorting", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001199 self.skipTest('Sorting feature is required')
1200 return f(self, *args, **kwargs)
1201 return inner
1202
1203
1204def _require_pagination(f):
1205 @functools.wraps(f)
1206 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301207 if not tutils.is_extension_enabled("pagination", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001208 self.skipTest('Pagination feature is required')
1209 return f(self, *args, **kwargs)
1210 return inner
1211
1212
1213class BaseSearchCriteriaTest(BaseNetworkTest):
1214
1215 # This should be defined by subclasses to reflect resource name to test
1216 resource = None
1217
Armando Migliaccio57581c62016-07-01 10:13:19 -07001218 field = 'name'
1219
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001220 # NOTE(ihrachys): some names, like those starting with an underscore (_)
1221 # are sorted differently depending on whether the plugin implements native
1222 # sorting support, or not. So we avoid any such cases here, sticking to
1223 # alphanumeric. Also test a case when there are multiple resources with the
1224 # same name
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001225 resource_names = ('test1', 'abc1', 'test10', '123test') + ('test1',)
1226
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001227 force_tenant_isolation = True
1228
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001229 list_kwargs = {}
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001230
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001231 list_as_admin = False
1232
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001233 def assertSameOrder(self, original, actual):
1234 # gracefully handle iterators passed
1235 original = list(original)
1236 actual = list(actual)
1237 self.assertEqual(len(original), len(actual))
1238 for expected, res in zip(original, actual):
Armando Migliaccio57581c62016-07-01 10:13:19 -07001239 self.assertEqual(expected[self.field], res[self.field])
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001240
1241 @utils.classproperty
1242 def plural_name(self):
1243 return '%ss' % self.resource
1244
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001245 @property
1246 def list_client(self):
1247 return self.admin_client if self.list_as_admin else self.client
1248
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001249 def list_method(self, *args, **kwargs):
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001250 method = getattr(self.list_client, 'list_%s' % self.plural_name)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001251 kwargs.update(self.list_kwargs)
1252 return method(*args, **kwargs)
1253
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001254 def get_bare_url(self, url):
1255 base_url = self.client.base_url
zheng.yong74e760a2019-05-22 14:16:14 +08001256 base_url_normalized = utils.normalize_url(base_url)
1257 url_normalized = utils.normalize_url(url)
1258 self.assertTrue(url_normalized.startswith(base_url_normalized))
1259 return url_normalized[len(base_url_normalized):]
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001260
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001261 @classmethod
1262 def _extract_resources(cls, body):
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001263 return body[cls.plural_name]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001264
1265 def _test_list_sorts(self, direction):
1266 sort_args = {
1267 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001268 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001269 }
1270 body = self.list_method(**sort_args)
1271 resources = self._extract_resources(body)
1272 self.assertNotEmpty(
1273 resources, "%s list returned is empty" % self.resource)
Armando Migliaccio57581c62016-07-01 10:13:19 -07001274 retrieved_names = [res[self.field] for res in resources]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001275 expected = sorted(retrieved_names)
1276 if direction == constants.SORT_DIRECTION_DESC:
1277 expected = list(reversed(expected))
1278 self.assertEqual(expected, retrieved_names)
1279
1280 @_require_sorting
1281 def _test_list_sorts_asc(self):
1282 self._test_list_sorts(constants.SORT_DIRECTION_ASC)
1283
1284 @_require_sorting
1285 def _test_list_sorts_desc(self):
1286 self._test_list_sorts(constants.SORT_DIRECTION_DESC)
1287
1288 @_require_pagination
1289 def _test_list_pagination(self):
1290 for limit in range(1, len(self.resource_names) + 1):
1291 pagination_args = {
1292 'limit': limit,
1293 }
1294 body = self.list_method(**pagination_args)
1295 resources = self._extract_resources(body)
1296 self.assertEqual(limit, len(resources))
1297
1298 @_require_pagination
1299 def _test_list_no_pagination_limit_0(self):
1300 pagination_args = {
1301 'limit': 0,
1302 }
1303 body = self.list_method(**pagination_args)
1304 resources = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001305 self.assertGreaterEqual(len(resources), len(self.resource_names))
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001306
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001307 def _test_list_pagination_iteratively(self, lister):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001308 # first, collect all resources for later comparison
1309 sort_args = {
1310 'sort_dir': constants.SORT_DIRECTION_ASC,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001311 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001312 }
1313 body = self.list_method(**sort_args)
1314 expected_resources = self._extract_resources(body)
1315 self.assertNotEmpty(expected_resources)
1316
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001317 resources = lister(
1318 len(expected_resources), sort_args
1319 )
1320
1321 # finally, compare that the list retrieved in one go is identical to
1322 # the one containing pagination results
1323 self.assertSameOrder(expected_resources, resources)
1324
1325 def _list_all_with_marker(self, niterations, sort_args):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001326 # paginate resources one by one, using last fetched resource as a
1327 # marker
1328 resources = []
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001329 for i in range(niterations):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001330 pagination_args = sort_args.copy()
1331 pagination_args['limit'] = 1
1332 if resources:
1333 pagination_args['marker'] = resources[-1]['id']
1334 body = self.list_method(**pagination_args)
1335 resources_ = self._extract_resources(body)
1336 self.assertEqual(1, len(resources_))
1337 resources.extend(resources_)
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001338 return resources
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001339
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001340 @_require_pagination
1341 @_require_sorting
1342 def _test_list_pagination_with_marker(self):
1343 self._test_list_pagination_iteratively(self._list_all_with_marker)
1344
1345 def _list_all_with_hrefs(self, niterations, sort_args):
1346 # paginate resources one by one, using next href links
1347 resources = []
1348 prev_links = {}
1349
1350 for i in range(niterations):
1351 if prev_links:
1352 uri = self.get_bare_url(prev_links['next'])
1353 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001354 sort_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001355 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001356 self.plural_name, limit=1, **sort_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001357 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001358 self.plural_name, uri
1359 )
1360 resources_ = self._extract_resources(body)
1361 self.assertEqual(1, len(resources_))
1362 resources.extend(resources_)
1363
1364 # The last element is empty and does not contain 'next' link
1365 uri = self.get_bare_url(prev_links['next'])
1366 prev_links, body = self.client.get_uri_with_links(
1367 self.plural_name, uri
1368 )
1369 self.assertNotIn('next', prev_links)
1370
1371 # Now walk backwards and compare results
1372 resources2 = []
1373 for i in range(niterations):
1374 uri = self.get_bare_url(prev_links['previous'])
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001375 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001376 self.plural_name, uri
1377 )
1378 resources_ = self._extract_resources(body)
1379 self.assertEqual(1, len(resources_))
1380 resources2.extend(resources_)
1381
1382 self.assertSameOrder(resources, reversed(resources2))
1383
1384 return resources
1385
1386 @_require_pagination
1387 @_require_sorting
1388 def _test_list_pagination_with_href_links(self):
1389 self._test_list_pagination_iteratively(self._list_all_with_hrefs)
1390
1391 @_require_pagination
1392 @_require_sorting
1393 def _test_list_pagination_page_reverse_with_href_links(
1394 self, direction=constants.SORT_DIRECTION_ASC):
1395 pagination_args = {
1396 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001397 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001398 }
1399 body = self.list_method(**pagination_args)
1400 expected_resources = self._extract_resources(body)
1401
1402 page_size = 2
1403 pagination_args['limit'] = page_size
1404
1405 prev_links = {}
1406 resources = []
1407 num_resources = len(expected_resources)
1408 niterations = int(math.ceil(float(num_resources) / page_size))
1409 for i in range(niterations):
1410 if prev_links:
1411 uri = self.get_bare_url(prev_links['previous'])
1412 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001413 pagination_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001414 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001415 self.plural_name, page_reverse=True, **pagination_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001416 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001417 self.plural_name, uri
1418 )
1419 resources_ = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001420 self.assertGreaterEqual(page_size, len(resources_))
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001421 resources.extend(reversed(resources_))
1422
1423 self.assertSameOrder(expected_resources, reversed(resources))
1424
1425 @_require_pagination
1426 @_require_sorting
1427 def _test_list_pagination_page_reverse_asc(self):
1428 self._test_list_pagination_page_reverse(
1429 direction=constants.SORT_DIRECTION_ASC)
1430
1431 @_require_pagination
1432 @_require_sorting
1433 def _test_list_pagination_page_reverse_desc(self):
1434 self._test_list_pagination_page_reverse(
1435 direction=constants.SORT_DIRECTION_DESC)
1436
1437 def _test_list_pagination_page_reverse(self, direction):
1438 pagination_args = {
1439 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001440 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001441 'limit': 3,
1442 }
1443 body = self.list_method(**pagination_args)
1444 expected_resources = self._extract_resources(body)
1445
1446 pagination_args['limit'] -= 1
1447 pagination_args['marker'] = expected_resources[-1]['id']
1448 pagination_args['page_reverse'] = True
1449 body = self.list_method(**pagination_args)
1450
1451 self.assertSameOrder(
1452 # the last entry is not included in 2nd result when used as a
1453 # marker
1454 expected_resources[:-1],
1455 self._extract_resources(body))
Victor Morales1be97b42016-09-05 08:50:06 -05001456
Hongbin Lu54f55922018-07-12 19:05:39 +00001457 @tutils.requires_ext(extension="filter-validation", service="network")
1458 def _test_list_validation_filters(
1459 self, validation_args, filter_is_valid=True):
1460 if not filter_is_valid:
1461 self.assertRaises(lib_exc.BadRequest, self.list_method,
1462 **validation_args)
1463 else:
1464 body = self.list_method(**validation_args)
1465 resources = self._extract_resources(body)
1466 for resource in resources:
1467 self.assertIn(resource['name'], self.resource_names)