blob: 08ba8d0fea92df4a886b432fe63f423ee353b280 [file] [log] [blame]
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001# Copyright 2012 OpenStack Foundation
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Ihar Hrachyshka59382252016-04-05 15:54:33 +020016import functools
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +020017import math
Lajos Katona2f904652018-08-23 14:04:56 +020018import time
Ihar Hrachyshka59382252016-04-05 15:54:33 +020019
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000020import netaddr
Chandan Kumarc125fd12017-11-15 19:41:01 +053021from neutron_lib import constants as const
Lajos Katona2f904652018-08-23 14:04:56 +020022from oslo_log import log
Chandan Kumarc125fd12017-11-15 19:41:01 +053023from tempest.common import utils as tutils
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000024from tempest.lib.common.utils import data_utils
25from tempest.lib import exceptions as lib_exc
26from tempest import test
27
Chandan Kumar667d3d32017-09-22 12:24:06 +053028from neutron_tempest_plugin.api import clients
29from neutron_tempest_plugin.common import constants
30from neutron_tempest_plugin.common import utils
31from neutron_tempest_plugin import config
32from neutron_tempest_plugin import exceptions
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000033
34CONF = config.CONF
35
Lajos Katona2f904652018-08-23 14:04:56 +020036LOG = log.getLogger(__name__)
37
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000038
39class BaseNetworkTest(test.BaseTestCase):
40
Brian Haleyae328b92018-10-09 19:51:54 -040041 """Base class for Neutron tests that use the Tempest Neutron REST client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000042
43 Per the Neutron API Guide, API v1.x was removed from the source code tree
44 (docs.openstack.org/api/openstack-network/2.0/content/Overview-d1e71.html)
45 Therefore, v2.x of the Neutron API is assumed. It is also assumed that the
46 following options are defined in the [network] section of etc/tempest.conf:
47
48 project_network_cidr with a block of cidr's from which smaller blocks
49 can be allocated for tenant networks
50
51 project_network_mask_bits with the mask bits to be used to partition
52 the block defined by tenant-network_cidr
53
54 Finally, it is assumed that the following option is defined in the
55 [service_available] section of etc/tempest.conf
56
57 neutron as True
58 """
59
60 force_tenant_isolation = False
61 credentials = ['primary']
62
63 # Default to ipv4.
Federico Ressi0ddc93b2018-04-09 12:01:48 +020064 _ip_version = const.IP_VERSION_4
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000065
Federico Ressi61b564e2018-07-06 08:10:31 +020066 # Derive from BaseAdminNetworkTest class to have this initialized
67 admin_client = None
68
Federico Ressia69dcd52018-07-06 09:45:34 +020069 external_network_id = CONF.network.public_network_id
70
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000071 @classmethod
72 def get_client_manager(cls, credential_type=None, roles=None,
73 force_new=None):
Genadi Chereshnyacc395c02016-07-25 12:17:37 +030074 manager = super(BaseNetworkTest, cls).get_client_manager(
75 credential_type=credential_type,
76 roles=roles,
77 force_new=force_new
78 )
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000079 # Neutron uses a different clients manager than the one in the Tempest
Jens Harbott860b46a2017-11-15 21:23:15 +000080 # save the original in case mixed tests need it
81 if credential_type == 'primary':
82 cls.os_tempest = manager
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000083 return clients.Manager(manager.credentials)
84
85 @classmethod
86 def skip_checks(cls):
87 super(BaseNetworkTest, cls).skip_checks()
88 if not CONF.service_available.neutron:
89 raise cls.skipException("Neutron support is required")
Federico Ressi0ddc93b2018-04-09 12:01:48 +020090 if (cls._ip_version == const.IP_VERSION_6 and
91 not CONF.network_feature_enabled.ipv6):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000092 raise cls.skipException("IPv6 Tests are disabled.")
Jakub Libosvar1982aa12017-05-30 11:15:33 +000093 for req_ext in getattr(cls, 'required_extensions', []):
Chandan Kumarc125fd12017-11-15 19:41:01 +053094 if not tutils.is_extension_enabled(req_ext, 'network'):
Jakub Libosvar1982aa12017-05-30 11:15:33 +000095 msg = "%s extension not enabled." % req_ext
96 raise cls.skipException(msg)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000097
98 @classmethod
99 def setup_credentials(cls):
100 # Create no network resources for these test.
101 cls.set_network_resources()
102 super(BaseNetworkTest, cls).setup_credentials()
103
104 @classmethod
105 def setup_clients(cls):
106 super(BaseNetworkTest, cls).setup_clients()
fumihiko kakumaa216fc12017-07-14 10:43:29 +0900107 cls.client = cls.os_primary.network_client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000108
Vasyl Saienkoe03ea792021-10-22 14:34:41 +0300109 # NOTE(vsaienko): when using static accounts we need
110 # to fill *_id information like project_id, user_id
111 # by authenticating in keystone
112 cls.client.auth_provider.get_token()
113
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000114 @classmethod
115 def resource_setup(cls):
116 super(BaseNetworkTest, cls).resource_setup()
117
118 cls.networks = []
Miguel Lavalle124378b2016-09-21 16:41:47 -0500119 cls.admin_networks = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000120 cls.subnets = []
Kevin Bentonba3651c2017-09-01 17:13:01 -0700121 cls.admin_subnets = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000122 cls.ports = []
123 cls.routers = []
124 cls.floating_ips = []
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200125 cls.port_forwardings = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000126 cls.metering_labels = []
127 cls.service_profiles = []
128 cls.flavors = []
129 cls.metering_label_rules = []
130 cls.qos_rules = []
131 cls.qos_policies = []
132 cls.ethertype = "IPv" + str(cls._ip_version)
133 cls.address_scopes = []
134 cls.admin_address_scopes = []
135 cls.subnetpools = []
136 cls.admin_subnetpools = []
Itzik Brownbac51dc2016-10-31 12:25:04 +0000137 cls.security_groups = []
Dongcan Ye2de722e2018-07-04 11:01:37 +0000138 cls.admin_security_groups = []
Chandan Kumarc125fd12017-11-15 19:41:01 +0530139 cls.projects = []
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700140 cls.log_objects = []
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200141 cls.reserved_subnet_cidrs = set()
Federico Ressiab286e42018-06-19 09:52:10 +0200142 cls.keypairs = []
Federico Ressi82e83e32018-07-03 14:19:55 +0200143 cls.trunks = []
Kailun Qineaaf9782018-12-20 04:45:01 +0800144 cls.network_segment_ranges = []
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200145 cls.conntrack_helpers = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000146
147 @classmethod
148 def resource_cleanup(cls):
149 if CONF.service_available.neutron:
Federico Ressi82e83e32018-07-03 14:19:55 +0200150 # Clean up trunks
151 for trunk in cls.trunks:
152 cls._try_delete_resource(cls.delete_trunk, trunk)
153
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200154 # Clean up port forwardings
155 for pf in cls.port_forwardings:
156 cls._try_delete_resource(cls.delete_port_forwarding, pf)
157
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000158 # Clean up floating IPs
159 for floating_ip in cls.floating_ips:
Federico Ressia69dcd52018-07-06 09:45:34 +0200160 cls._try_delete_resource(cls.delete_floatingip, floating_ip)
161
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200162 # Clean up conntrack helpers
163 for cth in cls.conntrack_helpers:
164 cls._try_delete_resource(cls.delete_conntrack_helper, cth)
165
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000166 # Clean up routers
167 for router in cls.routers:
168 cls._try_delete_resource(cls.delete_router,
169 router)
170 # Clean up metering label rules
171 for metering_label_rule in cls.metering_label_rules:
172 cls._try_delete_resource(
173 cls.admin_client.delete_metering_label_rule,
174 metering_label_rule['id'])
175 # Clean up metering labels
176 for metering_label in cls.metering_labels:
177 cls._try_delete_resource(
178 cls.admin_client.delete_metering_label,
179 metering_label['id'])
180 # Clean up flavors
181 for flavor in cls.flavors:
182 cls._try_delete_resource(
183 cls.admin_client.delete_flavor,
184 flavor['id'])
185 # Clean up service profiles
186 for service_profile in cls.service_profiles:
187 cls._try_delete_resource(
188 cls.admin_client.delete_service_profile,
189 service_profile['id'])
190 # Clean up ports
191 for port in cls.ports:
192 cls._try_delete_resource(cls.client.delete_port,
193 port['id'])
194 # Clean up subnets
195 for subnet in cls.subnets:
196 cls._try_delete_resource(cls.client.delete_subnet,
197 subnet['id'])
Kevin Bentonba3651c2017-09-01 17:13:01 -0700198 # Clean up admin subnets
199 for subnet in cls.admin_subnets:
200 cls._try_delete_resource(cls.admin_client.delete_subnet,
201 subnet['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000202 # Clean up networks
203 for network in cls.networks:
Federico Ressi61b564e2018-07-06 08:10:31 +0200204 cls._try_delete_resource(cls.delete_network, network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000205
Miguel Lavalle124378b2016-09-21 16:41:47 -0500206 # Clean up admin networks
207 for network in cls.admin_networks:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000208 cls._try_delete_resource(cls.admin_client.delete_network,
209 network['id'])
210
Itzik Brownbac51dc2016-10-31 12:25:04 +0000211 # Clean up security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200212 for security_group in cls.security_groups:
213 cls._try_delete_resource(cls.delete_security_group,
214 security_group)
Itzik Brownbac51dc2016-10-31 12:25:04 +0000215
Dongcan Ye2de722e2018-07-04 11:01:37 +0000216 # Clean up admin security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200217 for security_group in cls.admin_security_groups:
218 cls._try_delete_resource(cls.delete_security_group,
219 security_group,
220 client=cls.admin_client)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000221
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000222 for subnetpool in cls.subnetpools:
223 cls._try_delete_resource(cls.client.delete_subnetpool,
224 subnetpool['id'])
225
226 for subnetpool in cls.admin_subnetpools:
227 cls._try_delete_resource(cls.admin_client.delete_subnetpool,
228 subnetpool['id'])
229
230 for address_scope in cls.address_scopes:
231 cls._try_delete_resource(cls.client.delete_address_scope,
232 address_scope['id'])
233
234 for address_scope in cls.admin_address_scopes:
235 cls._try_delete_resource(
236 cls.admin_client.delete_address_scope,
237 address_scope['id'])
238
Chandan Kumarc125fd12017-11-15 19:41:01 +0530239 for project in cls.projects:
240 cls._try_delete_resource(
241 cls.identity_admin_client.delete_project,
242 project['id'])
243
Sławek Kapłońskie100c4d2017-08-23 21:18:34 +0000244 # Clean up QoS rules
245 for qos_rule in cls.qos_rules:
246 cls._try_delete_resource(cls.admin_client.delete_qos_rule,
247 qos_rule['id'])
248 # Clean up QoS policies
249 # as all networks and ports are already removed, QoS policies
250 # shouldn't be "in use"
251 for qos_policy in cls.qos_policies:
252 cls._try_delete_resource(cls.admin_client.delete_qos_policy,
253 qos_policy['id'])
254
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700255 # Clean up log_objects
256 for log_object in cls.log_objects:
257 cls._try_delete_resource(cls.admin_client.delete_log,
258 log_object['id'])
259
Federico Ressiab286e42018-06-19 09:52:10 +0200260 for keypair in cls.keypairs:
261 cls._try_delete_resource(cls.delete_keypair, keypair)
262
Kailun Qineaaf9782018-12-20 04:45:01 +0800263 # Clean up network_segment_ranges
264 for network_segment_range in cls.network_segment_ranges:
265 cls._try_delete_resource(
266 cls.admin_client.delete_network_segment_range,
267 network_segment_range['id'])
268
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000269 super(BaseNetworkTest, cls).resource_cleanup()
270
271 @classmethod
272 def _try_delete_resource(cls, delete_callable, *args, **kwargs):
273 """Cleanup resources in case of test-failure
274
275 Some resources are explicitly deleted by the test.
276 If the test failed to delete a resource, this method will execute
277 the appropriate delete methods. Otherwise, the method ignores NotFound
278 exceptions thrown for resources that were correctly deleted by the
279 test.
280
281 :param delete_callable: delete method
282 :param args: arguments for delete method
283 :param kwargs: keyword arguments for delete method
284 """
285 try:
286 delete_callable(*args, **kwargs)
287 # if resource is not found, this means it was deleted in the test
288 except lib_exc.NotFound:
289 pass
290
291 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200292 def create_network(cls, network_name=None, client=None, external=None,
293 shared=None, provider_network_type=None,
294 provider_physical_network=None,
295 provider_segmentation_id=None, **kwargs):
296 """Create a network.
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000297
Federico Ressi61b564e2018-07-06 08:10:31 +0200298 When client is not provider and admin_client is attribute is not None
299 (for example when using BaseAdminNetworkTest base class) and using any
300 of the convenience parameters (external, shared, provider_network_type,
301 provider_physical_network and provider_segmentation_id) it silently
302 uses admin_client. If the network is not shared then it uses the same
303 project_id as regular client.
304
305 :param network_name: Human-readable name of the network
306
307 :param client: client to be used for connecting to network service
308
309 :param external: indicates whether the network has an external routing
310 facility that's not managed by the networking service.
311
312 :param shared: indicates whether this resource is shared across all
313 projects. By default, only administrative users can change this value.
314 If True and admin_client attribute is not None, then the network is
315 created under administrative project.
316
317 :param provider_network_type: the type of physical network that this
318 network should be mapped to. For example, 'flat', 'vlan', 'vxlan', or
319 'gre'. Valid values depend on a networking back-end.
320
321 :param provider_physical_network: the physical network where this
322 network should be implemented. The Networking API v2.0 does not provide
323 a way to list available physical networks. For example, the Open
324 vSwitch plug-in configuration file defines a symbolic name that maps to
325 specific bridges on each compute host.
326
327 :param provider_segmentation_id: The ID of the isolated segment on the
328 physical network. The network_type attribute defines the segmentation
329 model. For example, if the network_type value is 'vlan', this ID is a
330 vlan identifier. If the network_type value is 'gre', this ID is a gre
331 key.
332
333 :param **kwargs: extra parameters to be forwarded to network service
334 """
335
336 name = (network_name or kwargs.pop('name', None) or
337 data_utils.rand_name('test-network-'))
338
339 # translate convenience parameters
340 admin_client_required = False
341 if provider_network_type:
342 admin_client_required = True
343 kwargs['provider:network_type'] = provider_network_type
344 if provider_physical_network:
345 admin_client_required = True
346 kwargs['provider:physical_network'] = provider_physical_network
347 if provider_segmentation_id:
348 admin_client_required = True
349 kwargs['provider:segmentation_id'] = provider_segmentation_id
350 if external is not None:
351 admin_client_required = True
352 kwargs['router:external'] = bool(external)
353 if shared is not None:
354 admin_client_required = True
355 kwargs['shared'] = bool(shared)
356
357 if not client:
358 if admin_client_required and cls.admin_client:
359 # For convenience silently switch to admin client
360 client = cls.admin_client
361 if not shared:
362 # Keep this network visible from current project
363 project_id = (kwargs.get('project_id') or
364 kwargs.get('tenant_id') or
365 cls.client.tenant_id)
366 kwargs.update(project_id=project_id, tenant_id=project_id)
367 else:
368 # Use default client
369 client = cls.client
370
371 network = client.create_network(name=name, **kwargs)['network']
372 network['client'] = client
373 cls.networks.append(network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000374 return network
375
376 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200377 def delete_network(cls, network, client=None):
378 client = client or network.get('client') or cls.client
379 client.delete_network(network['id'])
380
381 @classmethod
382 def create_shared_network(cls, network_name=None, **kwargs):
383 return cls.create_network(name=network_name, shared=True, **kwargs)
Miguel Lavalle124378b2016-09-21 16:41:47 -0500384
385 @classmethod
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200386 def create_subnet(cls, network, gateway='', cidr=None, mask_bits=None,
Federico Ressi98f20ec2018-05-11 06:09:49 +0200387 ip_version=None, client=None, reserve_cidr=True,
388 **kwargs):
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200389 """Wrapper utility that returns a test subnet.
390
391 Convenient wrapper for client.create_subnet method. It reserves and
392 allocates CIDRs to avoid creating overlapping subnets.
393
394 :param network: network where to create the subnet
395 network['id'] must contain the ID of the network
396
397 :param gateway: gateway IP address
398 It can be a str or a netaddr.IPAddress
399 If gateway is not given, then it will use default address for
400 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 +0200401 if gateway is given as None then no gateway will be assigned
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200402
403 :param cidr: CIDR of the subnet to create
404 It can be either None, a str or a netaddr.IPNetwork instance
405
406 :param mask_bits: CIDR prefix length
407 It can be either None or a numeric value.
408 If cidr parameter is given then mask_bits is used to determinate a
409 sequence of valid CIDR to use as generated.
410 Please see netaddr.IPNetwork.subnet method documentation[1]
411
412 :param ip_version: ip version of generated subnet CIDRs
413 It can be None, IP_VERSION_4 or IP_VERSION_6
414 It has to match given either given CIDR and gateway
415
416 :param ip_version: numeric value (either IP_VERSION_4 or IP_VERSION_6)
417 this value must match CIDR and gateway IP versions if any of them is
418 given
419
420 :param client: client to be used to connect to network service
421
Federico Ressi98f20ec2018-05-11 06:09:49 +0200422 :param reserve_cidr: if True then it reserves assigned CIDR to avoid
423 using the same CIDR for further subnets in the scope of the same
424 test case class
425
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200426 :param **kwargs: optional parameters to be forwarded to wrapped method
427
428 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
429 """
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000430
431 # allow tests to use admin client
432 if not client:
433 client = cls.client
434
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200435 if gateway:
436 gateway_ip = netaddr.IPAddress(gateway)
437 if ip_version:
438 if ip_version != gateway_ip.version:
439 raise ValueError(
440 "Gateway IP version doesn't match IP version")
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000441 else:
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200442 ip_version = gateway_ip.version
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200443 else:
444 ip_version = ip_version or cls._ip_version
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200445
446 for subnet_cidr in cls.get_subnet_cidrs(
447 ip_version=ip_version, cidr=cidr, mask_bits=mask_bits):
Federico Ressi98f20ec2018-05-11 06:09:49 +0200448 if gateway is not None:
449 kwargs['gateway_ip'] = str(gateway or (subnet_cidr.ip + 1))
Slawek Kaplonski21f53422018-11-02 16:02:09 +0100450 else:
451 kwargs['gateway_ip'] = None
Federico Ressi98f20ec2018-05-11 06:09:49 +0200452 try:
453 body = client.create_subnet(
454 network_id=network['id'],
455 cidr=str(subnet_cidr),
456 ip_version=subnet_cidr.version,
457 **kwargs)
458 break
459 except lib_exc.BadRequest as e:
460 if 'overlaps with another subnet' not in str(e):
461 raise
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000462 else:
463 message = 'Available CIDR for subnet creation could not be found'
464 raise ValueError(message)
465 subnet = body['subnet']
Kevin Bentonba3651c2017-09-01 17:13:01 -0700466 if client is cls.client:
467 cls.subnets.append(subnet)
468 else:
469 cls.admin_subnets.append(subnet)
Federico Ressi98f20ec2018-05-11 06:09:49 +0200470 if reserve_cidr:
471 cls.reserve_subnet_cidr(subnet_cidr)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000472 return subnet
473
474 @classmethod
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200475 def reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
476 """Reserve given subnet CIDR making sure it is not used by create_subnet
477
478 :param addr: the CIDR address to be reserved
479 It can be a str or netaddr.IPNetwork instance
480
481 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
482 parameters
483 """
484
485 if not cls.try_reserve_subnet_cidr(addr, **ipnetwork_kwargs):
486 raise ValueError('Subnet CIDR already reserved: %r'.format(
487 addr))
488
489 @classmethod
490 def try_reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
491 """Reserve given subnet CIDR if it hasn't been reserved before
492
493 :param addr: the CIDR address to be reserved
494 It can be a str or netaddr.IPNetwork instance
495
496 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
497 parameters
498
499 :return: True if it wasn't reserved before, False elsewhere.
500 """
501
502 subnet_cidr = netaddr.IPNetwork(addr, **ipnetwork_kwargs)
503 if subnet_cidr in cls.reserved_subnet_cidrs:
504 return False
505 else:
506 cls.reserved_subnet_cidrs.add(subnet_cidr)
507 return True
508
509 @classmethod
510 def get_subnet_cidrs(
511 cls, cidr=None, mask_bits=None, ip_version=None):
512 """Iterate over a sequence of unused subnet CIDR for IP version
513
514 :param cidr: CIDR of the subnet to create
515 It can be either None, a str or a netaddr.IPNetwork instance
516
517 :param mask_bits: CIDR prefix length
518 It can be either None or a numeric value.
519 If cidr parameter is given then mask_bits is used to determinate a
520 sequence of valid CIDR to use as generated.
521 Please see netaddr.IPNetwork.subnet method documentation[1]
522
523 :param ip_version: ip version of generated subnet CIDRs
524 It can be None, IP_VERSION_4 or IP_VERSION_6
525 It has to match given CIDR if given
526
527 :return: iterator over reserved CIDRs of type netaddr.IPNetwork
528
529 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
530 """
531
532 if cidr:
533 # Generate subnet CIDRs starting from given CIDR
534 # checking it is of requested IP version
535 cidr = netaddr.IPNetwork(cidr, version=ip_version)
536 else:
537 # Generate subnet CIDRs starting from configured values
538 ip_version = ip_version or cls._ip_version
539 if ip_version == const.IP_VERSION_4:
540 mask_bits = mask_bits or config.safe_get_config_value(
541 'network', 'project_network_mask_bits')
542 cidr = netaddr.IPNetwork(config.safe_get_config_value(
543 'network', 'project_network_cidr'))
544 elif ip_version == const.IP_VERSION_6:
545 mask_bits = config.safe_get_config_value(
546 'network', 'project_network_v6_mask_bits')
547 cidr = netaddr.IPNetwork(config.safe_get_config_value(
548 'network', 'project_network_v6_cidr'))
549 else:
550 raise ValueError('Invalid IP version: {!r}'.format(ip_version))
551
552 if mask_bits:
553 subnet_cidrs = cidr.subnet(mask_bits)
554 else:
555 subnet_cidrs = iter([cidr])
556
557 for subnet_cidr in subnet_cidrs:
558 if subnet_cidr not in cls.reserved_subnet_cidrs:
559 yield subnet_cidr
560
561 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000562 def create_port(cls, network, **kwargs):
563 """Wrapper utility that returns a test port."""
Edan Davidd75e48e2018-01-03 02:49:52 -0500564 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
565 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000566 body = cls.client.create_port(network_id=network['id'],
567 **kwargs)
568 port = body['port']
569 cls.ports.append(port)
570 return port
571
572 @classmethod
573 def update_port(cls, port, **kwargs):
574 """Wrapper utility that updates a test port."""
575 body = cls.client.update_port(port['id'],
576 **kwargs)
577 return body['port']
578
579 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300580 def _create_router_with_client(
581 cls, client, router_name=None, admin_state_up=False,
582 external_network_id=None, enable_snat=None, **kwargs
583 ):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000584 ext_gw_info = {}
585 if external_network_id:
586 ext_gw_info['network_id'] = external_network_id
YAMAMOTO Takashi9bd4f972017-06-20 12:49:30 +0900587 if enable_snat is not None:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000588 ext_gw_info['enable_snat'] = enable_snat
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300589 body = client.create_router(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000590 router_name, external_gateway_info=ext_gw_info,
591 admin_state_up=admin_state_up, **kwargs)
592 router = body['router']
593 cls.routers.append(router)
594 return router
595
596 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300597 def create_router(cls, *args, **kwargs):
598 return cls._create_router_with_client(cls.client, *args, **kwargs)
599
600 @classmethod
601 def create_admin_router(cls, *args, **kwargs):
rajat294495c042017-06-28 15:37:16 +0530602 return cls._create_router_with_client(cls.os_admin.network_client,
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300603 *args, **kwargs)
604
605 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200606 def create_floatingip(cls, external_network_id=None, port=None,
607 client=None, **kwargs):
608 """Creates a floating IP.
609
610 Create a floating IP and schedule it for later deletion.
611 If a client is passed, then it is used for deleting the IP too.
612
613 :param external_network_id: network ID where to create
614 By default this is 'CONF.network.public_network_id'.
615
616 :param port: port to bind floating IP to
617 This is translated to 'port_id=port['id']'
618 By default it is None.
619
620 :param client: network client to be used for creating and cleaning up
621 the floating IP.
622
623 :param **kwargs: additional creation parameters to be forwarded to
624 networking server.
625 """
626
627 client = client or cls.client
628 external_network_id = (external_network_id or
629 cls.external_network_id)
630
631 if port:
Federico Ressi47f6ae42018-09-24 16:19:14 +0200632 port_id = kwargs.setdefault('port_id', port['id'])
633 if port_id != port['id']:
634 message = "Port ID specified twice: {!s} != {!s}".format(
635 port_id, port['id'])
636 raise ValueError(message)
Federico Ressia69dcd52018-07-06 09:45:34 +0200637
638 fip = client.create_floatingip(external_network_id,
639 **kwargs)['floatingip']
640
641 # save client to be used later in cls.delete_floatingip
642 # for final cleanup
643 fip['client'] = client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000644 cls.floating_ips.append(fip)
645 return fip
646
647 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200648 def delete_floatingip(cls, floating_ip, client=None):
649 """Delete floating IP
650
651 :param client: Client to be used
652 If client is not given it will use the client used to create
653 the floating IP, or cls.client if unknown.
654 """
655
656 client = client or floating_ip.get('client') or cls.client
657 client.delete_floatingip(floating_ip['id'])
658
659 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200660 def create_port_forwarding(cls, fip_id, internal_port_id,
661 internal_port, external_port,
662 internal_ip_address=None, protocol="tcp",
663 client=None):
664 """Creates a port forwarding.
665
666 Create a port forwarding and schedule it for later deletion.
667 If a client is passed, then it is used for deleting the PF too.
668
669 :param fip_id: The ID of the floating IP address.
670
671 :param internal_port_id: The ID of the Neutron port associated to
672 the floating IP port forwarding.
673
674 :param internal_port: The TCP/UDP/other protocol port number of the
675 Neutron port fixed IP address associated to the floating ip
676 port forwarding.
677
678 :param external_port: The TCP/UDP/other protocol port number of
679 the port forwarding floating IP address.
680
681 :param internal_ip_address: The fixed IPv4 address of the Neutron
682 port associated to the floating IP port forwarding.
683
684 :param protocol: The IP protocol used in the floating IP port
685 forwarding.
686
687 :param client: network client to be used for creating and cleaning up
688 the floating IP port forwarding.
689 """
690
691 client = client or cls.client
692
693 pf = client.create_port_forwarding(
694 fip_id, internal_port_id, internal_port, external_port,
695 internal_ip_address, protocol)['port_forwarding']
696
697 # save ID of floating IP associated with port forwarding for final
698 # cleanup
699 pf['floatingip_id'] = fip_id
700
701 # save client to be used later in cls.delete_port_forwarding
702 # for final cleanup
703 pf['client'] = client
704 cls.port_forwardings.append(pf)
705 return pf
706
707 @classmethod
708 def delete_port_forwarding(cls, pf, client=None):
709 """Delete port forwarding
710
711 :param client: Client to be used
712 If client is not given it will use the client used to create
713 the port forwarding, or cls.client if unknown.
714 """
715
716 client = client or pf.get('client') or cls.client
717 client.delete_port_forwarding(pf['floatingip_id'], pf['id'])
718
719 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000720 def create_router_interface(cls, router_id, subnet_id):
721 """Wrapper utility that returns a router interface."""
722 interface = cls.client.add_router_interface_with_subnet_id(
723 router_id, subnet_id)
724 return interface
725
726 @classmethod
Bence Romsics46bd3af2019-09-13 10:52:41 +0200727 def add_extra_routes_atomic(cls, *args, **kwargs):
728 return cls.client.add_extra_routes_atomic(*args, **kwargs)
729
730 @classmethod
731 def remove_extra_routes_atomic(cls, *args, **kwargs):
732 return cls.client.remove_extra_routes_atomic(*args, **kwargs)
733
734 @classmethod
Sławek Kapłońskiff294062016-12-04 15:00:54 +0000735 def get_supported_qos_rule_types(cls):
736 body = cls.client.list_qos_rule_types()
737 return [rule_type['type'] for rule_type in body['rule_types']]
738
739 @classmethod
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +0200740 def create_qos_policy(cls, name, description=None, shared=False,
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000741 project_id=None, is_default=False):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000742 """Wrapper utility that returns a test QoS policy."""
743 body = cls.admin_client.create_qos_policy(
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000744 name, description, shared, project_id, is_default)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000745 qos_policy = body['policy']
746 cls.qos_policies.append(qos_policy)
747 return qos_policy
748
749 @classmethod
Sławek Kapłoński153f3452017-03-24 22:04:53 +0000750 def create_qos_bandwidth_limit_rule(cls, policy_id, max_kbps,
751 max_burst_kbps,
Chandan Kumarc125fd12017-11-15 19:41:01 +0530752 direction=const.EGRESS_DIRECTION):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000753 """Wrapper utility that returns a test QoS bandwidth limit rule."""
754 body = cls.admin_client.create_bandwidth_limit_rule(
Sławek Kapłoński153f3452017-03-24 22:04:53 +0000755 policy_id, max_kbps, max_burst_kbps, direction)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000756 qos_rule = body['bandwidth_limit_rule']
757 cls.qos_rules.append(qos_rule)
758 return qos_rule
759
760 @classmethod
Lajos Katona2f904652018-08-23 14:04:56 +0200761 def create_qos_minimum_bandwidth_rule(cls, policy_id, min_kbps,
762 direction=const.EGRESS_DIRECTION):
763 """Wrapper utility that creates and returns a QoS min bw rule."""
764 body = cls.admin_client.create_minimum_bandwidth_rule(
765 policy_id, direction, min_kbps)
766 qos_rule = body['minimum_bandwidth_rule']
767 cls.qos_rules.append(qos_rule)
768 return qos_rule
769
770 @classmethod
Jakub Libosvar83704832017-12-06 16:02:28 +0000771 def delete_router(cls, router, client=None):
772 client = client or cls.client
Aditya Vaja49819a72018-11-26 14:20:10 -0800773 if 'routes' in router:
774 client.remove_router_extra_routes(router['id'])
Jakub Libosvar83704832017-12-06 16:02:28 +0000775 body = client.list_router_interfaces(router['id'])
Chandan Kumarc125fd12017-11-15 19:41:01 +0530776 interfaces = [port for port in body['ports']
777 if port['device_owner'] in const.ROUTER_INTERFACE_OWNERS]
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000778 for i in interfaces:
779 try:
Jakub Libosvar83704832017-12-06 16:02:28 +0000780 client.remove_router_interface_with_subnet_id(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000781 router['id'], i['fixed_ips'][0]['subnet_id'])
782 except lib_exc.NotFound:
783 pass
Jakub Libosvar83704832017-12-06 16:02:28 +0000784 client.delete_router(router['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000785
786 @classmethod
787 def create_address_scope(cls, name, is_admin=False, **kwargs):
788 if is_admin:
789 body = cls.admin_client.create_address_scope(name=name, **kwargs)
790 cls.admin_address_scopes.append(body['address_scope'])
791 else:
792 body = cls.client.create_address_scope(name=name, **kwargs)
793 cls.address_scopes.append(body['address_scope'])
794 return body['address_scope']
795
796 @classmethod
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200797 def create_subnetpool(cls, name, is_admin=False, client=None, **kwargs):
798 if client is None:
799 client = cls.admin_client if is_admin else cls.client
800
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000801 if is_admin:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200802 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000803 cls.admin_subnetpools.append(body['subnetpool'])
804 else:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200805 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000806 cls.subnetpools.append(body['subnetpool'])
807 return body['subnetpool']
808
Chandan Kumarc125fd12017-11-15 19:41:01 +0530809 @classmethod
810 def create_project(cls, name=None, description=None):
811 test_project = name or data_utils.rand_name('test_project_')
812 test_description = description or data_utils.rand_name('desc_')
813 project = cls.identity_admin_client.create_project(
814 name=test_project,
815 description=test_description)['project']
816 cls.projects.append(project)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000817 # Create a project will create a default security group.
Dongcan Ye2de722e2018-07-04 11:01:37 +0000818 sgs_list = cls.admin_client.list_security_groups(
819 tenant_id=project['id'])['security_groups']
Federico Ressi4c590d72018-10-10 14:01:08 +0200820 for security_group in sgs_list:
821 # Make sure delete_security_group method will use
822 # the admin client for this group
823 security_group['client'] = cls.admin_client
824 cls.security_groups.append(security_group)
Chandan Kumarc125fd12017-11-15 19:41:01 +0530825 return project
826
827 @classmethod
Federico Ressi4c590d72018-10-10 14:01:08 +0200828 def create_security_group(cls, name=None, project=None, client=None,
829 **kwargs):
830 if project:
831 client = client or cls.admin_client
832 project_id = kwargs.setdefault('project_id', project['id'])
833 tenant_id = kwargs.setdefault('tenant_id', project['id'])
834 if project_id != project['id'] or tenant_id != project['id']:
835 raise ValueError('Project ID specified multiple times')
836 else:
837 client = client or cls.client
838
839 name = name or data_utils.rand_name(cls.__name__)
840 security_group = client.create_security_group(name=name, **kwargs)[
841 'security_group']
842 security_group['client'] = client
843 cls.security_groups.append(security_group)
844 return security_group
845
846 @classmethod
847 def delete_security_group(cls, security_group, client=None):
848 client = client or security_group.get('client') or cls.client
849 client.delete_security_group(security_group['id'])
850
851 @classmethod
852 def create_security_group_rule(cls, security_group=None, project=None,
853 client=None, ip_version=None, **kwargs):
854 if project:
855 client = client or cls.admin_client
856 project_id = kwargs.setdefault('project_id', project['id'])
857 tenant_id = kwargs.setdefault('tenant_id', project['id'])
858 if project_id != project['id'] or tenant_id != project['id']:
859 raise ValueError('Project ID specified multiple times')
860
861 if 'security_group_id' not in kwargs:
862 security_group = (security_group or
863 cls.get_security_group(client=client))
864
865 if security_group:
866 client = client or security_group.get('client')
867 security_group_id = kwargs.setdefault('security_group_id',
868 security_group['id'])
869 if security_group_id != security_group['id']:
870 raise ValueError('Security group ID specified multiple times.')
871
872 ip_version = ip_version or cls._ip_version
873 default_params = (
874 constants.DEFAULT_SECURITY_GROUP_RULE_PARAMS[ip_version])
875 for key, value in default_params.items():
876 kwargs.setdefault(key, value)
877
878 client = client or cls.client
879 return client.create_security_group_rule(**kwargs)[
880 'security_group_rule']
881
882 @classmethod
883 def get_security_group(cls, name='default', client=None):
884 client = client or cls.client
885 security_groups = client.list_security_groups()['security_groups']
886 for security_group in security_groups:
887 if security_group['name'] == name:
888 return security_group
889 raise ValueError("No such security group named {!r}".format(name))
Chandan Kumarc125fd12017-11-15 19:41:01 +0530890
Federico Ressiab286e42018-06-19 09:52:10 +0200891 @classmethod
892 def create_keypair(cls, client=None, name=None, **kwargs):
893 client = client or cls.os_primary.keypairs_client
894 name = name or data_utils.rand_name('keypair-test')
895 keypair = client.create_keypair(name=name, **kwargs)['keypair']
896
897 # save client for later cleanup
898 keypair['client'] = client
899 cls.keypairs.append(keypair)
900 return keypair
901
902 @classmethod
903 def delete_keypair(cls, keypair, client=None):
904 client = (client or keypair.get('client') or
905 cls.os_primary.keypairs_client)
906 client.delete_keypair(keypair_name=keypair['name'])
907
Federico Ressi82e83e32018-07-03 14:19:55 +0200908 @classmethod
909 def create_trunk(cls, port=None, subports=None, client=None, **kwargs):
910 """Create network trunk
911
912 :param port: dictionary containing parent port ID (port['id'])
913 :param client: client to be used for connecting to networking service
914 :param **kwargs: extra parameters to be forwarded to network service
915
916 :returns: dictionary containing created trunk details
917 """
918 client = client or cls.client
919
920 if port:
921 kwargs['port_id'] = port['id']
922
923 trunk = client.create_trunk(subports=subports, **kwargs)['trunk']
924 # Save client reference for later deletion
925 trunk['client'] = client
926 cls.trunks.append(trunk)
927 return trunk
928
929 @classmethod
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800930 def delete_trunk(cls, trunk, client=None, detach_parent_port=True):
Federico Ressi82e83e32018-07-03 14:19:55 +0200931 """Delete network trunk
932
933 :param trunk: dictionary containing trunk ID (trunk['id'])
934
935 :param client: client to be used for connecting to networking service
936 """
937 client = client or trunk.get('client') or cls.client
938 trunk.update(client.show_trunk(trunk['id'])['trunk'])
939
940 if not trunk['admin_state_up']:
941 # Cannot touch trunk before admin_state_up is True
942 client.update_trunk(trunk['id'], admin_state_up=True)
943 if trunk['sub_ports']:
944 # Removes trunk ports before deleting it
945 cls._try_delete_resource(client.remove_subports, trunk['id'],
946 trunk['sub_ports'])
947
948 # we have to detach the interface from the server before
949 # the trunk can be deleted.
950 parent_port = {'id': trunk['port_id']}
951
952 def is_parent_port_detached():
953 parent_port.update(client.show_port(parent_port['id'])['port'])
954 return not parent_port['device_id']
955
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800956 if detach_parent_port and not is_parent_port_detached():
Federico Ressi82e83e32018-07-03 14:19:55 +0200957 # this could probably happen when trunk is deleted and parent port
958 # has been assigned to a VM that is still running. Here we are
959 # assuming that device_id points to such VM.
960 cls.os_primary.compute.InterfacesClient().delete_interface(
961 parent_port['device_id'], parent_port['id'])
962 utils.wait_until_true(is_parent_port_detached)
963
964 client.delete_trunk(trunk['id'])
965
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200966 @classmethod
967 def create_conntrack_helper(cls, router_id, helper, protocol, port,
968 client=None):
969 """Create a conntrack helper
970
971 Create a conntrack helper and schedule it for later deletion. If a
972 client is passed, then it is used for deleteing the CTH too.
973
974 :param router_id: The ID of the Neutron router associated to the
975 conntrack helper.
976
977 :param helper: The conntrack helper module alias
978
979 :param protocol: The conntrack helper IP protocol used in the conntrack
980 helper.
981
982 :param port: The conntrack helper IP protocol port number for the
983 conntrack helper.
984
985 :param client: network client to be used for creating and cleaning up
986 the conntrack helper.
987 """
988
989 client = client or cls.client
990
991 cth = client.create_conntrack_helper(router_id, helper, protocol,
992 port)['conntrack_helper']
993
994 # save ID of router associated with conntrack helper for final cleanup
995 cth['router_id'] = router_id
996
997 # save client to be used later in cls.delete_conntrack_helper for final
998 # cleanup
999 cth['client'] = client
1000 cls.conntrack_helpers.append(cth)
1001 return cth
1002
1003 @classmethod
1004 def delete_conntrack_helper(cls, cth, client=None):
1005 """Delete conntrack helper
1006
1007 :param client: Client to be used
1008 If client is not given it will use the client used to create the
1009 conntrack helper, or cls.client if unknown.
1010 """
1011
1012 client = client or cth.get('client') or cls.client
1013 client.delete_conntrack_helper(cth['router_id'], cth['id'])
1014
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001015
1016class BaseAdminNetworkTest(BaseNetworkTest):
1017
1018 credentials = ['primary', 'admin']
1019
1020 @classmethod
1021 def setup_clients(cls):
1022 super(BaseAdminNetworkTest, cls).setup_clients()
fumihiko kakumaa216fc12017-07-14 10:43:29 +09001023 cls.admin_client = cls.os_admin.network_client
Jakub Libosvarf5758012017-08-15 13:45:30 +00001024 cls.identity_admin_client = cls.os_admin.projects_client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001025
1026 @classmethod
1027 def create_metering_label(cls, name, description):
1028 """Wrapper utility that returns a test metering label."""
1029 body = cls.admin_client.create_metering_label(
1030 description=description,
1031 name=data_utils.rand_name("metering-label"))
1032 metering_label = body['metering_label']
1033 cls.metering_labels.append(metering_label)
1034 return metering_label
1035
1036 @classmethod
1037 def create_metering_label_rule(cls, remote_ip_prefix, direction,
1038 metering_label_id):
1039 """Wrapper utility that returns a test metering label rule."""
1040 body = cls.admin_client.create_metering_label_rule(
1041 remote_ip_prefix=remote_ip_prefix, direction=direction,
1042 metering_label_id=metering_label_id)
1043 metering_label_rule = body['metering_label_rule']
1044 cls.metering_label_rules.append(metering_label_rule)
1045 return metering_label_rule
1046
1047 @classmethod
Kailun Qineaaf9782018-12-20 04:45:01 +08001048 def create_network_segment_range(cls, name, shared,
1049 project_id, network_type,
1050 physical_network, minimum,
1051 maximum):
1052 """Wrapper utility that returns a test network segment range."""
1053 network_segment_range_args = {'name': name,
1054 'shared': shared,
1055 'project_id': project_id,
1056 'network_type': network_type,
1057 'physical_network': physical_network,
1058 'minimum': minimum,
1059 'maximum': maximum}
1060 body = cls.admin_client.create_network_segment_range(
1061 **network_segment_range_args)
1062 network_segment_range = body['network_segment_range']
1063 cls.network_segment_ranges.append(network_segment_range)
1064 return network_segment_range
1065
1066 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001067 def create_flavor(cls, name, description, service_type):
1068 """Wrapper utility that returns a test flavor."""
1069 body = cls.admin_client.create_flavor(
1070 description=description, service_type=service_type,
1071 name=name)
1072 flavor = body['flavor']
1073 cls.flavors.append(flavor)
1074 return flavor
1075
1076 @classmethod
1077 def create_service_profile(cls, description, metainfo, driver):
1078 """Wrapper utility that returns a test service profile."""
1079 body = cls.admin_client.create_service_profile(
1080 driver=driver, metainfo=metainfo, description=description)
1081 service_profile = body['service_profile']
1082 cls.service_profiles.append(service_profile)
1083 return service_profile
1084
1085 @classmethod
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001086 def create_log(cls, name, description=None,
1087 resource_type='security_group', resource_id=None,
1088 target_id=None, event='ALL', enabled=True):
1089 """Wrapper utility that returns a test log object."""
1090 log_args = {'name': name,
1091 'description': description,
1092 'resource_type': resource_type,
1093 'resource_id': resource_id,
1094 'target_id': target_id,
1095 'event': event,
1096 'enabled': enabled}
1097 body = cls.admin_client.create_log(**log_args)
1098 log_object = body['log']
1099 cls.log_objects.append(log_object)
1100 return log_object
1101
1102 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001103 def get_unused_ip(cls, net_id, ip_version=None):
Gary Kotton011345f2016-06-15 08:04:31 -07001104 """Get an unused ip address in a allocation pool of net"""
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001105 body = cls.admin_client.list_ports(network_id=net_id)
1106 ports = body['ports']
1107 used_ips = []
1108 for port in ports:
1109 used_ips.extend(
1110 [fixed_ip['ip_address'] for fixed_ip in port['fixed_ips']])
1111 body = cls.admin_client.list_subnets(network_id=net_id)
1112 subnets = body['subnets']
1113
1114 for subnet in subnets:
1115 if ip_version and subnet['ip_version'] != ip_version:
1116 continue
1117 cidr = subnet['cidr']
1118 allocation_pools = subnet['allocation_pools']
1119 iterators = []
1120 if allocation_pools:
1121 for allocation_pool in allocation_pools:
1122 iterators.append(netaddr.iter_iprange(
1123 allocation_pool['start'], allocation_pool['end']))
1124 else:
1125 net = netaddr.IPNetwork(cidr)
1126
1127 def _iterip():
1128 for ip in net:
1129 if ip not in (net.network, net.broadcast):
1130 yield ip
1131 iterators.append(iter(_iterip()))
1132
1133 for iterator in iterators:
1134 for ip in iterator:
1135 if str(ip) not in used_ips:
1136 return str(ip)
1137
1138 message = (
1139 "net(%s) has no usable IP address in allocation pools" % net_id)
1140 raise exceptions.InvalidConfiguration(message)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001141
Lajos Katona2f904652018-08-23 14:04:56 +02001142 @classmethod
1143 def create_provider_network(cls, physnet_name, start_segmentation_id,
1144 max_attempts=30):
1145 segmentation_id = start_segmentation_id
Lajos Katona7eb67252019-01-14 12:55:35 +01001146 for attempts in range(max_attempts):
Lajos Katona2f904652018-08-23 14:04:56 +02001147 try:
Lajos Katona7eb67252019-01-14 12:55:35 +01001148 return cls.create_network(
Lajos Katona2f904652018-08-23 14:04:56 +02001149 name=data_utils.rand_name('test_net'),
1150 shared=True,
1151 provider_network_type='vlan',
1152 provider_physical_network=physnet_name,
1153 provider_segmentation_id=segmentation_id)
Lajos Katona2f904652018-08-23 14:04:56 +02001154 except lib_exc.Conflict:
Lajos Katona2f904652018-08-23 14:04:56 +02001155 segmentation_id += 1
1156 if segmentation_id > 4095:
1157 raise lib_exc.TempestException(
1158 "No free segmentation id was found for provider "
1159 "network creation!")
1160 time.sleep(CONF.network.build_interval)
Lajos Katona7eb67252019-01-14 12:55:35 +01001161 LOG.exception("Failed to create provider network after "
1162 "%d attempts", max_attempts)
1163 raise lib_exc.TimeoutException
Lajos Katona2f904652018-08-23 14:04:56 +02001164
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001165
Sławek Kapłońskiff294062016-12-04 15:00:54 +00001166def require_qos_rule_type(rule_type):
1167 def decorator(f):
1168 @functools.wraps(f)
1169 def wrapper(self, *func_args, **func_kwargs):
1170 if rule_type not in self.get_supported_qos_rule_types():
1171 raise self.skipException(
1172 "%s rule type is required." % rule_type)
1173 return f(self, *func_args, **func_kwargs)
1174 return wrapper
1175 return decorator
1176
1177
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001178def _require_sorting(f):
1179 @functools.wraps(f)
1180 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301181 if not tutils.is_extension_enabled("sorting", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001182 self.skipTest('Sorting feature is required')
1183 return f(self, *args, **kwargs)
1184 return inner
1185
1186
1187def _require_pagination(f):
1188 @functools.wraps(f)
1189 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301190 if not tutils.is_extension_enabled("pagination", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001191 self.skipTest('Pagination feature is required')
1192 return f(self, *args, **kwargs)
1193 return inner
1194
1195
1196class BaseSearchCriteriaTest(BaseNetworkTest):
1197
1198 # This should be defined by subclasses to reflect resource name to test
1199 resource = None
1200
Armando Migliaccio57581c62016-07-01 10:13:19 -07001201 field = 'name'
1202
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001203 # NOTE(ihrachys): some names, like those starting with an underscore (_)
1204 # are sorted differently depending on whether the plugin implements native
1205 # sorting support, or not. So we avoid any such cases here, sticking to
1206 # alphanumeric. Also test a case when there are multiple resources with the
1207 # same name
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001208 resource_names = ('test1', 'abc1', 'test10', '123test') + ('test1',)
1209
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001210 force_tenant_isolation = True
1211
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001212 list_kwargs = {}
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001213
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001214 list_as_admin = False
1215
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001216 def assertSameOrder(self, original, actual):
1217 # gracefully handle iterators passed
1218 original = list(original)
1219 actual = list(actual)
1220 self.assertEqual(len(original), len(actual))
1221 for expected, res in zip(original, actual):
Armando Migliaccio57581c62016-07-01 10:13:19 -07001222 self.assertEqual(expected[self.field], res[self.field])
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001223
1224 @utils.classproperty
1225 def plural_name(self):
1226 return '%ss' % self.resource
1227
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001228 @property
1229 def list_client(self):
1230 return self.admin_client if self.list_as_admin else self.client
1231
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001232 def list_method(self, *args, **kwargs):
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001233 method = getattr(self.list_client, 'list_%s' % self.plural_name)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001234 kwargs.update(self.list_kwargs)
1235 return method(*args, **kwargs)
1236
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001237 def get_bare_url(self, url):
1238 base_url = self.client.base_url
zheng.yong74e760a2019-05-22 14:16:14 +08001239 base_url_normalized = utils.normalize_url(base_url)
1240 url_normalized = utils.normalize_url(url)
1241 self.assertTrue(url_normalized.startswith(base_url_normalized))
1242 return url_normalized[len(base_url_normalized):]
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001243
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001244 @classmethod
1245 def _extract_resources(cls, body):
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001246 return body[cls.plural_name]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001247
1248 def _test_list_sorts(self, direction):
1249 sort_args = {
1250 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001251 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001252 }
1253 body = self.list_method(**sort_args)
1254 resources = self._extract_resources(body)
1255 self.assertNotEmpty(
1256 resources, "%s list returned is empty" % self.resource)
Armando Migliaccio57581c62016-07-01 10:13:19 -07001257 retrieved_names = [res[self.field] for res in resources]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001258 expected = sorted(retrieved_names)
1259 if direction == constants.SORT_DIRECTION_DESC:
1260 expected = list(reversed(expected))
1261 self.assertEqual(expected, retrieved_names)
1262
1263 @_require_sorting
1264 def _test_list_sorts_asc(self):
1265 self._test_list_sorts(constants.SORT_DIRECTION_ASC)
1266
1267 @_require_sorting
1268 def _test_list_sorts_desc(self):
1269 self._test_list_sorts(constants.SORT_DIRECTION_DESC)
1270
1271 @_require_pagination
1272 def _test_list_pagination(self):
1273 for limit in range(1, len(self.resource_names) + 1):
1274 pagination_args = {
1275 'limit': limit,
1276 }
1277 body = self.list_method(**pagination_args)
1278 resources = self._extract_resources(body)
1279 self.assertEqual(limit, len(resources))
1280
1281 @_require_pagination
1282 def _test_list_no_pagination_limit_0(self):
1283 pagination_args = {
1284 'limit': 0,
1285 }
1286 body = self.list_method(**pagination_args)
1287 resources = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001288 self.assertGreaterEqual(len(resources), len(self.resource_names))
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001289
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001290 def _test_list_pagination_iteratively(self, lister):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001291 # first, collect all resources for later comparison
1292 sort_args = {
1293 'sort_dir': constants.SORT_DIRECTION_ASC,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001294 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001295 }
1296 body = self.list_method(**sort_args)
1297 expected_resources = self._extract_resources(body)
1298 self.assertNotEmpty(expected_resources)
1299
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001300 resources = lister(
1301 len(expected_resources), sort_args
1302 )
1303
1304 # finally, compare that the list retrieved in one go is identical to
1305 # the one containing pagination results
1306 self.assertSameOrder(expected_resources, resources)
1307
1308 def _list_all_with_marker(self, niterations, sort_args):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001309 # paginate resources one by one, using last fetched resource as a
1310 # marker
1311 resources = []
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001312 for i in range(niterations):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001313 pagination_args = sort_args.copy()
1314 pagination_args['limit'] = 1
1315 if resources:
1316 pagination_args['marker'] = resources[-1]['id']
1317 body = self.list_method(**pagination_args)
1318 resources_ = self._extract_resources(body)
1319 self.assertEqual(1, len(resources_))
1320 resources.extend(resources_)
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001321 return resources
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001322
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001323 @_require_pagination
1324 @_require_sorting
1325 def _test_list_pagination_with_marker(self):
1326 self._test_list_pagination_iteratively(self._list_all_with_marker)
1327
1328 def _list_all_with_hrefs(self, niterations, sort_args):
1329 # paginate resources one by one, using next href links
1330 resources = []
1331 prev_links = {}
1332
1333 for i in range(niterations):
1334 if prev_links:
1335 uri = self.get_bare_url(prev_links['next'])
1336 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001337 sort_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001338 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001339 self.plural_name, limit=1, **sort_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001340 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001341 self.plural_name, uri
1342 )
1343 resources_ = self._extract_resources(body)
1344 self.assertEqual(1, len(resources_))
1345 resources.extend(resources_)
1346
1347 # The last element is empty and does not contain 'next' link
1348 uri = self.get_bare_url(prev_links['next'])
1349 prev_links, body = self.client.get_uri_with_links(
1350 self.plural_name, uri
1351 )
1352 self.assertNotIn('next', prev_links)
1353
1354 # Now walk backwards and compare results
1355 resources2 = []
1356 for i in range(niterations):
1357 uri = self.get_bare_url(prev_links['previous'])
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 resources2.extend(resources_)
1364
1365 self.assertSameOrder(resources, reversed(resources2))
1366
1367 return resources
1368
1369 @_require_pagination
1370 @_require_sorting
1371 def _test_list_pagination_with_href_links(self):
1372 self._test_list_pagination_iteratively(self._list_all_with_hrefs)
1373
1374 @_require_pagination
1375 @_require_sorting
1376 def _test_list_pagination_page_reverse_with_href_links(
1377 self, direction=constants.SORT_DIRECTION_ASC):
1378 pagination_args = {
1379 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001380 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001381 }
1382 body = self.list_method(**pagination_args)
1383 expected_resources = self._extract_resources(body)
1384
1385 page_size = 2
1386 pagination_args['limit'] = page_size
1387
1388 prev_links = {}
1389 resources = []
1390 num_resources = len(expected_resources)
1391 niterations = int(math.ceil(float(num_resources) / page_size))
1392 for i in range(niterations):
1393 if prev_links:
1394 uri = self.get_bare_url(prev_links['previous'])
1395 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001396 pagination_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001397 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001398 self.plural_name, page_reverse=True, **pagination_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001399 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001400 self.plural_name, uri
1401 )
1402 resources_ = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001403 self.assertGreaterEqual(page_size, len(resources_))
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001404 resources.extend(reversed(resources_))
1405
1406 self.assertSameOrder(expected_resources, reversed(resources))
1407
1408 @_require_pagination
1409 @_require_sorting
1410 def _test_list_pagination_page_reverse_asc(self):
1411 self._test_list_pagination_page_reverse(
1412 direction=constants.SORT_DIRECTION_ASC)
1413
1414 @_require_pagination
1415 @_require_sorting
1416 def _test_list_pagination_page_reverse_desc(self):
1417 self._test_list_pagination_page_reverse(
1418 direction=constants.SORT_DIRECTION_DESC)
1419
1420 def _test_list_pagination_page_reverse(self, direction):
1421 pagination_args = {
1422 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001423 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001424 'limit': 3,
1425 }
1426 body = self.list_method(**pagination_args)
1427 expected_resources = self._extract_resources(body)
1428
1429 pagination_args['limit'] -= 1
1430 pagination_args['marker'] = expected_resources[-1]['id']
1431 pagination_args['page_reverse'] = True
1432 body = self.list_method(**pagination_args)
1433
1434 self.assertSameOrder(
1435 # the last entry is not included in 2nd result when used as a
1436 # marker
1437 expected_resources[:-1],
1438 self._extract_resources(body))
Victor Morales1be97b42016-09-05 08:50:06 -05001439
Hongbin Lu54f55922018-07-12 19:05:39 +00001440 @tutils.requires_ext(extension="filter-validation", service="network")
1441 def _test_list_validation_filters(
1442 self, validation_args, filter_is_valid=True):
1443 if not filter_is_valid:
1444 self.assertRaises(lib_exc.BadRequest, self.list_method,
1445 **validation_args)
1446 else:
1447 body = self.list_method(**validation_args)
1448 resources = self._extract_resources(body)
1449 for resource in resources:
1450 self.assertIn(resource['name'], self.resource_names)