blob: 216ccfcb7881918eb1edc712dc24d4428168b8f2 [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
Sławek Kapłoński153f3452017-03-24 22:04:53 +0000766 def create_qos_bandwidth_limit_rule(cls, policy_id, max_kbps,
767 max_burst_kbps,
Chandan Kumarc125fd12017-11-15 19:41:01 +0530768 direction=const.EGRESS_DIRECTION):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000769 """Wrapper utility that returns a test QoS bandwidth limit rule."""
770 body = cls.admin_client.create_bandwidth_limit_rule(
Sławek Kapłoński153f3452017-03-24 22:04:53 +0000771 policy_id, max_kbps, max_burst_kbps, direction)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000772 qos_rule = body['bandwidth_limit_rule']
773 cls.qos_rules.append(qos_rule)
774 return qos_rule
775
776 @classmethod
Lajos Katona2f904652018-08-23 14:04:56 +0200777 def create_qos_minimum_bandwidth_rule(cls, policy_id, min_kbps,
778 direction=const.EGRESS_DIRECTION):
779 """Wrapper utility that creates and returns a QoS min bw rule."""
780 body = cls.admin_client.create_minimum_bandwidth_rule(
781 policy_id, direction, min_kbps)
782 qos_rule = body['minimum_bandwidth_rule']
783 cls.qos_rules.append(qos_rule)
784 return qos_rule
785
786 @classmethod
Jakub Libosvar83704832017-12-06 16:02:28 +0000787 def delete_router(cls, router, client=None):
788 client = client or cls.client
Aditya Vaja49819a72018-11-26 14:20:10 -0800789 if 'routes' in router:
790 client.remove_router_extra_routes(router['id'])
Jakub Libosvar83704832017-12-06 16:02:28 +0000791 body = client.list_router_interfaces(router['id'])
Chandan Kumarc125fd12017-11-15 19:41:01 +0530792 interfaces = [port for port in body['ports']
793 if port['device_owner'] in const.ROUTER_INTERFACE_OWNERS]
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000794 for i in interfaces:
795 try:
Jakub Libosvar83704832017-12-06 16:02:28 +0000796 client.remove_router_interface_with_subnet_id(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000797 router['id'], i['fixed_ips'][0]['subnet_id'])
798 except lib_exc.NotFound:
799 pass
Jakub Libosvar83704832017-12-06 16:02:28 +0000800 client.delete_router(router['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000801
802 @classmethod
803 def create_address_scope(cls, name, is_admin=False, **kwargs):
804 if is_admin:
805 body = cls.admin_client.create_address_scope(name=name, **kwargs)
806 cls.admin_address_scopes.append(body['address_scope'])
807 else:
808 body = cls.client.create_address_scope(name=name, **kwargs)
809 cls.address_scopes.append(body['address_scope'])
810 return body['address_scope']
811
812 @classmethod
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200813 def create_subnetpool(cls, name, is_admin=False, client=None, **kwargs):
814 if client is None:
815 client = cls.admin_client if is_admin else cls.client
816
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000817 if is_admin:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200818 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000819 cls.admin_subnetpools.append(body['subnetpool'])
820 else:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200821 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000822 cls.subnetpools.append(body['subnetpool'])
823 return body['subnetpool']
824
Chandan Kumarc125fd12017-11-15 19:41:01 +0530825 @classmethod
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600826 def create_address_group(cls, name, is_admin=False, **kwargs):
827 if is_admin:
828 body = cls.admin_client.create_address_group(name=name, **kwargs)
829 cls.admin_address_groups.append(body['address_group'])
830 else:
831 body = cls.client.create_address_group(name=name, **kwargs)
832 cls.address_groups.append(body['address_group'])
833 return body['address_group']
834
835 @classmethod
Chandan Kumarc125fd12017-11-15 19:41:01 +0530836 def create_project(cls, name=None, description=None):
837 test_project = name or data_utils.rand_name('test_project_')
838 test_description = description or data_utils.rand_name('desc_')
839 project = cls.identity_admin_client.create_project(
840 name=test_project,
841 description=test_description)['project']
842 cls.projects.append(project)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000843 # Create a project will create a default security group.
Dongcan Ye2de722e2018-07-04 11:01:37 +0000844 sgs_list = cls.admin_client.list_security_groups(
845 tenant_id=project['id'])['security_groups']
Federico Ressi4c590d72018-10-10 14:01:08 +0200846 for security_group in sgs_list:
847 # Make sure delete_security_group method will use
848 # the admin client for this group
849 security_group['client'] = cls.admin_client
850 cls.security_groups.append(security_group)
Chandan Kumarc125fd12017-11-15 19:41:01 +0530851 return project
852
853 @classmethod
Federico Ressi4c590d72018-10-10 14:01:08 +0200854 def create_security_group(cls, name=None, project=None, client=None,
855 **kwargs):
856 if project:
857 client = client or cls.admin_client
858 project_id = kwargs.setdefault('project_id', project['id'])
859 tenant_id = kwargs.setdefault('tenant_id', project['id'])
860 if project_id != project['id'] or tenant_id != project['id']:
861 raise ValueError('Project ID specified multiple times')
862 else:
863 client = client or cls.client
864
865 name = name or data_utils.rand_name(cls.__name__)
866 security_group = client.create_security_group(name=name, **kwargs)[
867 'security_group']
868 security_group['client'] = client
869 cls.security_groups.append(security_group)
870 return security_group
871
872 @classmethod
873 def delete_security_group(cls, security_group, client=None):
874 client = client or security_group.get('client') or cls.client
875 client.delete_security_group(security_group['id'])
876
877 @classmethod
878 def create_security_group_rule(cls, security_group=None, project=None,
879 client=None, ip_version=None, **kwargs):
880 if project:
881 client = client or cls.admin_client
882 project_id = kwargs.setdefault('project_id', project['id'])
883 tenant_id = kwargs.setdefault('tenant_id', project['id'])
884 if project_id != project['id'] or tenant_id != project['id']:
885 raise ValueError('Project ID specified multiple times')
886
887 if 'security_group_id' not in kwargs:
888 security_group = (security_group or
889 cls.get_security_group(client=client))
890
891 if security_group:
892 client = client or security_group.get('client')
893 security_group_id = kwargs.setdefault('security_group_id',
894 security_group['id'])
895 if security_group_id != security_group['id']:
896 raise ValueError('Security group ID specified multiple times.')
897
898 ip_version = ip_version or cls._ip_version
899 default_params = (
900 constants.DEFAULT_SECURITY_GROUP_RULE_PARAMS[ip_version])
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600901 if ('remote_address_group_id' in kwargs and 'remote_ip_prefix' in
902 default_params):
903 default_params.pop('remote_ip_prefix')
Federico Ressi4c590d72018-10-10 14:01:08 +0200904 for key, value in default_params.items():
905 kwargs.setdefault(key, value)
906
907 client = client or cls.client
908 return client.create_security_group_rule(**kwargs)[
909 'security_group_rule']
910
911 @classmethod
912 def get_security_group(cls, name='default', client=None):
913 client = client or cls.client
914 security_groups = client.list_security_groups()['security_groups']
915 for security_group in security_groups:
916 if security_group['name'] == name:
917 return security_group
918 raise ValueError("No such security group named {!r}".format(name))
Chandan Kumarc125fd12017-11-15 19:41:01 +0530919
Federico Ressiab286e42018-06-19 09:52:10 +0200920 @classmethod
921 def create_keypair(cls, client=None, name=None, **kwargs):
922 client = client or cls.os_primary.keypairs_client
923 name = name or data_utils.rand_name('keypair-test')
924 keypair = client.create_keypair(name=name, **kwargs)['keypair']
925
926 # save client for later cleanup
927 keypair['client'] = client
928 cls.keypairs.append(keypair)
929 return keypair
930
931 @classmethod
932 def delete_keypair(cls, keypair, client=None):
933 client = (client or keypair.get('client') or
934 cls.os_primary.keypairs_client)
935 client.delete_keypair(keypair_name=keypair['name'])
936
Federico Ressi82e83e32018-07-03 14:19:55 +0200937 @classmethod
938 def create_trunk(cls, port=None, subports=None, client=None, **kwargs):
939 """Create network trunk
940
941 :param port: dictionary containing parent port ID (port['id'])
942 :param client: client to be used for connecting to networking service
943 :param **kwargs: extra parameters to be forwarded to network service
944
945 :returns: dictionary containing created trunk details
946 """
947 client = client or cls.client
948
949 if port:
950 kwargs['port_id'] = port['id']
951
952 trunk = client.create_trunk(subports=subports, **kwargs)['trunk']
953 # Save client reference for later deletion
954 trunk['client'] = client
955 cls.trunks.append(trunk)
956 return trunk
957
958 @classmethod
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800959 def delete_trunk(cls, trunk, client=None, detach_parent_port=True):
Federico Ressi82e83e32018-07-03 14:19:55 +0200960 """Delete network trunk
961
962 :param trunk: dictionary containing trunk ID (trunk['id'])
963
964 :param client: client to be used for connecting to networking service
965 """
966 client = client or trunk.get('client') or cls.client
967 trunk.update(client.show_trunk(trunk['id'])['trunk'])
968
969 if not trunk['admin_state_up']:
970 # Cannot touch trunk before admin_state_up is True
971 client.update_trunk(trunk['id'], admin_state_up=True)
972 if trunk['sub_ports']:
973 # Removes trunk ports before deleting it
974 cls._try_delete_resource(client.remove_subports, trunk['id'],
975 trunk['sub_ports'])
976
977 # we have to detach the interface from the server before
978 # the trunk can be deleted.
979 parent_port = {'id': trunk['port_id']}
980
981 def is_parent_port_detached():
982 parent_port.update(client.show_port(parent_port['id'])['port'])
983 return not parent_port['device_id']
984
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800985 if detach_parent_port and not is_parent_port_detached():
Federico Ressi82e83e32018-07-03 14:19:55 +0200986 # this could probably happen when trunk is deleted and parent port
987 # has been assigned to a VM that is still running. Here we are
988 # assuming that device_id points to such VM.
989 cls.os_primary.compute.InterfacesClient().delete_interface(
990 parent_port['device_id'], parent_port['id'])
991 utils.wait_until_true(is_parent_port_detached)
992
993 client.delete_trunk(trunk['id'])
994
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200995 @classmethod
996 def create_conntrack_helper(cls, router_id, helper, protocol, port,
997 client=None):
998 """Create a conntrack helper
999
1000 Create a conntrack helper and schedule it for later deletion. If a
1001 client is passed, then it is used for deleteing the CTH too.
1002
1003 :param router_id: The ID of the Neutron router associated to the
1004 conntrack helper.
1005
1006 :param helper: The conntrack helper module alias
1007
1008 :param protocol: The conntrack helper IP protocol used in the conntrack
1009 helper.
1010
1011 :param port: The conntrack helper IP protocol port number for the
1012 conntrack helper.
1013
1014 :param client: network client to be used for creating and cleaning up
1015 the conntrack helper.
1016 """
1017
1018 client = client or cls.client
1019
1020 cth = client.create_conntrack_helper(router_id, helper, protocol,
1021 port)['conntrack_helper']
1022
1023 # save ID of router associated with conntrack helper for final cleanup
1024 cth['router_id'] = router_id
1025
1026 # save client to be used later in cls.delete_conntrack_helper for final
1027 # cleanup
1028 cth['client'] = client
1029 cls.conntrack_helpers.append(cth)
1030 return cth
1031
1032 @classmethod
1033 def delete_conntrack_helper(cls, cth, client=None):
1034 """Delete conntrack helper
1035
1036 :param client: Client to be used
1037 If client is not given it will use the client used to create the
1038 conntrack helper, or cls.client if unknown.
1039 """
1040
1041 client = client or cth.get('client') or cls.client
1042 client.delete_conntrack_helper(cth['router_id'], cth['id'])
1043
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001044
1045class BaseAdminNetworkTest(BaseNetworkTest):
1046
1047 credentials = ['primary', 'admin']
1048
1049 @classmethod
1050 def setup_clients(cls):
1051 super(BaseAdminNetworkTest, cls).setup_clients()
fumihiko kakumaa216fc12017-07-14 10:43:29 +09001052 cls.admin_client = cls.os_admin.network_client
Jakub Libosvarf5758012017-08-15 13:45:30 +00001053 cls.identity_admin_client = cls.os_admin.projects_client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001054
1055 @classmethod
1056 def create_metering_label(cls, name, description):
1057 """Wrapper utility that returns a test metering label."""
1058 body = cls.admin_client.create_metering_label(
1059 description=description,
1060 name=data_utils.rand_name("metering-label"))
1061 metering_label = body['metering_label']
1062 cls.metering_labels.append(metering_label)
1063 return metering_label
1064
1065 @classmethod
1066 def create_metering_label_rule(cls, remote_ip_prefix, direction,
1067 metering_label_id):
1068 """Wrapper utility that returns a test metering label rule."""
1069 body = cls.admin_client.create_metering_label_rule(
1070 remote_ip_prefix=remote_ip_prefix, direction=direction,
1071 metering_label_id=metering_label_id)
1072 metering_label_rule = body['metering_label_rule']
1073 cls.metering_label_rules.append(metering_label_rule)
1074 return metering_label_rule
1075
1076 @classmethod
Kailun Qineaaf9782018-12-20 04:45:01 +08001077 def create_network_segment_range(cls, name, shared,
1078 project_id, network_type,
1079 physical_network, minimum,
1080 maximum):
1081 """Wrapper utility that returns a test network segment range."""
1082 network_segment_range_args = {'name': name,
1083 'shared': shared,
1084 'project_id': project_id,
1085 'network_type': network_type,
1086 'physical_network': physical_network,
1087 'minimum': minimum,
1088 'maximum': maximum}
1089 body = cls.admin_client.create_network_segment_range(
1090 **network_segment_range_args)
1091 network_segment_range = body['network_segment_range']
1092 cls.network_segment_ranges.append(network_segment_range)
1093 return network_segment_range
1094
1095 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001096 def create_flavor(cls, name, description, service_type):
1097 """Wrapper utility that returns a test flavor."""
1098 body = cls.admin_client.create_flavor(
1099 description=description, service_type=service_type,
1100 name=name)
1101 flavor = body['flavor']
1102 cls.flavors.append(flavor)
1103 return flavor
1104
1105 @classmethod
1106 def create_service_profile(cls, description, metainfo, driver):
1107 """Wrapper utility that returns a test service profile."""
1108 body = cls.admin_client.create_service_profile(
1109 driver=driver, metainfo=metainfo, description=description)
1110 service_profile = body['service_profile']
1111 cls.service_profiles.append(service_profile)
1112 return service_profile
1113
1114 @classmethod
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001115 def create_log(cls, name, description=None,
1116 resource_type='security_group', resource_id=None,
1117 target_id=None, event='ALL', enabled=True):
1118 """Wrapper utility that returns a test log object."""
1119 log_args = {'name': name,
1120 'description': description,
1121 'resource_type': resource_type,
1122 'resource_id': resource_id,
1123 'target_id': target_id,
1124 'event': event,
1125 'enabled': enabled}
1126 body = cls.admin_client.create_log(**log_args)
1127 log_object = body['log']
1128 cls.log_objects.append(log_object)
1129 return log_object
1130
1131 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001132 def get_unused_ip(cls, net_id, ip_version=None):
Gary Kotton011345f2016-06-15 08:04:31 -07001133 """Get an unused ip address in a allocation pool of net"""
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001134 body = cls.admin_client.list_ports(network_id=net_id)
1135 ports = body['ports']
1136 used_ips = []
1137 for port in ports:
1138 used_ips.extend(
1139 [fixed_ip['ip_address'] for fixed_ip in port['fixed_ips']])
1140 body = cls.admin_client.list_subnets(network_id=net_id)
1141 subnets = body['subnets']
1142
1143 for subnet in subnets:
1144 if ip_version and subnet['ip_version'] != ip_version:
1145 continue
1146 cidr = subnet['cidr']
1147 allocation_pools = subnet['allocation_pools']
1148 iterators = []
1149 if allocation_pools:
1150 for allocation_pool in allocation_pools:
1151 iterators.append(netaddr.iter_iprange(
1152 allocation_pool['start'], allocation_pool['end']))
1153 else:
1154 net = netaddr.IPNetwork(cidr)
1155
1156 def _iterip():
1157 for ip in net:
1158 if ip not in (net.network, net.broadcast):
1159 yield ip
1160 iterators.append(iter(_iterip()))
1161
1162 for iterator in iterators:
1163 for ip in iterator:
1164 if str(ip) not in used_ips:
1165 return str(ip)
1166
1167 message = (
1168 "net(%s) has no usable IP address in allocation pools" % net_id)
1169 raise exceptions.InvalidConfiguration(message)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001170
Lajos Katona2f904652018-08-23 14:04:56 +02001171 @classmethod
1172 def create_provider_network(cls, physnet_name, start_segmentation_id,
1173 max_attempts=30):
1174 segmentation_id = start_segmentation_id
Lajos Katona7eb67252019-01-14 12:55:35 +01001175 for attempts in range(max_attempts):
Lajos Katona2f904652018-08-23 14:04:56 +02001176 try:
Lajos Katona7eb67252019-01-14 12:55:35 +01001177 return cls.create_network(
Lajos Katona2f904652018-08-23 14:04:56 +02001178 name=data_utils.rand_name('test_net'),
1179 shared=True,
1180 provider_network_type='vlan',
1181 provider_physical_network=physnet_name,
1182 provider_segmentation_id=segmentation_id)
Lajos Katona2f904652018-08-23 14:04:56 +02001183 except lib_exc.Conflict:
Lajos Katona2f904652018-08-23 14:04:56 +02001184 segmentation_id += 1
1185 if segmentation_id > 4095:
1186 raise lib_exc.TempestException(
1187 "No free segmentation id was found for provider "
1188 "network creation!")
1189 time.sleep(CONF.network.build_interval)
Lajos Katona7eb67252019-01-14 12:55:35 +01001190 LOG.exception("Failed to create provider network after "
1191 "%d attempts", max_attempts)
1192 raise lib_exc.TimeoutException
Lajos Katona2f904652018-08-23 14:04:56 +02001193
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001194
Sławek Kapłońskiff294062016-12-04 15:00:54 +00001195def require_qos_rule_type(rule_type):
1196 def decorator(f):
1197 @functools.wraps(f)
1198 def wrapper(self, *func_args, **func_kwargs):
1199 if rule_type not in self.get_supported_qos_rule_types():
1200 raise self.skipException(
1201 "%s rule type is required." % rule_type)
1202 return f(self, *func_args, **func_kwargs)
1203 return wrapper
1204 return decorator
1205
1206
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001207def _require_sorting(f):
1208 @functools.wraps(f)
1209 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301210 if not tutils.is_extension_enabled("sorting", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001211 self.skipTest('Sorting feature is required')
1212 return f(self, *args, **kwargs)
1213 return inner
1214
1215
1216def _require_pagination(f):
1217 @functools.wraps(f)
1218 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301219 if not tutils.is_extension_enabled("pagination", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001220 self.skipTest('Pagination feature is required')
1221 return f(self, *args, **kwargs)
1222 return inner
1223
1224
1225class BaseSearchCriteriaTest(BaseNetworkTest):
1226
1227 # This should be defined by subclasses to reflect resource name to test
1228 resource = None
1229
Armando Migliaccio57581c62016-07-01 10:13:19 -07001230 field = 'name'
1231
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001232 # NOTE(ihrachys): some names, like those starting with an underscore (_)
1233 # are sorted differently depending on whether the plugin implements native
1234 # sorting support, or not. So we avoid any such cases here, sticking to
1235 # alphanumeric. Also test a case when there are multiple resources with the
1236 # same name
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001237 resource_names = ('test1', 'abc1', 'test10', '123test') + ('test1',)
1238
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001239 force_tenant_isolation = True
1240
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001241 list_kwargs = {}
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001242
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001243 list_as_admin = False
1244
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001245 def assertSameOrder(self, original, actual):
1246 # gracefully handle iterators passed
1247 original = list(original)
1248 actual = list(actual)
1249 self.assertEqual(len(original), len(actual))
1250 for expected, res in zip(original, actual):
Armando Migliaccio57581c62016-07-01 10:13:19 -07001251 self.assertEqual(expected[self.field], res[self.field])
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001252
1253 @utils.classproperty
1254 def plural_name(self):
1255 return '%ss' % self.resource
1256
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001257 @property
1258 def list_client(self):
1259 return self.admin_client if self.list_as_admin else self.client
1260
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001261 def list_method(self, *args, **kwargs):
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001262 method = getattr(self.list_client, 'list_%s' % self.plural_name)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001263 kwargs.update(self.list_kwargs)
1264 return method(*args, **kwargs)
1265
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001266 def get_bare_url(self, url):
1267 base_url = self.client.base_url
zheng.yong74e760a2019-05-22 14:16:14 +08001268 base_url_normalized = utils.normalize_url(base_url)
1269 url_normalized = utils.normalize_url(url)
1270 self.assertTrue(url_normalized.startswith(base_url_normalized))
1271 return url_normalized[len(base_url_normalized):]
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001272
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001273 @classmethod
1274 def _extract_resources(cls, body):
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001275 return body[cls.plural_name]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001276
1277 def _test_list_sorts(self, direction):
1278 sort_args = {
1279 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001280 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001281 }
1282 body = self.list_method(**sort_args)
1283 resources = self._extract_resources(body)
1284 self.assertNotEmpty(
1285 resources, "%s list returned is empty" % self.resource)
Armando Migliaccio57581c62016-07-01 10:13:19 -07001286 retrieved_names = [res[self.field] for res in resources]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001287 expected = sorted(retrieved_names)
1288 if direction == constants.SORT_DIRECTION_DESC:
1289 expected = list(reversed(expected))
1290 self.assertEqual(expected, retrieved_names)
1291
1292 @_require_sorting
1293 def _test_list_sorts_asc(self):
1294 self._test_list_sorts(constants.SORT_DIRECTION_ASC)
1295
1296 @_require_sorting
1297 def _test_list_sorts_desc(self):
1298 self._test_list_sorts(constants.SORT_DIRECTION_DESC)
1299
1300 @_require_pagination
1301 def _test_list_pagination(self):
1302 for limit in range(1, len(self.resource_names) + 1):
1303 pagination_args = {
1304 'limit': limit,
1305 }
1306 body = self.list_method(**pagination_args)
1307 resources = self._extract_resources(body)
1308 self.assertEqual(limit, len(resources))
1309
1310 @_require_pagination
1311 def _test_list_no_pagination_limit_0(self):
1312 pagination_args = {
1313 'limit': 0,
1314 }
1315 body = self.list_method(**pagination_args)
1316 resources = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001317 self.assertGreaterEqual(len(resources), len(self.resource_names))
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001318
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001319 def _test_list_pagination_iteratively(self, lister):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001320 # first, collect all resources for later comparison
1321 sort_args = {
1322 'sort_dir': constants.SORT_DIRECTION_ASC,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001323 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001324 }
1325 body = self.list_method(**sort_args)
1326 expected_resources = self._extract_resources(body)
1327 self.assertNotEmpty(expected_resources)
1328
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001329 resources = lister(
1330 len(expected_resources), sort_args
1331 )
1332
1333 # finally, compare that the list retrieved in one go is identical to
1334 # the one containing pagination results
1335 self.assertSameOrder(expected_resources, resources)
1336
1337 def _list_all_with_marker(self, niterations, sort_args):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001338 # paginate resources one by one, using last fetched resource as a
1339 # marker
1340 resources = []
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001341 for i in range(niterations):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001342 pagination_args = sort_args.copy()
1343 pagination_args['limit'] = 1
1344 if resources:
1345 pagination_args['marker'] = resources[-1]['id']
1346 body = self.list_method(**pagination_args)
1347 resources_ = self._extract_resources(body)
1348 self.assertEqual(1, len(resources_))
1349 resources.extend(resources_)
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001350 return resources
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001351
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001352 @_require_pagination
1353 @_require_sorting
1354 def _test_list_pagination_with_marker(self):
1355 self._test_list_pagination_iteratively(self._list_all_with_marker)
1356
1357 def _list_all_with_hrefs(self, niterations, sort_args):
1358 # paginate resources one by one, using next href links
1359 resources = []
1360 prev_links = {}
1361
1362 for i in range(niterations):
1363 if prev_links:
1364 uri = self.get_bare_url(prev_links['next'])
1365 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001366 sort_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001367 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001368 self.plural_name, limit=1, **sort_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001369 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001370 self.plural_name, uri
1371 )
1372 resources_ = self._extract_resources(body)
1373 self.assertEqual(1, len(resources_))
1374 resources.extend(resources_)
1375
1376 # The last element is empty and does not contain 'next' link
1377 uri = self.get_bare_url(prev_links['next'])
1378 prev_links, body = self.client.get_uri_with_links(
1379 self.plural_name, uri
1380 )
1381 self.assertNotIn('next', prev_links)
1382
1383 # Now walk backwards and compare results
1384 resources2 = []
1385 for i in range(niterations):
1386 uri = self.get_bare_url(prev_links['previous'])
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001387 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001388 self.plural_name, uri
1389 )
1390 resources_ = self._extract_resources(body)
1391 self.assertEqual(1, len(resources_))
1392 resources2.extend(resources_)
1393
1394 self.assertSameOrder(resources, reversed(resources2))
1395
1396 return resources
1397
1398 @_require_pagination
1399 @_require_sorting
1400 def _test_list_pagination_with_href_links(self):
1401 self._test_list_pagination_iteratively(self._list_all_with_hrefs)
1402
1403 @_require_pagination
1404 @_require_sorting
1405 def _test_list_pagination_page_reverse_with_href_links(
1406 self, direction=constants.SORT_DIRECTION_ASC):
1407 pagination_args = {
1408 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001409 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001410 }
1411 body = self.list_method(**pagination_args)
1412 expected_resources = self._extract_resources(body)
1413
1414 page_size = 2
1415 pagination_args['limit'] = page_size
1416
1417 prev_links = {}
1418 resources = []
1419 num_resources = len(expected_resources)
1420 niterations = int(math.ceil(float(num_resources) / page_size))
1421 for i in range(niterations):
1422 if prev_links:
1423 uri = self.get_bare_url(prev_links['previous'])
1424 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001425 pagination_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001426 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001427 self.plural_name, page_reverse=True, **pagination_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001428 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001429 self.plural_name, uri
1430 )
1431 resources_ = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001432 self.assertGreaterEqual(page_size, len(resources_))
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001433 resources.extend(reversed(resources_))
1434
1435 self.assertSameOrder(expected_resources, reversed(resources))
1436
1437 @_require_pagination
1438 @_require_sorting
1439 def _test_list_pagination_page_reverse_asc(self):
1440 self._test_list_pagination_page_reverse(
1441 direction=constants.SORT_DIRECTION_ASC)
1442
1443 @_require_pagination
1444 @_require_sorting
1445 def _test_list_pagination_page_reverse_desc(self):
1446 self._test_list_pagination_page_reverse(
1447 direction=constants.SORT_DIRECTION_DESC)
1448
1449 def _test_list_pagination_page_reverse(self, direction):
1450 pagination_args = {
1451 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001452 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001453 'limit': 3,
1454 }
1455 body = self.list_method(**pagination_args)
1456 expected_resources = self._extract_resources(body)
1457
1458 pagination_args['limit'] -= 1
1459 pagination_args['marker'] = expected_resources[-1]['id']
1460 pagination_args['page_reverse'] = True
1461 body = self.list_method(**pagination_args)
1462
1463 self.assertSameOrder(
1464 # the last entry is not included in 2nd result when used as a
1465 # marker
1466 expected_resources[:-1],
1467 self._extract_resources(body))
Victor Morales1be97b42016-09-05 08:50:06 -05001468
Hongbin Lu54f55922018-07-12 19:05:39 +00001469 @tutils.requires_ext(extension="filter-validation", service="network")
1470 def _test_list_validation_filters(
1471 self, validation_args, filter_is_valid=True):
1472 if not filter_is_valid:
1473 self.assertRaises(lib_exc.BadRequest, self.list_method,
1474 **validation_args)
1475 else:
1476 body = self.list_method(**validation_args)
1477 resources = self._extract_resources(body)
1478 for resource in resources:
1479 self.assertIn(resource['name'], self.resource_names)