blob: 4e4cae25f7901babbe4b371194932b689f53e2d1 [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 Saienko8a1eb472021-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 = []
Nurmatov Mamatisa3f2bbb52021-10-20 14:33:54 +0300126 cls.local_ips = []
127 cls.local_ip_associations = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000128 cls.metering_labels = []
129 cls.service_profiles = []
130 cls.flavors = []
131 cls.metering_label_rules = []
132 cls.qos_rules = []
133 cls.qos_policies = []
134 cls.ethertype = "IPv" + str(cls._ip_version)
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600135 cls.address_groups = []
136 cls.admin_address_groups = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000137 cls.address_scopes = []
138 cls.admin_address_scopes = []
139 cls.subnetpools = []
140 cls.admin_subnetpools = []
Itzik Brownbac51dc2016-10-31 12:25:04 +0000141 cls.security_groups = []
Dongcan Ye2de722e2018-07-04 11:01:37 +0000142 cls.admin_security_groups = []
Chandan Kumarc125fd12017-11-15 19:41:01 +0530143 cls.projects = []
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700144 cls.log_objects = []
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200145 cls.reserved_subnet_cidrs = set()
Federico Ressiab286e42018-06-19 09:52:10 +0200146 cls.keypairs = []
Federico Ressi82e83e32018-07-03 14:19:55 +0200147 cls.trunks = []
Kailun Qineaaf9782018-12-20 04:45:01 +0800148 cls.network_segment_ranges = []
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200149 cls.conntrack_helpers = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000150
151 @classmethod
yangjianfeng23e40c22020-11-22 08:42:18 +0000152 def reserve_external_subnet_cidrs(cls):
153 client = cls.os_admin.network_client
154 ext_nets = client.list_networks(
155 **{"router:external": True})['networks']
156 for ext_net in ext_nets:
157 ext_subnets = client.list_subnets(
158 network_id=ext_net['id'])['subnets']
159 for ext_subnet in ext_subnets:
160 cls.reserve_subnet_cidr(ext_subnet['cidr'])
161
162 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000163 def resource_cleanup(cls):
164 if CONF.service_available.neutron:
Federico Ressi82e83e32018-07-03 14:19:55 +0200165 # Clean up trunks
166 for trunk in cls.trunks:
167 cls._try_delete_resource(cls.delete_trunk, trunk)
168
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200169 # Clean up port forwardings
170 for pf in cls.port_forwardings:
171 cls._try_delete_resource(cls.delete_port_forwarding, pf)
172
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000173 # Clean up floating IPs
174 for floating_ip in cls.floating_ips:
Federico Ressia69dcd52018-07-06 09:45:34 +0200175 cls._try_delete_resource(cls.delete_floatingip, floating_ip)
176
Nurmatov Mamatisa3f2bbb52021-10-20 14:33:54 +0300177 # Clean up Local IP Associations
178 for association in cls.local_ip_associations:
179 cls._try_delete_resource(cls.delete_local_ip_association,
180 association)
181 # Clean up Local IPs
182 for local_ip in cls.local_ips:
183 cls._try_delete_resource(cls.delete_local_ip,
184 local_ip)
185
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200186 # Clean up conntrack helpers
187 for cth in cls.conntrack_helpers:
188 cls._try_delete_resource(cls.delete_conntrack_helper, cth)
189
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000190 # Clean up routers
191 for router in cls.routers:
192 cls._try_delete_resource(cls.delete_router,
193 router)
194 # Clean up metering label rules
195 for metering_label_rule in cls.metering_label_rules:
196 cls._try_delete_resource(
197 cls.admin_client.delete_metering_label_rule,
198 metering_label_rule['id'])
199 # Clean up metering labels
200 for metering_label in cls.metering_labels:
201 cls._try_delete_resource(
202 cls.admin_client.delete_metering_label,
203 metering_label['id'])
204 # Clean up flavors
205 for flavor in cls.flavors:
206 cls._try_delete_resource(
207 cls.admin_client.delete_flavor,
208 flavor['id'])
209 # Clean up service profiles
210 for service_profile in cls.service_profiles:
211 cls._try_delete_resource(
212 cls.admin_client.delete_service_profile,
213 service_profile['id'])
214 # Clean up ports
215 for port in cls.ports:
216 cls._try_delete_resource(cls.client.delete_port,
217 port['id'])
218 # Clean up subnets
219 for subnet in cls.subnets:
220 cls._try_delete_resource(cls.client.delete_subnet,
221 subnet['id'])
Kevin Bentonba3651c2017-09-01 17:13:01 -0700222 # Clean up admin subnets
223 for subnet in cls.admin_subnets:
224 cls._try_delete_resource(cls.admin_client.delete_subnet,
225 subnet['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000226 # Clean up networks
227 for network in cls.networks:
Federico Ressi61b564e2018-07-06 08:10:31 +0200228 cls._try_delete_resource(cls.delete_network, network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000229
Miguel Lavalle124378b2016-09-21 16:41:47 -0500230 # Clean up admin networks
231 for network in cls.admin_networks:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000232 cls._try_delete_resource(cls.admin_client.delete_network,
233 network['id'])
234
Itzik Brownbac51dc2016-10-31 12:25:04 +0000235 # Clean up security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200236 for security_group in cls.security_groups:
237 cls._try_delete_resource(cls.delete_security_group,
238 security_group)
Itzik Brownbac51dc2016-10-31 12:25:04 +0000239
Dongcan Ye2de722e2018-07-04 11:01:37 +0000240 # Clean up admin security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200241 for security_group in cls.admin_security_groups:
242 cls._try_delete_resource(cls.delete_security_group,
243 security_group,
244 client=cls.admin_client)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000245
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000246 for subnetpool in cls.subnetpools:
247 cls._try_delete_resource(cls.client.delete_subnetpool,
248 subnetpool['id'])
249
250 for subnetpool in cls.admin_subnetpools:
251 cls._try_delete_resource(cls.admin_client.delete_subnetpool,
252 subnetpool['id'])
253
254 for address_scope in cls.address_scopes:
255 cls._try_delete_resource(cls.client.delete_address_scope,
256 address_scope['id'])
257
258 for address_scope in cls.admin_address_scopes:
259 cls._try_delete_resource(
260 cls.admin_client.delete_address_scope,
261 address_scope['id'])
262
Chandan Kumarc125fd12017-11-15 19:41:01 +0530263 for project in cls.projects:
264 cls._try_delete_resource(
265 cls.identity_admin_client.delete_project,
266 project['id'])
267
Sławek Kapłońskie100c4d2017-08-23 21:18:34 +0000268 # Clean up QoS rules
269 for qos_rule in cls.qos_rules:
270 cls._try_delete_resource(cls.admin_client.delete_qos_rule,
271 qos_rule['id'])
272 # Clean up QoS policies
273 # as all networks and ports are already removed, QoS policies
274 # shouldn't be "in use"
275 for qos_policy in cls.qos_policies:
276 cls._try_delete_resource(cls.admin_client.delete_qos_policy,
277 qos_policy['id'])
278
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700279 # Clean up log_objects
280 for log_object in cls.log_objects:
281 cls._try_delete_resource(cls.admin_client.delete_log,
282 log_object['id'])
283
Federico Ressiab286e42018-06-19 09:52:10 +0200284 for keypair in cls.keypairs:
285 cls._try_delete_resource(cls.delete_keypair, keypair)
286
Kailun Qineaaf9782018-12-20 04:45:01 +0800287 # Clean up network_segment_ranges
288 for network_segment_range in cls.network_segment_ranges:
289 cls._try_delete_resource(
290 cls.admin_client.delete_network_segment_range,
291 network_segment_range['id'])
292
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000293 super(BaseNetworkTest, cls).resource_cleanup()
294
295 @classmethod
296 def _try_delete_resource(cls, delete_callable, *args, **kwargs):
297 """Cleanup resources in case of test-failure
298
299 Some resources are explicitly deleted by the test.
300 If the test failed to delete a resource, this method will execute
301 the appropriate delete methods. Otherwise, the method ignores NotFound
302 exceptions thrown for resources that were correctly deleted by the
303 test.
304
305 :param delete_callable: delete method
306 :param args: arguments for delete method
307 :param kwargs: keyword arguments for delete method
308 """
309 try:
310 delete_callable(*args, **kwargs)
311 # if resource is not found, this means it was deleted in the test
312 except lib_exc.NotFound:
313 pass
314
315 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200316 def create_network(cls, network_name=None, client=None, external=None,
317 shared=None, provider_network_type=None,
318 provider_physical_network=None,
319 provider_segmentation_id=None, **kwargs):
320 """Create a network.
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000321
Federico Ressi61b564e2018-07-06 08:10:31 +0200322 When client is not provider and admin_client is attribute is not None
323 (for example when using BaseAdminNetworkTest base class) and using any
324 of the convenience parameters (external, shared, provider_network_type,
325 provider_physical_network and provider_segmentation_id) it silently
326 uses admin_client. If the network is not shared then it uses the same
327 project_id as regular client.
328
329 :param network_name: Human-readable name of the network
330
331 :param client: client to be used for connecting to network service
332
333 :param external: indicates whether the network has an external routing
334 facility that's not managed by the networking service.
335
336 :param shared: indicates whether this resource is shared across all
337 projects. By default, only administrative users can change this value.
338 If True and admin_client attribute is not None, then the network is
339 created under administrative project.
340
341 :param provider_network_type: the type of physical network that this
342 network should be mapped to. For example, 'flat', 'vlan', 'vxlan', or
343 'gre'. Valid values depend on a networking back-end.
344
345 :param provider_physical_network: the physical network where this
346 network should be implemented. The Networking API v2.0 does not provide
347 a way to list available physical networks. For example, the Open
348 vSwitch plug-in configuration file defines a symbolic name that maps to
349 specific bridges on each compute host.
350
351 :param provider_segmentation_id: The ID of the isolated segment on the
352 physical network. The network_type attribute defines the segmentation
353 model. For example, if the network_type value is 'vlan', this ID is a
354 vlan identifier. If the network_type value is 'gre', this ID is a gre
355 key.
356
357 :param **kwargs: extra parameters to be forwarded to network service
358 """
359
360 name = (network_name or kwargs.pop('name', None) or
361 data_utils.rand_name('test-network-'))
362
363 # translate convenience parameters
364 admin_client_required = False
365 if provider_network_type:
366 admin_client_required = True
367 kwargs['provider:network_type'] = provider_network_type
368 if provider_physical_network:
369 admin_client_required = True
370 kwargs['provider:physical_network'] = provider_physical_network
371 if provider_segmentation_id:
372 admin_client_required = True
373 kwargs['provider:segmentation_id'] = provider_segmentation_id
374 if external is not None:
375 admin_client_required = True
376 kwargs['router:external'] = bool(external)
377 if shared is not None:
378 admin_client_required = True
379 kwargs['shared'] = bool(shared)
380
381 if not client:
382 if admin_client_required and cls.admin_client:
383 # For convenience silently switch to admin client
384 client = cls.admin_client
385 if not shared:
386 # Keep this network visible from current project
387 project_id = (kwargs.get('project_id') or
388 kwargs.get('tenant_id') or
389 cls.client.tenant_id)
390 kwargs.update(project_id=project_id, tenant_id=project_id)
391 else:
392 # Use default client
393 client = cls.client
394
395 network = client.create_network(name=name, **kwargs)['network']
396 network['client'] = client
397 cls.networks.append(network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000398 return network
399
400 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200401 def delete_network(cls, network, client=None):
402 client = client or network.get('client') or cls.client
403 client.delete_network(network['id'])
404
405 @classmethod
406 def create_shared_network(cls, network_name=None, **kwargs):
407 return cls.create_network(name=network_name, shared=True, **kwargs)
Miguel Lavalle124378b2016-09-21 16:41:47 -0500408
409 @classmethod
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200410 def create_subnet(cls, network, gateway='', cidr=None, mask_bits=None,
Federico Ressi98f20ec2018-05-11 06:09:49 +0200411 ip_version=None, client=None, reserve_cidr=True,
412 **kwargs):
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200413 """Wrapper utility that returns a test subnet.
414
415 Convenient wrapper for client.create_subnet method. It reserves and
416 allocates CIDRs to avoid creating overlapping subnets.
417
418 :param network: network where to create the subnet
419 network['id'] must contain the ID of the network
420
421 :param gateway: gateway IP address
422 It can be a str or a netaddr.IPAddress
423 If gateway is not given, then it will use default address for
424 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 +0200425 if gateway is given as None then no gateway will be assigned
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200426
427 :param cidr: CIDR of the subnet to create
428 It can be either None, a str or a netaddr.IPNetwork instance
429
430 :param mask_bits: CIDR prefix length
431 It can be either None or a numeric value.
432 If cidr parameter is given then mask_bits is used to determinate a
433 sequence of valid CIDR to use as generated.
434 Please see netaddr.IPNetwork.subnet method documentation[1]
435
436 :param ip_version: ip version of generated subnet CIDRs
437 It can be None, IP_VERSION_4 or IP_VERSION_6
438 It has to match given either given CIDR and gateway
439
440 :param ip_version: numeric value (either IP_VERSION_4 or IP_VERSION_6)
441 this value must match CIDR and gateway IP versions if any of them is
442 given
443
444 :param client: client to be used to connect to network service
445
Federico Ressi98f20ec2018-05-11 06:09:49 +0200446 :param reserve_cidr: if True then it reserves assigned CIDR to avoid
447 using the same CIDR for further subnets in the scope of the same
448 test case class
449
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200450 :param **kwargs: optional parameters to be forwarded to wrapped method
451
452 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
453 """
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000454
455 # allow tests to use admin client
456 if not client:
457 client = cls.client
458
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200459 if gateway:
460 gateway_ip = netaddr.IPAddress(gateway)
461 if ip_version:
462 if ip_version != gateway_ip.version:
463 raise ValueError(
464 "Gateway IP version doesn't match IP version")
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000465 else:
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200466 ip_version = gateway_ip.version
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200467 else:
468 ip_version = ip_version or cls._ip_version
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200469
470 for subnet_cidr in cls.get_subnet_cidrs(
471 ip_version=ip_version, cidr=cidr, mask_bits=mask_bits):
Federico Ressi98f20ec2018-05-11 06:09:49 +0200472 if gateway is not None:
473 kwargs['gateway_ip'] = str(gateway or (subnet_cidr.ip + 1))
Slawek Kaplonski21f53422018-11-02 16:02:09 +0100474 else:
475 kwargs['gateway_ip'] = None
Federico Ressi98f20ec2018-05-11 06:09:49 +0200476 try:
477 body = client.create_subnet(
478 network_id=network['id'],
479 cidr=str(subnet_cidr),
480 ip_version=subnet_cidr.version,
481 **kwargs)
482 break
483 except lib_exc.BadRequest as e:
484 if 'overlaps with another subnet' not in str(e):
485 raise
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000486 else:
487 message = 'Available CIDR for subnet creation could not be found'
488 raise ValueError(message)
489 subnet = body['subnet']
Kevin Bentonba3651c2017-09-01 17:13:01 -0700490 if client is cls.client:
491 cls.subnets.append(subnet)
492 else:
493 cls.admin_subnets.append(subnet)
Federico Ressi98f20ec2018-05-11 06:09:49 +0200494 if reserve_cidr:
495 cls.reserve_subnet_cidr(subnet_cidr)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000496 return subnet
497
498 @classmethod
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200499 def reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
500 """Reserve given subnet CIDR making sure it is not used by create_subnet
501
502 :param addr: the CIDR address to be reserved
503 It can be a str or netaddr.IPNetwork instance
504
505 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
506 parameters
507 """
508
509 if not cls.try_reserve_subnet_cidr(addr, **ipnetwork_kwargs):
Bernard Cafarellic3bec862020-09-10 13:59:49 +0200510 raise ValueError('Subnet CIDR already reserved: {0!r}'.format(
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200511 addr))
512
513 @classmethod
514 def try_reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
515 """Reserve given subnet CIDR if it hasn't been reserved before
516
517 :param addr: the CIDR address to be reserved
518 It can be a str or netaddr.IPNetwork instance
519
520 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
521 parameters
522
523 :return: True if it wasn't reserved before, False elsewhere.
524 """
525
526 subnet_cidr = netaddr.IPNetwork(addr, **ipnetwork_kwargs)
527 if subnet_cidr in cls.reserved_subnet_cidrs:
528 return False
529 else:
530 cls.reserved_subnet_cidrs.add(subnet_cidr)
531 return True
532
533 @classmethod
534 def get_subnet_cidrs(
535 cls, cidr=None, mask_bits=None, ip_version=None):
536 """Iterate over a sequence of unused subnet CIDR for IP version
537
538 :param cidr: CIDR of the subnet to create
539 It can be either None, a str or a netaddr.IPNetwork instance
540
541 :param mask_bits: CIDR prefix length
542 It can be either None or a numeric value.
543 If cidr parameter is given then mask_bits is used to determinate a
544 sequence of valid CIDR to use as generated.
545 Please see netaddr.IPNetwork.subnet method documentation[1]
546
547 :param ip_version: ip version of generated subnet CIDRs
548 It can be None, IP_VERSION_4 or IP_VERSION_6
549 It has to match given CIDR if given
550
551 :return: iterator over reserved CIDRs of type netaddr.IPNetwork
552
553 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
554 """
555
556 if cidr:
557 # Generate subnet CIDRs starting from given CIDR
558 # checking it is of requested IP version
559 cidr = netaddr.IPNetwork(cidr, version=ip_version)
560 else:
561 # Generate subnet CIDRs starting from configured values
562 ip_version = ip_version or cls._ip_version
563 if ip_version == const.IP_VERSION_4:
564 mask_bits = mask_bits or config.safe_get_config_value(
565 'network', 'project_network_mask_bits')
566 cidr = netaddr.IPNetwork(config.safe_get_config_value(
567 'network', 'project_network_cidr'))
568 elif ip_version == const.IP_VERSION_6:
569 mask_bits = config.safe_get_config_value(
570 'network', 'project_network_v6_mask_bits')
571 cidr = netaddr.IPNetwork(config.safe_get_config_value(
572 'network', 'project_network_v6_cidr'))
573 else:
574 raise ValueError('Invalid IP version: {!r}'.format(ip_version))
575
576 if mask_bits:
577 subnet_cidrs = cidr.subnet(mask_bits)
578 else:
579 subnet_cidrs = iter([cidr])
580
581 for subnet_cidr in subnet_cidrs:
582 if subnet_cidr not in cls.reserved_subnet_cidrs:
583 yield subnet_cidr
584
585 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000586 def create_port(cls, network, **kwargs):
587 """Wrapper utility that returns a test port."""
Edan Davidd75e48e2018-01-03 02:49:52 -0500588 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
589 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
Glenn Van de Water5d9b1402020-09-16 15:14:14 +0200590 if CONF.network.port_profile and 'binding:profile' not in kwargs:
591 kwargs['binding:profile'] = CONF.network.port_profile
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000592 body = cls.client.create_port(network_id=network['id'],
593 **kwargs)
594 port = body['port']
595 cls.ports.append(port)
596 return port
597
598 @classmethod
599 def update_port(cls, port, **kwargs):
600 """Wrapper utility that updates a test port."""
601 body = cls.client.update_port(port['id'],
602 **kwargs)
603 return body['port']
604
605 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300606 def _create_router_with_client(
607 cls, client, router_name=None, admin_state_up=False,
608 external_network_id=None, enable_snat=None, **kwargs
609 ):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000610 ext_gw_info = {}
611 if external_network_id:
612 ext_gw_info['network_id'] = external_network_id
YAMAMOTO Takashi9bd4f972017-06-20 12:49:30 +0900613 if enable_snat is not None:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000614 ext_gw_info['enable_snat'] = enable_snat
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300615 body = client.create_router(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000616 router_name, external_gateway_info=ext_gw_info,
617 admin_state_up=admin_state_up, **kwargs)
618 router = body['router']
619 cls.routers.append(router)
620 return router
621
622 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300623 def create_router(cls, *args, **kwargs):
624 return cls._create_router_with_client(cls.client, *args, **kwargs)
625
626 @classmethod
627 def create_admin_router(cls, *args, **kwargs):
rajat294495c042017-06-28 15:37:16 +0530628 return cls._create_router_with_client(cls.os_admin.network_client,
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300629 *args, **kwargs)
630
631 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200632 def create_floatingip(cls, external_network_id=None, port=None,
633 client=None, **kwargs):
634 """Creates a floating IP.
635
636 Create a floating IP and schedule it for later deletion.
637 If a client is passed, then it is used for deleting the IP too.
638
639 :param external_network_id: network ID where to create
640 By default this is 'CONF.network.public_network_id'.
641
642 :param port: port to bind floating IP to
643 This is translated to 'port_id=port['id']'
644 By default it is None.
645
646 :param client: network client to be used for creating and cleaning up
647 the floating IP.
648
649 :param **kwargs: additional creation parameters to be forwarded to
650 networking server.
651 """
652
653 client = client or cls.client
654 external_network_id = (external_network_id or
655 cls.external_network_id)
656
657 if port:
Federico Ressi47f6ae42018-09-24 16:19:14 +0200658 port_id = kwargs.setdefault('port_id', port['id'])
659 if port_id != port['id']:
660 message = "Port ID specified twice: {!s} != {!s}".format(
661 port_id, port['id'])
662 raise ValueError(message)
Federico Ressia69dcd52018-07-06 09:45:34 +0200663
664 fip = client.create_floatingip(external_network_id,
665 **kwargs)['floatingip']
666
667 # save client to be used later in cls.delete_floatingip
668 # for final cleanup
669 fip['client'] = client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000670 cls.floating_ips.append(fip)
671 return fip
672
673 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200674 def delete_floatingip(cls, floating_ip, client=None):
675 """Delete floating IP
676
677 :param client: Client to be used
678 If client is not given it will use the client used to create
679 the floating IP, or cls.client if unknown.
680 """
681
682 client = client or floating_ip.get('client') or cls.client
683 client.delete_floatingip(floating_ip['id'])
684
685 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200686 def create_port_forwarding(cls, fip_id, internal_port_id,
687 internal_port, external_port,
688 internal_ip_address=None, protocol="tcp",
689 client=None):
690 """Creates a port forwarding.
691
692 Create a port forwarding and schedule it for later deletion.
693 If a client is passed, then it is used for deleting the PF too.
694
695 :param fip_id: The ID of the floating IP address.
696
697 :param internal_port_id: The ID of the Neutron port associated to
698 the floating IP port forwarding.
699
700 :param internal_port: The TCP/UDP/other protocol port number of the
701 Neutron port fixed IP address associated to the floating ip
702 port forwarding.
703
704 :param external_port: The TCP/UDP/other protocol port number of
705 the port forwarding floating IP address.
706
707 :param internal_ip_address: The fixed IPv4 address of the Neutron
708 port associated to the floating IP port forwarding.
709
710 :param protocol: The IP protocol used in the floating IP port
711 forwarding.
712
713 :param client: network client to be used for creating and cleaning up
714 the floating IP port forwarding.
715 """
716
717 client = client or cls.client
718
719 pf = client.create_port_forwarding(
720 fip_id, internal_port_id, internal_port, external_port,
721 internal_ip_address, protocol)['port_forwarding']
722
723 # save ID of floating IP associated with port forwarding for final
724 # cleanup
725 pf['floatingip_id'] = fip_id
726
727 # save client to be used later in cls.delete_port_forwarding
728 # for final cleanup
729 pf['client'] = client
730 cls.port_forwardings.append(pf)
731 return pf
732
733 @classmethod
Flavio Fernandesa1952c62020-10-02 06:39:08 -0400734 def update_port_forwarding(cls, fip_id, pf_id, client=None, **kwargs):
735 """Wrapper utility for update_port_forwarding."""
736 client = client or cls.client
737 return client.update_port_forwarding(fip_id, pf_id, **kwargs)
738
739 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200740 def delete_port_forwarding(cls, pf, client=None):
741 """Delete port forwarding
742
743 :param client: Client to be used
744 If client is not given it will use the client used to create
745 the port forwarding, or cls.client if unknown.
746 """
747
748 client = client or pf.get('client') or cls.client
749 client.delete_port_forwarding(pf['floatingip_id'], pf['id'])
750
Nurmatov Mamatisa3f2bbb52021-10-20 14:33:54 +0300751 def create_local_ip(cls, network_id=None,
752 client=None, **kwargs):
753 """Creates a Local IP.
754
755 Create a Local IP and schedule it for later deletion.
756 If a client is passed, then it is used for deleting the IP too.
757
758 :param network_id: network ID where to create
759 By default this is 'CONF.network.public_network_id'.
760
761 :param client: network client to be used for creating and cleaning up
762 the Local IP.
763
764 :param **kwargs: additional creation parameters to be forwarded to
765 networking server.
766 """
767
768 client = client or cls.client
769 network_id = (network_id or
770 cls.external_network_id)
771
772 local_ip = client.create_local_ip(network_id,
773 **kwargs)['local_ip']
774
775 # save client to be used later in cls.delete_local_ip
776 # for final cleanup
777 local_ip['client'] = client
778 cls.local_ips.append(local_ip)
779 return local_ip
780
781 @classmethod
782 def delete_local_ip(cls, local_ip, client=None):
783 """Delete Local IP
784
785 :param client: Client to be used
786 If client is not given it will use the client used to create
787 the Local IP, or cls.client if unknown.
788 """
789
790 client = client or local_ip.get('client') or cls.client
791 client.delete_local_ip(local_ip['id'])
792
793 @classmethod
794 def create_local_ip_association(cls, local_ip_id, fixed_port_id,
795 fixed_ip_address=None, client=None):
796 """Creates a Local IP association.
797
798 Create a Local IP Association and schedule it for later deletion.
799 If a client is passed, then it is used for deleting the association
800 too.
801
802 :param local_ip_id: The ID of the Local IP.
803
804 :param fixed_port_id: The ID of the Neutron port
805 to be associated with the Local IP
806
807 :param fixed_ip_address: The fixed IPv4 address of the Neutron
808 port to be associated with the Local IP
809
810 :param client: network client to be used for creating and cleaning up
811 the Local IP Association.
812 """
813
814 client = client or cls.client
815
816 association = client.create_local_ip_association(
817 local_ip_id, fixed_port_id,
818 fixed_ip_address)['port_association']
819
820 # save ID of Local IP for final cleanup
821 association['local_ip_id'] = local_ip_id
822
823 # save client to be used later in
824 # cls.delete_local_ip_association for final cleanup
825 association['client'] = client
826 cls.local_ip_associations.append(association)
827 return association
828
829 @classmethod
830 def delete_local_ip_association(cls, association, client=None):
831
832 """Delete Local IP Association
833
834 :param client: Client to be used
835 If client is not given it will use the client used to create
836 the local IP association, or cls.client if unknown.
837 """
838
839 client = client or association.get('client') or cls.client
840 client.delete_local_ip_association(association['local_ip_id'],
841 association['fixed_port_id'])
842
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200843 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000844 def create_router_interface(cls, router_id, subnet_id):
845 """Wrapper utility that returns a router interface."""
846 interface = cls.client.add_router_interface_with_subnet_id(
847 router_id, subnet_id)
848 return interface
849
850 @classmethod
Bence Romsics46bd3af2019-09-13 10:52:41 +0200851 def add_extra_routes_atomic(cls, *args, **kwargs):
852 return cls.client.add_extra_routes_atomic(*args, **kwargs)
853
854 @classmethod
855 def remove_extra_routes_atomic(cls, *args, **kwargs):
856 return cls.client.remove_extra_routes_atomic(*args, **kwargs)
857
858 @classmethod
Sławek Kapłońskiff294062016-12-04 15:00:54 +0000859 def get_supported_qos_rule_types(cls):
860 body = cls.client.list_qos_rule_types()
861 return [rule_type['type'] for rule_type in body['rule_types']]
862
863 @classmethod
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +0200864 def create_qos_policy(cls, name, description=None, shared=False,
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000865 project_id=None, is_default=False):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000866 """Wrapper utility that returns a test QoS policy."""
867 body = cls.admin_client.create_qos_policy(
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000868 name, description, shared, project_id, is_default)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000869 qos_policy = body['policy']
870 cls.qos_policies.append(qos_policy)
871 return qos_policy
872
873 @classmethod
elajkatdbb0b482021-05-04 17:20:07 +0200874 def create_qos_dscp_marking_rule(cls, policy_id, dscp_mark):
875 """Wrapper utility that creates and returns a QoS dscp rule."""
876 body = cls.admin_client.create_dscp_marking_rule(
877 policy_id, dscp_mark)
878 qos_rule = body['dscp_marking_rule']
879 cls.qos_rules.append(qos_rule)
880 return qos_rule
881
882 @classmethod
Jakub Libosvar83704832017-12-06 16:02:28 +0000883 def delete_router(cls, router, client=None):
884 client = client or cls.client
Aditya Vaja49819a72018-11-26 14:20:10 -0800885 if 'routes' in router:
886 client.remove_router_extra_routes(router['id'])
Jakub Libosvar83704832017-12-06 16:02:28 +0000887 body = client.list_router_interfaces(router['id'])
Chandan Kumarc125fd12017-11-15 19:41:01 +0530888 interfaces = [port for port in body['ports']
889 if port['device_owner'] in const.ROUTER_INTERFACE_OWNERS]
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000890 for i in interfaces:
891 try:
Jakub Libosvar83704832017-12-06 16:02:28 +0000892 client.remove_router_interface_with_subnet_id(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000893 router['id'], i['fixed_ips'][0]['subnet_id'])
894 except lib_exc.NotFound:
895 pass
Jakub Libosvar83704832017-12-06 16:02:28 +0000896 client.delete_router(router['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000897
898 @classmethod
899 def create_address_scope(cls, name, is_admin=False, **kwargs):
900 if is_admin:
901 body = cls.admin_client.create_address_scope(name=name, **kwargs)
902 cls.admin_address_scopes.append(body['address_scope'])
903 else:
904 body = cls.client.create_address_scope(name=name, **kwargs)
905 cls.address_scopes.append(body['address_scope'])
906 return body['address_scope']
907
908 @classmethod
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200909 def create_subnetpool(cls, name, is_admin=False, client=None, **kwargs):
910 if client is None:
911 client = cls.admin_client if is_admin else cls.client
912
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000913 if is_admin:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200914 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000915 cls.admin_subnetpools.append(body['subnetpool'])
916 else:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200917 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000918 cls.subnetpools.append(body['subnetpool'])
919 return body['subnetpool']
920
Chandan Kumarc125fd12017-11-15 19:41:01 +0530921 @classmethod
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600922 def create_address_group(cls, name, is_admin=False, **kwargs):
923 if is_admin:
924 body = cls.admin_client.create_address_group(name=name, **kwargs)
925 cls.admin_address_groups.append(body['address_group'])
926 else:
927 body = cls.client.create_address_group(name=name, **kwargs)
928 cls.address_groups.append(body['address_group'])
929 return body['address_group']
930
931 @classmethod
Chandan Kumarc125fd12017-11-15 19:41:01 +0530932 def create_project(cls, name=None, description=None):
933 test_project = name or data_utils.rand_name('test_project_')
934 test_description = description or data_utils.rand_name('desc_')
935 project = cls.identity_admin_client.create_project(
936 name=test_project,
937 description=test_description)['project']
938 cls.projects.append(project)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000939 # Create a project will create a default security group.
Dongcan Ye2de722e2018-07-04 11:01:37 +0000940 sgs_list = cls.admin_client.list_security_groups(
941 tenant_id=project['id'])['security_groups']
Federico Ressi4c590d72018-10-10 14:01:08 +0200942 for security_group in sgs_list:
943 # Make sure delete_security_group method will use
944 # the admin client for this group
945 security_group['client'] = cls.admin_client
946 cls.security_groups.append(security_group)
Chandan Kumarc125fd12017-11-15 19:41:01 +0530947 return project
948
949 @classmethod
Federico Ressi4c590d72018-10-10 14:01:08 +0200950 def create_security_group(cls, name=None, project=None, client=None,
951 **kwargs):
952 if project:
953 client = client or cls.admin_client
954 project_id = kwargs.setdefault('project_id', project['id'])
955 tenant_id = kwargs.setdefault('tenant_id', project['id'])
956 if project_id != project['id'] or tenant_id != project['id']:
957 raise ValueError('Project ID specified multiple times')
958 else:
959 client = client or cls.client
960
961 name = name or data_utils.rand_name(cls.__name__)
962 security_group = client.create_security_group(name=name, **kwargs)[
963 'security_group']
964 security_group['client'] = client
965 cls.security_groups.append(security_group)
966 return security_group
967
968 @classmethod
969 def delete_security_group(cls, security_group, client=None):
970 client = client or security_group.get('client') or cls.client
971 client.delete_security_group(security_group['id'])
972
973 @classmethod
974 def create_security_group_rule(cls, security_group=None, project=None,
975 client=None, ip_version=None, **kwargs):
976 if project:
977 client = client or cls.admin_client
978 project_id = kwargs.setdefault('project_id', project['id'])
979 tenant_id = kwargs.setdefault('tenant_id', project['id'])
980 if project_id != project['id'] or tenant_id != project['id']:
981 raise ValueError('Project ID specified multiple times')
982
983 if 'security_group_id' not in kwargs:
984 security_group = (security_group or
985 cls.get_security_group(client=client))
986
987 if security_group:
988 client = client or security_group.get('client')
989 security_group_id = kwargs.setdefault('security_group_id',
990 security_group['id'])
991 if security_group_id != security_group['id']:
992 raise ValueError('Security group ID specified multiple times.')
993
994 ip_version = ip_version or cls._ip_version
995 default_params = (
996 constants.DEFAULT_SECURITY_GROUP_RULE_PARAMS[ip_version])
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600997 if ('remote_address_group_id' in kwargs and 'remote_ip_prefix' in
998 default_params):
999 default_params.pop('remote_ip_prefix')
Federico Ressi4c590d72018-10-10 14:01:08 +02001000 for key, value in default_params.items():
1001 kwargs.setdefault(key, value)
1002
1003 client = client or cls.client
1004 return client.create_security_group_rule(**kwargs)[
1005 'security_group_rule']
1006
1007 @classmethod
1008 def get_security_group(cls, name='default', client=None):
1009 client = client or cls.client
1010 security_groups = client.list_security_groups()['security_groups']
1011 for security_group in security_groups:
1012 if security_group['name'] == name:
1013 return security_group
1014 raise ValueError("No such security group named {!r}".format(name))
Chandan Kumarc125fd12017-11-15 19:41:01 +05301015
Federico Ressiab286e42018-06-19 09:52:10 +02001016 @classmethod
1017 def create_keypair(cls, client=None, name=None, **kwargs):
1018 client = client or cls.os_primary.keypairs_client
1019 name = name or data_utils.rand_name('keypair-test')
1020 keypair = client.create_keypair(name=name, **kwargs)['keypair']
1021
1022 # save client for later cleanup
1023 keypair['client'] = client
1024 cls.keypairs.append(keypair)
1025 return keypair
1026
1027 @classmethod
1028 def delete_keypair(cls, keypair, client=None):
1029 client = (client or keypair.get('client') or
1030 cls.os_primary.keypairs_client)
1031 client.delete_keypair(keypair_name=keypair['name'])
1032
Federico Ressi82e83e32018-07-03 14:19:55 +02001033 @classmethod
1034 def create_trunk(cls, port=None, subports=None, client=None, **kwargs):
1035 """Create network trunk
1036
1037 :param port: dictionary containing parent port ID (port['id'])
1038 :param client: client to be used for connecting to networking service
1039 :param **kwargs: extra parameters to be forwarded to network service
1040
1041 :returns: dictionary containing created trunk details
1042 """
1043 client = client or cls.client
1044
1045 if port:
1046 kwargs['port_id'] = port['id']
1047
1048 trunk = client.create_trunk(subports=subports, **kwargs)['trunk']
1049 # Save client reference for later deletion
1050 trunk['client'] = client
1051 cls.trunks.append(trunk)
1052 return trunk
1053
1054 @classmethod
Huifeng Le1c9f40b2018-11-07 01:14:21 +08001055 def delete_trunk(cls, trunk, client=None, detach_parent_port=True):
Federico Ressi82e83e32018-07-03 14:19:55 +02001056 """Delete network trunk
1057
1058 :param trunk: dictionary containing trunk ID (trunk['id'])
1059
1060 :param client: client to be used for connecting to networking service
1061 """
1062 client = client or trunk.get('client') or cls.client
1063 trunk.update(client.show_trunk(trunk['id'])['trunk'])
1064
1065 if not trunk['admin_state_up']:
1066 # Cannot touch trunk before admin_state_up is True
1067 client.update_trunk(trunk['id'], admin_state_up=True)
1068 if trunk['sub_ports']:
1069 # Removes trunk ports before deleting it
1070 cls._try_delete_resource(client.remove_subports, trunk['id'],
1071 trunk['sub_ports'])
1072
1073 # we have to detach the interface from the server before
1074 # the trunk can be deleted.
1075 parent_port = {'id': trunk['port_id']}
1076
1077 def is_parent_port_detached():
1078 parent_port.update(client.show_port(parent_port['id'])['port'])
1079 return not parent_port['device_id']
1080
Huifeng Le1c9f40b2018-11-07 01:14:21 +08001081 if detach_parent_port and not is_parent_port_detached():
Federico Ressi82e83e32018-07-03 14:19:55 +02001082 # this could probably happen when trunk is deleted and parent port
1083 # has been assigned to a VM that is still running. Here we are
1084 # assuming that device_id points to such VM.
1085 cls.os_primary.compute.InterfacesClient().delete_interface(
1086 parent_port['device_id'], parent_port['id'])
1087 utils.wait_until_true(is_parent_port_detached)
1088
1089 client.delete_trunk(trunk['id'])
1090
Harald Jensåsc9782fa2019-06-03 22:35:41 +02001091 @classmethod
1092 def create_conntrack_helper(cls, router_id, helper, protocol, port,
1093 client=None):
1094 """Create a conntrack helper
1095
1096 Create a conntrack helper and schedule it for later deletion. If a
1097 client is passed, then it is used for deleteing the CTH too.
1098
1099 :param router_id: The ID of the Neutron router associated to the
1100 conntrack helper.
1101
1102 :param helper: The conntrack helper module alias
1103
1104 :param protocol: The conntrack helper IP protocol used in the conntrack
1105 helper.
1106
1107 :param port: The conntrack helper IP protocol port number for the
1108 conntrack helper.
1109
1110 :param client: network client to be used for creating and cleaning up
1111 the conntrack helper.
1112 """
1113
1114 client = client or cls.client
1115
1116 cth = client.create_conntrack_helper(router_id, helper, protocol,
1117 port)['conntrack_helper']
1118
1119 # save ID of router associated with conntrack helper for final cleanup
1120 cth['router_id'] = router_id
1121
1122 # save client to be used later in cls.delete_conntrack_helper for final
1123 # cleanup
1124 cth['client'] = client
1125 cls.conntrack_helpers.append(cth)
1126 return cth
1127
1128 @classmethod
1129 def delete_conntrack_helper(cls, cth, client=None):
1130 """Delete conntrack helper
1131
1132 :param client: Client to be used
1133 If client is not given it will use the client used to create the
1134 conntrack helper, or cls.client if unknown.
1135 """
1136
1137 client = client or cth.get('client') or cls.client
1138 client.delete_conntrack_helper(cth['router_id'], cth['id'])
1139
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001140
1141class BaseAdminNetworkTest(BaseNetworkTest):
1142
1143 credentials = ['primary', 'admin']
1144
1145 @classmethod
1146 def setup_clients(cls):
1147 super(BaseAdminNetworkTest, cls).setup_clients()
fumihiko kakumaa216fc12017-07-14 10:43:29 +09001148 cls.admin_client = cls.os_admin.network_client
Jakub Libosvarf5758012017-08-15 13:45:30 +00001149 cls.identity_admin_client = cls.os_admin.projects_client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001150
1151 @classmethod
1152 def create_metering_label(cls, name, description):
1153 """Wrapper utility that returns a test metering label."""
1154 body = cls.admin_client.create_metering_label(
1155 description=description,
1156 name=data_utils.rand_name("metering-label"))
1157 metering_label = body['metering_label']
1158 cls.metering_labels.append(metering_label)
1159 return metering_label
1160
1161 @classmethod
1162 def create_metering_label_rule(cls, remote_ip_prefix, direction,
1163 metering_label_id):
1164 """Wrapper utility that returns a test metering label rule."""
1165 body = cls.admin_client.create_metering_label_rule(
1166 remote_ip_prefix=remote_ip_prefix, direction=direction,
1167 metering_label_id=metering_label_id)
1168 metering_label_rule = body['metering_label_rule']
1169 cls.metering_label_rules.append(metering_label_rule)
1170 return metering_label_rule
1171
1172 @classmethod
Kailun Qineaaf9782018-12-20 04:45:01 +08001173 def create_network_segment_range(cls, name, shared,
1174 project_id, network_type,
1175 physical_network, minimum,
1176 maximum):
1177 """Wrapper utility that returns a test network segment range."""
1178 network_segment_range_args = {'name': name,
1179 'shared': shared,
1180 'project_id': project_id,
1181 'network_type': network_type,
1182 'physical_network': physical_network,
1183 'minimum': minimum,
1184 'maximum': maximum}
1185 body = cls.admin_client.create_network_segment_range(
1186 **network_segment_range_args)
1187 network_segment_range = body['network_segment_range']
1188 cls.network_segment_ranges.append(network_segment_range)
1189 return network_segment_range
1190
1191 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001192 def create_flavor(cls, name, description, service_type):
1193 """Wrapper utility that returns a test flavor."""
1194 body = cls.admin_client.create_flavor(
1195 description=description, service_type=service_type,
1196 name=name)
1197 flavor = body['flavor']
1198 cls.flavors.append(flavor)
1199 return flavor
1200
1201 @classmethod
1202 def create_service_profile(cls, description, metainfo, driver):
1203 """Wrapper utility that returns a test service profile."""
1204 body = cls.admin_client.create_service_profile(
1205 driver=driver, metainfo=metainfo, description=description)
1206 service_profile = body['service_profile']
1207 cls.service_profiles.append(service_profile)
1208 return service_profile
1209
1210 @classmethod
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001211 def create_log(cls, name, description=None,
1212 resource_type='security_group', resource_id=None,
1213 target_id=None, event='ALL', enabled=True):
1214 """Wrapper utility that returns a test log object."""
1215 log_args = {'name': name,
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001216 'resource_type': resource_type,
1217 'resource_id': resource_id,
1218 'target_id': target_id,
1219 'event': event,
1220 'enabled': enabled}
Slawek Kaplonskid9fe3022021-08-11 15:25:16 +02001221 if description:
1222 log_args['description'] = description
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001223 body = cls.admin_client.create_log(**log_args)
1224 log_object = body['log']
1225 cls.log_objects.append(log_object)
1226 return log_object
1227
1228 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001229 def get_unused_ip(cls, net_id, ip_version=None):
Gary Kotton011345f2016-06-15 08:04:31 -07001230 """Get an unused ip address in a allocation pool of net"""
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001231 body = cls.admin_client.list_ports(network_id=net_id)
1232 ports = body['ports']
1233 used_ips = []
1234 for port in ports:
1235 used_ips.extend(
1236 [fixed_ip['ip_address'] for fixed_ip in port['fixed_ips']])
1237 body = cls.admin_client.list_subnets(network_id=net_id)
1238 subnets = body['subnets']
1239
1240 for subnet in subnets:
1241 if ip_version and subnet['ip_version'] != ip_version:
1242 continue
1243 cidr = subnet['cidr']
1244 allocation_pools = subnet['allocation_pools']
1245 iterators = []
1246 if allocation_pools:
1247 for allocation_pool in allocation_pools:
1248 iterators.append(netaddr.iter_iprange(
1249 allocation_pool['start'], allocation_pool['end']))
1250 else:
1251 net = netaddr.IPNetwork(cidr)
1252
1253 def _iterip():
1254 for ip in net:
1255 if ip not in (net.network, net.broadcast):
1256 yield ip
1257 iterators.append(iter(_iterip()))
1258
1259 for iterator in iterators:
1260 for ip in iterator:
1261 if str(ip) not in used_ips:
1262 return str(ip)
1263
1264 message = (
1265 "net(%s) has no usable IP address in allocation pools" % net_id)
1266 raise exceptions.InvalidConfiguration(message)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001267
Lajos Katona2f904652018-08-23 14:04:56 +02001268 @classmethod
1269 def create_provider_network(cls, physnet_name, start_segmentation_id,
1270 max_attempts=30):
1271 segmentation_id = start_segmentation_id
Lajos Katona7eb67252019-01-14 12:55:35 +01001272 for attempts in range(max_attempts):
Lajos Katona2f904652018-08-23 14:04:56 +02001273 try:
Lajos Katona7eb67252019-01-14 12:55:35 +01001274 return cls.create_network(
Lajos Katona2f904652018-08-23 14:04:56 +02001275 name=data_utils.rand_name('test_net'),
1276 shared=True,
1277 provider_network_type='vlan',
1278 provider_physical_network=physnet_name,
1279 provider_segmentation_id=segmentation_id)
Lajos Katona2f904652018-08-23 14:04:56 +02001280 except lib_exc.Conflict:
Lajos Katona2f904652018-08-23 14:04:56 +02001281 segmentation_id += 1
1282 if segmentation_id > 4095:
1283 raise lib_exc.TempestException(
1284 "No free segmentation id was found for provider "
1285 "network creation!")
1286 time.sleep(CONF.network.build_interval)
Lajos Katona7eb67252019-01-14 12:55:35 +01001287 LOG.exception("Failed to create provider network after "
1288 "%d attempts", max_attempts)
1289 raise lib_exc.TimeoutException
Lajos Katona2f904652018-08-23 14:04:56 +02001290
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001291
Sławek Kapłońskiff294062016-12-04 15:00:54 +00001292def require_qos_rule_type(rule_type):
1293 def decorator(f):
1294 @functools.wraps(f)
1295 def wrapper(self, *func_args, **func_kwargs):
1296 if rule_type not in self.get_supported_qos_rule_types():
1297 raise self.skipException(
1298 "%s rule type is required." % rule_type)
1299 return f(self, *func_args, **func_kwargs)
1300 return wrapper
1301 return decorator
1302
1303
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001304def _require_sorting(f):
1305 @functools.wraps(f)
1306 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301307 if not tutils.is_extension_enabled("sorting", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001308 self.skipTest('Sorting feature is required')
1309 return f(self, *args, **kwargs)
1310 return inner
1311
1312
1313def _require_pagination(f):
1314 @functools.wraps(f)
1315 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301316 if not tutils.is_extension_enabled("pagination", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001317 self.skipTest('Pagination feature is required')
1318 return f(self, *args, **kwargs)
1319 return inner
1320
1321
1322class BaseSearchCriteriaTest(BaseNetworkTest):
1323
1324 # This should be defined by subclasses to reflect resource name to test
1325 resource = None
1326
Armando Migliaccio57581c62016-07-01 10:13:19 -07001327 field = 'name'
1328
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001329 # NOTE(ihrachys): some names, like those starting with an underscore (_)
1330 # are sorted differently depending on whether the plugin implements native
1331 # sorting support, or not. So we avoid any such cases here, sticking to
1332 # alphanumeric. Also test a case when there are multiple resources with the
1333 # same name
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001334 resource_names = ('test1', 'abc1', 'test10', '123test') + ('test1',)
1335
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001336 force_tenant_isolation = True
1337
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001338 list_kwargs = {}
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001339
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001340 list_as_admin = False
1341
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001342 def assertSameOrder(self, original, actual):
1343 # gracefully handle iterators passed
1344 original = list(original)
1345 actual = list(actual)
1346 self.assertEqual(len(original), len(actual))
1347 for expected, res in zip(original, actual):
Armando Migliaccio57581c62016-07-01 10:13:19 -07001348 self.assertEqual(expected[self.field], res[self.field])
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001349
1350 @utils.classproperty
1351 def plural_name(self):
1352 return '%ss' % self.resource
1353
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001354 @property
1355 def list_client(self):
1356 return self.admin_client if self.list_as_admin else self.client
1357
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001358 def list_method(self, *args, **kwargs):
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001359 method = getattr(self.list_client, 'list_%s' % self.plural_name)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001360 kwargs.update(self.list_kwargs)
1361 return method(*args, **kwargs)
1362
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001363 def get_bare_url(self, url):
1364 base_url = self.client.base_url
zheng.yong74e760a2019-05-22 14:16:14 +08001365 base_url_normalized = utils.normalize_url(base_url)
1366 url_normalized = utils.normalize_url(url)
1367 self.assertTrue(url_normalized.startswith(base_url_normalized))
1368 return url_normalized[len(base_url_normalized):]
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001369
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001370 @classmethod
1371 def _extract_resources(cls, body):
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001372 return body[cls.plural_name]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001373
1374 def _test_list_sorts(self, direction):
1375 sort_args = {
1376 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001377 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001378 }
1379 body = self.list_method(**sort_args)
1380 resources = self._extract_resources(body)
1381 self.assertNotEmpty(
1382 resources, "%s list returned is empty" % self.resource)
Armando Migliaccio57581c62016-07-01 10:13:19 -07001383 retrieved_names = [res[self.field] for res in resources]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001384 expected = sorted(retrieved_names)
1385 if direction == constants.SORT_DIRECTION_DESC:
1386 expected = list(reversed(expected))
1387 self.assertEqual(expected, retrieved_names)
1388
1389 @_require_sorting
1390 def _test_list_sorts_asc(self):
1391 self._test_list_sorts(constants.SORT_DIRECTION_ASC)
1392
1393 @_require_sorting
1394 def _test_list_sorts_desc(self):
1395 self._test_list_sorts(constants.SORT_DIRECTION_DESC)
1396
1397 @_require_pagination
1398 def _test_list_pagination(self):
1399 for limit in range(1, len(self.resource_names) + 1):
1400 pagination_args = {
1401 'limit': limit,
1402 }
1403 body = self.list_method(**pagination_args)
1404 resources = self._extract_resources(body)
1405 self.assertEqual(limit, len(resources))
1406
1407 @_require_pagination
1408 def _test_list_no_pagination_limit_0(self):
1409 pagination_args = {
1410 'limit': 0,
1411 }
1412 body = self.list_method(**pagination_args)
1413 resources = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001414 self.assertGreaterEqual(len(resources), len(self.resource_names))
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001415
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001416 def _test_list_pagination_iteratively(self, lister):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001417 # first, collect all resources for later comparison
1418 sort_args = {
1419 'sort_dir': constants.SORT_DIRECTION_ASC,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001420 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001421 }
1422 body = self.list_method(**sort_args)
1423 expected_resources = self._extract_resources(body)
1424 self.assertNotEmpty(expected_resources)
1425
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001426 resources = lister(
1427 len(expected_resources), sort_args
1428 )
1429
1430 # finally, compare that the list retrieved in one go is identical to
1431 # the one containing pagination results
1432 self.assertSameOrder(expected_resources, resources)
1433
1434 def _list_all_with_marker(self, niterations, sort_args):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001435 # paginate resources one by one, using last fetched resource as a
1436 # marker
1437 resources = []
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001438 for i in range(niterations):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001439 pagination_args = sort_args.copy()
1440 pagination_args['limit'] = 1
1441 if resources:
1442 pagination_args['marker'] = resources[-1]['id']
1443 body = self.list_method(**pagination_args)
1444 resources_ = self._extract_resources(body)
1445 self.assertEqual(1, len(resources_))
1446 resources.extend(resources_)
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001447 return resources
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001448
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001449 @_require_pagination
1450 @_require_sorting
1451 def _test_list_pagination_with_marker(self):
1452 self._test_list_pagination_iteratively(self._list_all_with_marker)
1453
1454 def _list_all_with_hrefs(self, niterations, sort_args):
1455 # paginate resources one by one, using next href links
1456 resources = []
1457 prev_links = {}
1458
1459 for i in range(niterations):
1460 if prev_links:
1461 uri = self.get_bare_url(prev_links['next'])
1462 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001463 sort_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001464 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001465 self.plural_name, limit=1, **sort_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001466 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001467 self.plural_name, uri
1468 )
1469 resources_ = self._extract_resources(body)
1470 self.assertEqual(1, len(resources_))
1471 resources.extend(resources_)
1472
1473 # The last element is empty and does not contain 'next' link
1474 uri = self.get_bare_url(prev_links['next'])
1475 prev_links, body = self.client.get_uri_with_links(
1476 self.plural_name, uri
1477 )
1478 self.assertNotIn('next', prev_links)
1479
1480 # Now walk backwards and compare results
1481 resources2 = []
1482 for i in range(niterations):
1483 uri = self.get_bare_url(prev_links['previous'])
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001484 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001485 self.plural_name, uri
1486 )
1487 resources_ = self._extract_resources(body)
1488 self.assertEqual(1, len(resources_))
1489 resources2.extend(resources_)
1490
1491 self.assertSameOrder(resources, reversed(resources2))
1492
1493 return resources
1494
1495 @_require_pagination
1496 @_require_sorting
1497 def _test_list_pagination_with_href_links(self):
1498 self._test_list_pagination_iteratively(self._list_all_with_hrefs)
1499
1500 @_require_pagination
1501 @_require_sorting
1502 def _test_list_pagination_page_reverse_with_href_links(
1503 self, direction=constants.SORT_DIRECTION_ASC):
1504 pagination_args = {
1505 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001506 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001507 }
1508 body = self.list_method(**pagination_args)
1509 expected_resources = self._extract_resources(body)
1510
1511 page_size = 2
1512 pagination_args['limit'] = page_size
1513
1514 prev_links = {}
1515 resources = []
1516 num_resources = len(expected_resources)
1517 niterations = int(math.ceil(float(num_resources) / page_size))
1518 for i in range(niterations):
1519 if prev_links:
1520 uri = self.get_bare_url(prev_links['previous'])
1521 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001522 pagination_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001523 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001524 self.plural_name, page_reverse=True, **pagination_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001525 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001526 self.plural_name, uri
1527 )
1528 resources_ = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001529 self.assertGreaterEqual(page_size, len(resources_))
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001530 resources.extend(reversed(resources_))
1531
1532 self.assertSameOrder(expected_resources, reversed(resources))
1533
1534 @_require_pagination
1535 @_require_sorting
1536 def _test_list_pagination_page_reverse_asc(self):
1537 self._test_list_pagination_page_reverse(
1538 direction=constants.SORT_DIRECTION_ASC)
1539
1540 @_require_pagination
1541 @_require_sorting
1542 def _test_list_pagination_page_reverse_desc(self):
1543 self._test_list_pagination_page_reverse(
1544 direction=constants.SORT_DIRECTION_DESC)
1545
1546 def _test_list_pagination_page_reverse(self, direction):
1547 pagination_args = {
1548 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001549 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001550 'limit': 3,
1551 }
1552 body = self.list_method(**pagination_args)
1553 expected_resources = self._extract_resources(body)
1554
1555 pagination_args['limit'] -= 1
1556 pagination_args['marker'] = expected_resources[-1]['id']
1557 pagination_args['page_reverse'] = True
1558 body = self.list_method(**pagination_args)
1559
1560 self.assertSameOrder(
1561 # the last entry is not included in 2nd result when used as a
1562 # marker
1563 expected_resources[:-1],
1564 self._extract_resources(body))
Victor Morales1be97b42016-09-05 08:50:06 -05001565
Hongbin Lu54f55922018-07-12 19:05:39 +00001566 @tutils.requires_ext(extension="filter-validation", service="network")
1567 def _test_list_validation_filters(
1568 self, validation_args, filter_is_valid=True):
1569 if not filter_is_valid:
1570 self.assertRaises(lib_exc.BadRequest, self.list_method,
1571 **validation_args)
1572 else:
1573 body = self.list_method(**validation_args)
1574 resources = self._extract_resources(body)
1575 for resource in resources:
1576 self.assertIn(resource['name'], self.resource_names)