blob: 07fcb0bde5a0137ec1a22a65d8917f242af34284 [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 = []
Nurmatov Mamatisa3f2bbb52021-10-20 14:33:54 +0300121 cls.local_ips = []
122 cls.local_ip_associations = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000123 cls.metering_labels = []
124 cls.service_profiles = []
125 cls.flavors = []
126 cls.metering_label_rules = []
127 cls.qos_rules = []
128 cls.qos_policies = []
129 cls.ethertype = "IPv" + str(cls._ip_version)
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600130 cls.address_groups = []
131 cls.admin_address_groups = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000132 cls.address_scopes = []
133 cls.admin_address_scopes = []
134 cls.subnetpools = []
135 cls.admin_subnetpools = []
Itzik Brownbac51dc2016-10-31 12:25:04 +0000136 cls.security_groups = []
Dongcan Ye2de722e2018-07-04 11:01:37 +0000137 cls.admin_security_groups = []
Chandan Kumarc125fd12017-11-15 19:41:01 +0530138 cls.projects = []
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700139 cls.log_objects = []
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200140 cls.reserved_subnet_cidrs = set()
Federico Ressiab286e42018-06-19 09:52:10 +0200141 cls.keypairs = []
Federico Ressi82e83e32018-07-03 14:19:55 +0200142 cls.trunks = []
Kailun Qineaaf9782018-12-20 04:45:01 +0800143 cls.network_segment_ranges = []
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200144 cls.conntrack_helpers = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000145
146 @classmethod
yangjianfeng23e40c22020-11-22 08:42:18 +0000147 def reserve_external_subnet_cidrs(cls):
148 client = cls.os_admin.network_client
149 ext_nets = client.list_networks(
150 **{"router:external": True})['networks']
151 for ext_net in ext_nets:
152 ext_subnets = client.list_subnets(
153 network_id=ext_net['id'])['subnets']
154 for ext_subnet in ext_subnets:
155 cls.reserve_subnet_cidr(ext_subnet['cidr'])
156
157 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000158 def resource_cleanup(cls):
159 if CONF.service_available.neutron:
Federico Ressi82e83e32018-07-03 14:19:55 +0200160 # Clean up trunks
161 for trunk in cls.trunks:
162 cls._try_delete_resource(cls.delete_trunk, trunk)
163
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200164 # Clean up port forwardings
165 for pf in cls.port_forwardings:
166 cls._try_delete_resource(cls.delete_port_forwarding, pf)
167
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000168 # Clean up floating IPs
169 for floating_ip in cls.floating_ips:
Federico Ressia69dcd52018-07-06 09:45:34 +0200170 cls._try_delete_resource(cls.delete_floatingip, floating_ip)
171
Nurmatov Mamatisa3f2bbb52021-10-20 14:33:54 +0300172 # Clean up Local IP Associations
173 for association in cls.local_ip_associations:
174 cls._try_delete_resource(cls.delete_local_ip_association,
175 association)
176 # Clean up Local IPs
177 for local_ip in cls.local_ips:
178 cls._try_delete_resource(cls.delete_local_ip,
179 local_ip)
180
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200181 # Clean up conntrack helpers
182 for cth in cls.conntrack_helpers:
183 cls._try_delete_resource(cls.delete_conntrack_helper, cth)
184
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000185 # Clean up routers
186 for router in cls.routers:
187 cls._try_delete_resource(cls.delete_router,
188 router)
189 # Clean up metering label rules
190 for metering_label_rule in cls.metering_label_rules:
191 cls._try_delete_resource(
192 cls.admin_client.delete_metering_label_rule,
193 metering_label_rule['id'])
194 # Clean up metering labels
195 for metering_label in cls.metering_labels:
196 cls._try_delete_resource(
197 cls.admin_client.delete_metering_label,
198 metering_label['id'])
199 # Clean up flavors
200 for flavor in cls.flavors:
201 cls._try_delete_resource(
202 cls.admin_client.delete_flavor,
203 flavor['id'])
204 # Clean up service profiles
205 for service_profile in cls.service_profiles:
206 cls._try_delete_resource(
207 cls.admin_client.delete_service_profile,
208 service_profile['id'])
209 # Clean up ports
210 for port in cls.ports:
211 cls._try_delete_resource(cls.client.delete_port,
212 port['id'])
213 # Clean up subnets
214 for subnet in cls.subnets:
215 cls._try_delete_resource(cls.client.delete_subnet,
216 subnet['id'])
Kevin Bentonba3651c2017-09-01 17:13:01 -0700217 # Clean up admin subnets
218 for subnet in cls.admin_subnets:
219 cls._try_delete_resource(cls.admin_client.delete_subnet,
220 subnet['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000221 # Clean up networks
222 for network in cls.networks:
Federico Ressi61b564e2018-07-06 08:10:31 +0200223 cls._try_delete_resource(cls.delete_network, network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000224
Miguel Lavalle124378b2016-09-21 16:41:47 -0500225 # Clean up admin networks
226 for network in cls.admin_networks:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000227 cls._try_delete_resource(cls.admin_client.delete_network,
228 network['id'])
229
Itzik Brownbac51dc2016-10-31 12:25:04 +0000230 # Clean up security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200231 for security_group in cls.security_groups:
232 cls._try_delete_resource(cls.delete_security_group,
233 security_group)
Itzik Brownbac51dc2016-10-31 12:25:04 +0000234
Dongcan Ye2de722e2018-07-04 11:01:37 +0000235 # Clean up admin security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200236 for security_group in cls.admin_security_groups:
237 cls._try_delete_resource(cls.delete_security_group,
238 security_group,
239 client=cls.admin_client)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000240
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000241 for subnetpool in cls.subnetpools:
242 cls._try_delete_resource(cls.client.delete_subnetpool,
243 subnetpool['id'])
244
245 for subnetpool in cls.admin_subnetpools:
246 cls._try_delete_resource(cls.admin_client.delete_subnetpool,
247 subnetpool['id'])
248
249 for address_scope in cls.address_scopes:
250 cls._try_delete_resource(cls.client.delete_address_scope,
251 address_scope['id'])
252
253 for address_scope in cls.admin_address_scopes:
254 cls._try_delete_resource(
255 cls.admin_client.delete_address_scope,
256 address_scope['id'])
257
Chandan Kumarc125fd12017-11-15 19:41:01 +0530258 for project in cls.projects:
259 cls._try_delete_resource(
260 cls.identity_admin_client.delete_project,
261 project['id'])
262
Sławek Kapłońskie100c4d2017-08-23 21:18:34 +0000263 # Clean up QoS rules
264 for qos_rule in cls.qos_rules:
265 cls._try_delete_resource(cls.admin_client.delete_qos_rule,
266 qos_rule['id'])
267 # Clean up QoS policies
268 # as all networks and ports are already removed, QoS policies
269 # shouldn't be "in use"
270 for qos_policy in cls.qos_policies:
271 cls._try_delete_resource(cls.admin_client.delete_qos_policy,
272 qos_policy['id'])
273
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700274 # Clean up log_objects
275 for log_object in cls.log_objects:
276 cls._try_delete_resource(cls.admin_client.delete_log,
277 log_object['id'])
278
Federico Ressiab286e42018-06-19 09:52:10 +0200279 for keypair in cls.keypairs:
280 cls._try_delete_resource(cls.delete_keypair, keypair)
281
Kailun Qineaaf9782018-12-20 04:45:01 +0800282 # Clean up network_segment_ranges
283 for network_segment_range in cls.network_segment_ranges:
284 cls._try_delete_resource(
285 cls.admin_client.delete_network_segment_range,
286 network_segment_range['id'])
287
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000288 super(BaseNetworkTest, cls).resource_cleanup()
289
290 @classmethod
291 def _try_delete_resource(cls, delete_callable, *args, **kwargs):
292 """Cleanup resources in case of test-failure
293
294 Some resources are explicitly deleted by the test.
295 If the test failed to delete a resource, this method will execute
296 the appropriate delete methods. Otherwise, the method ignores NotFound
297 exceptions thrown for resources that were correctly deleted by the
298 test.
299
300 :param delete_callable: delete method
301 :param args: arguments for delete method
302 :param kwargs: keyword arguments for delete method
303 """
304 try:
305 delete_callable(*args, **kwargs)
306 # if resource is not found, this means it was deleted in the test
307 except lib_exc.NotFound:
308 pass
309
310 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200311 def create_network(cls, network_name=None, client=None, external=None,
312 shared=None, provider_network_type=None,
313 provider_physical_network=None,
314 provider_segmentation_id=None, **kwargs):
315 """Create a network.
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000316
Federico Ressi61b564e2018-07-06 08:10:31 +0200317 When client is not provider and admin_client is attribute is not None
318 (for example when using BaseAdminNetworkTest base class) and using any
319 of the convenience parameters (external, shared, provider_network_type,
320 provider_physical_network and provider_segmentation_id) it silently
321 uses admin_client. If the network is not shared then it uses the same
322 project_id as regular client.
323
324 :param network_name: Human-readable name of the network
325
326 :param client: client to be used for connecting to network service
327
328 :param external: indicates whether the network has an external routing
329 facility that's not managed by the networking service.
330
331 :param shared: indicates whether this resource is shared across all
332 projects. By default, only administrative users can change this value.
333 If True and admin_client attribute is not None, then the network is
334 created under administrative project.
335
336 :param provider_network_type: the type of physical network that this
337 network should be mapped to. For example, 'flat', 'vlan', 'vxlan', or
338 'gre'. Valid values depend on a networking back-end.
339
340 :param provider_physical_network: the physical network where this
341 network should be implemented. The Networking API v2.0 does not provide
342 a way to list available physical networks. For example, the Open
343 vSwitch plug-in configuration file defines a symbolic name that maps to
344 specific bridges on each compute host.
345
346 :param provider_segmentation_id: The ID of the isolated segment on the
347 physical network. The network_type attribute defines the segmentation
348 model. For example, if the network_type value is 'vlan', this ID is a
349 vlan identifier. If the network_type value is 'gre', this ID is a gre
350 key.
351
352 :param **kwargs: extra parameters to be forwarded to network service
353 """
354
355 name = (network_name or kwargs.pop('name', None) or
356 data_utils.rand_name('test-network-'))
357
358 # translate convenience parameters
359 admin_client_required = False
360 if provider_network_type:
361 admin_client_required = True
362 kwargs['provider:network_type'] = provider_network_type
363 if provider_physical_network:
364 admin_client_required = True
365 kwargs['provider:physical_network'] = provider_physical_network
366 if provider_segmentation_id:
367 admin_client_required = True
368 kwargs['provider:segmentation_id'] = provider_segmentation_id
369 if external is not None:
370 admin_client_required = True
371 kwargs['router:external'] = bool(external)
372 if shared is not None:
373 admin_client_required = True
374 kwargs['shared'] = bool(shared)
375
376 if not client:
377 if admin_client_required and cls.admin_client:
378 # For convenience silently switch to admin client
379 client = cls.admin_client
380 if not shared:
381 # Keep this network visible from current project
382 project_id = (kwargs.get('project_id') or
383 kwargs.get('tenant_id') or
384 cls.client.tenant_id)
385 kwargs.update(project_id=project_id, tenant_id=project_id)
386 else:
387 # Use default client
388 client = cls.client
389
390 network = client.create_network(name=name, **kwargs)['network']
391 network['client'] = client
392 cls.networks.append(network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000393 return network
394
395 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200396 def delete_network(cls, network, client=None):
397 client = client or network.get('client') or cls.client
398 client.delete_network(network['id'])
399
400 @classmethod
401 def create_shared_network(cls, network_name=None, **kwargs):
402 return cls.create_network(name=network_name, shared=True, **kwargs)
Miguel Lavalle124378b2016-09-21 16:41:47 -0500403
404 @classmethod
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200405 def create_subnet(cls, network, gateway='', cidr=None, mask_bits=None,
Federico Ressi98f20ec2018-05-11 06:09:49 +0200406 ip_version=None, client=None, reserve_cidr=True,
407 **kwargs):
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200408 """Wrapper utility that returns a test subnet.
409
410 Convenient wrapper for client.create_subnet method. It reserves and
411 allocates CIDRs to avoid creating overlapping subnets.
412
413 :param network: network where to create the subnet
414 network['id'] must contain the ID of the network
415
416 :param gateway: gateway IP address
417 It can be a str or a netaddr.IPAddress
418 If gateway is not given, then it will use default address for
419 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 +0200420 if gateway is given as None then no gateway will be assigned
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200421
422 :param cidr: CIDR of the subnet to create
423 It can be either None, a str or a netaddr.IPNetwork instance
424
425 :param mask_bits: CIDR prefix length
426 It can be either None or a numeric value.
427 If cidr parameter is given then mask_bits is used to determinate a
428 sequence of valid CIDR to use as generated.
429 Please see netaddr.IPNetwork.subnet method documentation[1]
430
431 :param ip_version: ip version of generated subnet CIDRs
432 It can be None, IP_VERSION_4 or IP_VERSION_6
433 It has to match given either given CIDR and gateway
434
435 :param ip_version: numeric value (either IP_VERSION_4 or IP_VERSION_6)
436 this value must match CIDR and gateway IP versions if any of them is
437 given
438
439 :param client: client to be used to connect to network service
440
Federico Ressi98f20ec2018-05-11 06:09:49 +0200441 :param reserve_cidr: if True then it reserves assigned CIDR to avoid
442 using the same CIDR for further subnets in the scope of the same
443 test case class
444
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200445 :param **kwargs: optional parameters to be forwarded to wrapped method
446
447 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
448 """
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000449
450 # allow tests to use admin client
451 if not client:
452 client = cls.client
453
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200454 if gateway:
455 gateway_ip = netaddr.IPAddress(gateway)
456 if ip_version:
457 if ip_version != gateway_ip.version:
458 raise ValueError(
459 "Gateway IP version doesn't match IP version")
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000460 else:
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200461 ip_version = gateway_ip.version
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200462 else:
463 ip_version = ip_version or cls._ip_version
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200464
465 for subnet_cidr in cls.get_subnet_cidrs(
466 ip_version=ip_version, cidr=cidr, mask_bits=mask_bits):
Federico Ressi98f20ec2018-05-11 06:09:49 +0200467 if gateway is not None:
468 kwargs['gateway_ip'] = str(gateway or (subnet_cidr.ip + 1))
Slawek Kaplonski21f53422018-11-02 16:02:09 +0100469 else:
470 kwargs['gateway_ip'] = None
Federico Ressi98f20ec2018-05-11 06:09:49 +0200471 try:
472 body = client.create_subnet(
473 network_id=network['id'],
474 cidr=str(subnet_cidr),
475 ip_version=subnet_cidr.version,
476 **kwargs)
477 break
478 except lib_exc.BadRequest as e:
479 if 'overlaps with another subnet' not in str(e):
480 raise
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000481 else:
482 message = 'Available CIDR for subnet creation could not be found'
483 raise ValueError(message)
484 subnet = body['subnet']
Kevin Bentonba3651c2017-09-01 17:13:01 -0700485 if client is cls.client:
486 cls.subnets.append(subnet)
487 else:
488 cls.admin_subnets.append(subnet)
Federico Ressi98f20ec2018-05-11 06:09:49 +0200489 if reserve_cidr:
490 cls.reserve_subnet_cidr(subnet_cidr)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000491 return subnet
492
493 @classmethod
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200494 def reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
495 """Reserve given subnet CIDR making sure it is not used by create_subnet
496
497 :param addr: the CIDR address to be reserved
498 It can be a str or netaddr.IPNetwork instance
499
500 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
501 parameters
502 """
503
504 if not cls.try_reserve_subnet_cidr(addr, **ipnetwork_kwargs):
Bernard Cafarellic3bec862020-09-10 13:59:49 +0200505 raise ValueError('Subnet CIDR already reserved: {0!r}'.format(
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200506 addr))
507
508 @classmethod
509 def try_reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
510 """Reserve given subnet CIDR if it hasn't been reserved before
511
512 :param addr: the CIDR address to be reserved
513 It can be a str or netaddr.IPNetwork instance
514
515 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
516 parameters
517
518 :return: True if it wasn't reserved before, False elsewhere.
519 """
520
521 subnet_cidr = netaddr.IPNetwork(addr, **ipnetwork_kwargs)
522 if subnet_cidr in cls.reserved_subnet_cidrs:
523 return False
524 else:
525 cls.reserved_subnet_cidrs.add(subnet_cidr)
526 return True
527
528 @classmethod
529 def get_subnet_cidrs(
530 cls, cidr=None, mask_bits=None, ip_version=None):
531 """Iterate over a sequence of unused subnet CIDR for IP version
532
533 :param cidr: CIDR of the subnet to create
534 It can be either None, a str or a netaddr.IPNetwork instance
535
536 :param mask_bits: CIDR prefix length
537 It can be either None or a numeric value.
538 If cidr parameter is given then mask_bits is used to determinate a
539 sequence of valid CIDR to use as generated.
540 Please see netaddr.IPNetwork.subnet method documentation[1]
541
542 :param ip_version: ip version of generated subnet CIDRs
543 It can be None, IP_VERSION_4 or IP_VERSION_6
544 It has to match given CIDR if given
545
546 :return: iterator over reserved CIDRs of type netaddr.IPNetwork
547
548 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
549 """
550
551 if cidr:
552 # Generate subnet CIDRs starting from given CIDR
553 # checking it is of requested IP version
554 cidr = netaddr.IPNetwork(cidr, version=ip_version)
555 else:
556 # Generate subnet CIDRs starting from configured values
557 ip_version = ip_version or cls._ip_version
558 if ip_version == const.IP_VERSION_4:
559 mask_bits = mask_bits or config.safe_get_config_value(
560 'network', 'project_network_mask_bits')
561 cidr = netaddr.IPNetwork(config.safe_get_config_value(
562 'network', 'project_network_cidr'))
563 elif ip_version == const.IP_VERSION_6:
564 mask_bits = config.safe_get_config_value(
565 'network', 'project_network_v6_mask_bits')
566 cidr = netaddr.IPNetwork(config.safe_get_config_value(
567 'network', 'project_network_v6_cidr'))
568 else:
569 raise ValueError('Invalid IP version: {!r}'.format(ip_version))
570
571 if mask_bits:
572 subnet_cidrs = cidr.subnet(mask_bits)
573 else:
574 subnet_cidrs = iter([cidr])
575
576 for subnet_cidr in subnet_cidrs:
577 if subnet_cidr not in cls.reserved_subnet_cidrs:
578 yield subnet_cidr
579
580 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000581 def create_port(cls, network, **kwargs):
582 """Wrapper utility that returns a test port."""
Edan Davidd75e48e2018-01-03 02:49:52 -0500583 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
584 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
Glenn Van de Water5d9b1402020-09-16 15:14:14 +0200585 if CONF.network.port_profile and 'binding:profile' not in kwargs:
586 kwargs['binding:profile'] = CONF.network.port_profile
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000587 body = cls.client.create_port(network_id=network['id'],
588 **kwargs)
589 port = body['port']
590 cls.ports.append(port)
591 return port
592
593 @classmethod
594 def update_port(cls, port, **kwargs):
595 """Wrapper utility that updates a test port."""
596 body = cls.client.update_port(port['id'],
597 **kwargs)
598 return body['port']
599
600 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300601 def _create_router_with_client(
602 cls, client, router_name=None, admin_state_up=False,
603 external_network_id=None, enable_snat=None, **kwargs
604 ):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000605 ext_gw_info = {}
606 if external_network_id:
607 ext_gw_info['network_id'] = external_network_id
YAMAMOTO Takashi9bd4f972017-06-20 12:49:30 +0900608 if enable_snat is not None:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000609 ext_gw_info['enable_snat'] = enable_snat
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300610 body = client.create_router(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000611 router_name, external_gateway_info=ext_gw_info,
612 admin_state_up=admin_state_up, **kwargs)
613 router = body['router']
614 cls.routers.append(router)
615 return router
616
617 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300618 def create_router(cls, *args, **kwargs):
619 return cls._create_router_with_client(cls.client, *args, **kwargs)
620
621 @classmethod
622 def create_admin_router(cls, *args, **kwargs):
rajat294495c042017-06-28 15:37:16 +0530623 return cls._create_router_with_client(cls.os_admin.network_client,
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300624 *args, **kwargs)
625
626 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200627 def create_floatingip(cls, external_network_id=None, port=None,
628 client=None, **kwargs):
629 """Creates a floating IP.
630
631 Create a floating IP and schedule it for later deletion.
632 If a client is passed, then it is used for deleting the IP too.
633
634 :param external_network_id: network ID where to create
635 By default this is 'CONF.network.public_network_id'.
636
637 :param port: port to bind floating IP to
638 This is translated to 'port_id=port['id']'
639 By default it is None.
640
641 :param client: network client to be used for creating and cleaning up
642 the floating IP.
643
644 :param **kwargs: additional creation parameters to be forwarded to
645 networking server.
646 """
647
648 client = client or cls.client
649 external_network_id = (external_network_id or
650 cls.external_network_id)
651
652 if port:
Federico Ressi47f6ae42018-09-24 16:19:14 +0200653 port_id = kwargs.setdefault('port_id', port['id'])
654 if port_id != port['id']:
655 message = "Port ID specified twice: {!s} != {!s}".format(
656 port_id, port['id'])
657 raise ValueError(message)
Federico Ressia69dcd52018-07-06 09:45:34 +0200658
659 fip = client.create_floatingip(external_network_id,
660 **kwargs)['floatingip']
661
662 # save client to be used later in cls.delete_floatingip
663 # for final cleanup
664 fip['client'] = client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000665 cls.floating_ips.append(fip)
666 return fip
667
668 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200669 def delete_floatingip(cls, floating_ip, client=None):
670 """Delete floating IP
671
672 :param client: Client to be used
673 If client is not given it will use the client used to create
674 the floating IP, or cls.client if unknown.
675 """
676
677 client = client or floating_ip.get('client') or cls.client
678 client.delete_floatingip(floating_ip['id'])
679
680 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200681 def create_port_forwarding(cls, fip_id, internal_port_id,
682 internal_port, external_port,
683 internal_ip_address=None, protocol="tcp",
684 client=None):
685 """Creates a port forwarding.
686
687 Create a port forwarding and schedule it for later deletion.
688 If a client is passed, then it is used for deleting the PF too.
689
690 :param fip_id: The ID of the floating IP address.
691
692 :param internal_port_id: The ID of the Neutron port associated to
693 the floating IP port forwarding.
694
695 :param internal_port: The TCP/UDP/other protocol port number of the
696 Neutron port fixed IP address associated to the floating ip
697 port forwarding.
698
699 :param external_port: The TCP/UDP/other protocol port number of
700 the port forwarding floating IP address.
701
702 :param internal_ip_address: The fixed IPv4 address of the Neutron
703 port associated to the floating IP port forwarding.
704
705 :param protocol: The IP protocol used in the floating IP port
706 forwarding.
707
708 :param client: network client to be used for creating and cleaning up
709 the floating IP port forwarding.
710 """
711
712 client = client or cls.client
713
714 pf = client.create_port_forwarding(
715 fip_id, internal_port_id, internal_port, external_port,
716 internal_ip_address, protocol)['port_forwarding']
717
718 # save ID of floating IP associated with port forwarding for final
719 # cleanup
720 pf['floatingip_id'] = fip_id
721
722 # save client to be used later in cls.delete_port_forwarding
723 # for final cleanup
724 pf['client'] = client
725 cls.port_forwardings.append(pf)
726 return pf
727
728 @classmethod
Flavio Fernandesa1952c62020-10-02 06:39:08 -0400729 def update_port_forwarding(cls, fip_id, pf_id, client=None, **kwargs):
730 """Wrapper utility for update_port_forwarding."""
731 client = client or cls.client
732 return client.update_port_forwarding(fip_id, pf_id, **kwargs)
733
734 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200735 def delete_port_forwarding(cls, pf, client=None):
736 """Delete port forwarding
737
738 :param client: Client to be used
739 If client is not given it will use the client used to create
740 the port forwarding, or cls.client if unknown.
741 """
742
743 client = client or pf.get('client') or cls.client
744 client.delete_port_forwarding(pf['floatingip_id'], pf['id'])
745
Nurmatov Mamatisa3f2bbb52021-10-20 14:33:54 +0300746 def create_local_ip(cls, network_id=None,
747 client=None, **kwargs):
748 """Creates a Local IP.
749
750 Create a Local IP and schedule it for later deletion.
751 If a client is passed, then it is used for deleting the IP too.
752
753 :param network_id: network ID where to create
754 By default this is 'CONF.network.public_network_id'.
755
756 :param client: network client to be used for creating and cleaning up
757 the Local IP.
758
759 :param **kwargs: additional creation parameters to be forwarded to
760 networking server.
761 """
762
763 client = client or cls.client
764 network_id = (network_id or
765 cls.external_network_id)
766
767 local_ip = client.create_local_ip(network_id,
768 **kwargs)['local_ip']
769
770 # save client to be used later in cls.delete_local_ip
771 # for final cleanup
772 local_ip['client'] = client
773 cls.local_ips.append(local_ip)
774 return local_ip
775
776 @classmethod
777 def delete_local_ip(cls, local_ip, client=None):
778 """Delete Local IP
779
780 :param client: Client to be used
781 If client is not given it will use the client used to create
782 the Local IP, or cls.client if unknown.
783 """
784
785 client = client or local_ip.get('client') or cls.client
786 client.delete_local_ip(local_ip['id'])
787
788 @classmethod
789 def create_local_ip_association(cls, local_ip_id, fixed_port_id,
790 fixed_ip_address=None, client=None):
791 """Creates a Local IP association.
792
793 Create a Local IP Association and schedule it for later deletion.
794 If a client is passed, then it is used for deleting the association
795 too.
796
797 :param local_ip_id: The ID of the Local IP.
798
799 :param fixed_port_id: The ID of the Neutron port
800 to be associated with the Local IP
801
802 :param fixed_ip_address: The fixed IPv4 address of the Neutron
803 port to be associated with the Local IP
804
805 :param client: network client to be used for creating and cleaning up
806 the Local IP Association.
807 """
808
809 client = client or cls.client
810
811 association = client.create_local_ip_association(
812 local_ip_id, fixed_port_id,
813 fixed_ip_address)['port_association']
814
815 # save ID of Local IP for final cleanup
816 association['local_ip_id'] = local_ip_id
817
818 # save client to be used later in
819 # cls.delete_local_ip_association for final cleanup
820 association['client'] = client
821 cls.local_ip_associations.append(association)
822 return association
823
824 @classmethod
825 def delete_local_ip_association(cls, association, client=None):
826
827 """Delete Local IP Association
828
829 :param client: Client to be used
830 If client is not given it will use the client used to create
831 the local IP association, or cls.client if unknown.
832 """
833
834 client = client or association.get('client') or cls.client
835 client.delete_local_ip_association(association['local_ip_id'],
836 association['fixed_port_id'])
837
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200838 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000839 def create_router_interface(cls, router_id, subnet_id):
840 """Wrapper utility that returns a router interface."""
841 interface = cls.client.add_router_interface_with_subnet_id(
842 router_id, subnet_id)
843 return interface
844
845 @classmethod
Bence Romsics46bd3af2019-09-13 10:52:41 +0200846 def add_extra_routes_atomic(cls, *args, **kwargs):
847 return cls.client.add_extra_routes_atomic(*args, **kwargs)
848
849 @classmethod
850 def remove_extra_routes_atomic(cls, *args, **kwargs):
851 return cls.client.remove_extra_routes_atomic(*args, **kwargs)
852
853 @classmethod
Sławek Kapłońskiff294062016-12-04 15:00:54 +0000854 def get_supported_qos_rule_types(cls):
855 body = cls.client.list_qos_rule_types()
856 return [rule_type['type'] for rule_type in body['rule_types']]
857
858 @classmethod
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +0200859 def create_qos_policy(cls, name, description=None, shared=False,
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000860 project_id=None, is_default=False):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000861 """Wrapper utility that returns a test QoS policy."""
862 body = cls.admin_client.create_qos_policy(
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000863 name, description, shared, project_id, is_default)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000864 qos_policy = body['policy']
865 cls.qos_policies.append(qos_policy)
866 return qos_policy
867
868 @classmethod
elajkatdbb0b482021-05-04 17:20:07 +0200869 def create_qos_dscp_marking_rule(cls, policy_id, dscp_mark):
870 """Wrapper utility that creates and returns a QoS dscp rule."""
871 body = cls.admin_client.create_dscp_marking_rule(
872 policy_id, dscp_mark)
873 qos_rule = body['dscp_marking_rule']
874 cls.qos_rules.append(qos_rule)
875 return qos_rule
876
877 @classmethod
Jakub Libosvar83704832017-12-06 16:02:28 +0000878 def delete_router(cls, router, client=None):
879 client = client or cls.client
Aditya Vaja49819a72018-11-26 14:20:10 -0800880 if 'routes' in router:
881 client.remove_router_extra_routes(router['id'])
Jakub Libosvar83704832017-12-06 16:02:28 +0000882 body = client.list_router_interfaces(router['id'])
Chandan Kumarc125fd12017-11-15 19:41:01 +0530883 interfaces = [port for port in body['ports']
884 if port['device_owner'] in const.ROUTER_INTERFACE_OWNERS]
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000885 for i in interfaces:
886 try:
Jakub Libosvar83704832017-12-06 16:02:28 +0000887 client.remove_router_interface_with_subnet_id(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000888 router['id'], i['fixed_ips'][0]['subnet_id'])
889 except lib_exc.NotFound:
890 pass
Jakub Libosvar83704832017-12-06 16:02:28 +0000891 client.delete_router(router['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000892
893 @classmethod
894 def create_address_scope(cls, name, is_admin=False, **kwargs):
895 if is_admin:
896 body = cls.admin_client.create_address_scope(name=name, **kwargs)
897 cls.admin_address_scopes.append(body['address_scope'])
898 else:
899 body = cls.client.create_address_scope(name=name, **kwargs)
900 cls.address_scopes.append(body['address_scope'])
901 return body['address_scope']
902
903 @classmethod
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200904 def create_subnetpool(cls, name, is_admin=False, client=None, **kwargs):
905 if client is None:
906 client = cls.admin_client if is_admin else cls.client
907
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000908 if is_admin:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200909 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000910 cls.admin_subnetpools.append(body['subnetpool'])
911 else:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200912 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000913 cls.subnetpools.append(body['subnetpool'])
914 return body['subnetpool']
915
Chandan Kumarc125fd12017-11-15 19:41:01 +0530916 @classmethod
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600917 def create_address_group(cls, name, is_admin=False, **kwargs):
918 if is_admin:
919 body = cls.admin_client.create_address_group(name=name, **kwargs)
920 cls.admin_address_groups.append(body['address_group'])
921 else:
922 body = cls.client.create_address_group(name=name, **kwargs)
923 cls.address_groups.append(body['address_group'])
924 return body['address_group']
925
926 @classmethod
Chandan Kumarc125fd12017-11-15 19:41:01 +0530927 def create_project(cls, name=None, description=None):
928 test_project = name or data_utils.rand_name('test_project_')
929 test_description = description or data_utils.rand_name('desc_')
930 project = cls.identity_admin_client.create_project(
931 name=test_project,
932 description=test_description)['project']
933 cls.projects.append(project)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000934 # Create a project will create a default security group.
Dongcan Ye2de722e2018-07-04 11:01:37 +0000935 sgs_list = cls.admin_client.list_security_groups(
936 tenant_id=project['id'])['security_groups']
Federico Ressi4c590d72018-10-10 14:01:08 +0200937 for security_group in sgs_list:
938 # Make sure delete_security_group method will use
939 # the admin client for this group
940 security_group['client'] = cls.admin_client
941 cls.security_groups.append(security_group)
Chandan Kumarc125fd12017-11-15 19:41:01 +0530942 return project
943
944 @classmethod
Federico Ressi4c590d72018-10-10 14:01:08 +0200945 def create_security_group(cls, name=None, project=None, client=None,
946 **kwargs):
947 if project:
948 client = client or cls.admin_client
949 project_id = kwargs.setdefault('project_id', project['id'])
950 tenant_id = kwargs.setdefault('tenant_id', project['id'])
951 if project_id != project['id'] or tenant_id != project['id']:
952 raise ValueError('Project ID specified multiple times')
953 else:
954 client = client or cls.client
955
956 name = name or data_utils.rand_name(cls.__name__)
957 security_group = client.create_security_group(name=name, **kwargs)[
958 'security_group']
959 security_group['client'] = client
960 cls.security_groups.append(security_group)
961 return security_group
962
963 @classmethod
964 def delete_security_group(cls, security_group, client=None):
965 client = client or security_group.get('client') or cls.client
966 client.delete_security_group(security_group['id'])
967
968 @classmethod
969 def create_security_group_rule(cls, security_group=None, project=None,
970 client=None, ip_version=None, **kwargs):
971 if project:
972 client = client or cls.admin_client
973 project_id = kwargs.setdefault('project_id', project['id'])
974 tenant_id = kwargs.setdefault('tenant_id', project['id'])
975 if project_id != project['id'] or tenant_id != project['id']:
976 raise ValueError('Project ID specified multiple times')
977
978 if 'security_group_id' not in kwargs:
979 security_group = (security_group or
980 cls.get_security_group(client=client))
981
982 if security_group:
983 client = client or security_group.get('client')
984 security_group_id = kwargs.setdefault('security_group_id',
985 security_group['id'])
986 if security_group_id != security_group['id']:
987 raise ValueError('Security group ID specified multiple times.')
988
989 ip_version = ip_version or cls._ip_version
990 default_params = (
991 constants.DEFAULT_SECURITY_GROUP_RULE_PARAMS[ip_version])
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600992 if ('remote_address_group_id' in kwargs and 'remote_ip_prefix' in
993 default_params):
994 default_params.pop('remote_ip_prefix')
Federico Ressi4c590d72018-10-10 14:01:08 +0200995 for key, value in default_params.items():
996 kwargs.setdefault(key, value)
997
998 client = client or cls.client
999 return client.create_security_group_rule(**kwargs)[
1000 'security_group_rule']
1001
1002 @classmethod
1003 def get_security_group(cls, name='default', client=None):
1004 client = client or cls.client
1005 security_groups = client.list_security_groups()['security_groups']
1006 for security_group in security_groups:
1007 if security_group['name'] == name:
1008 return security_group
1009 raise ValueError("No such security group named {!r}".format(name))
Chandan Kumarc125fd12017-11-15 19:41:01 +05301010
Federico Ressiab286e42018-06-19 09:52:10 +02001011 @classmethod
1012 def create_keypair(cls, client=None, name=None, **kwargs):
1013 client = client or cls.os_primary.keypairs_client
1014 name = name or data_utils.rand_name('keypair-test')
1015 keypair = client.create_keypair(name=name, **kwargs)['keypair']
1016
1017 # save client for later cleanup
1018 keypair['client'] = client
1019 cls.keypairs.append(keypair)
1020 return keypair
1021
1022 @classmethod
1023 def delete_keypair(cls, keypair, client=None):
1024 client = (client or keypair.get('client') or
1025 cls.os_primary.keypairs_client)
1026 client.delete_keypair(keypair_name=keypair['name'])
1027
Federico Ressi82e83e32018-07-03 14:19:55 +02001028 @classmethod
1029 def create_trunk(cls, port=None, subports=None, client=None, **kwargs):
1030 """Create network trunk
1031
1032 :param port: dictionary containing parent port ID (port['id'])
1033 :param client: client to be used for connecting to networking service
1034 :param **kwargs: extra parameters to be forwarded to network service
1035
1036 :returns: dictionary containing created trunk details
1037 """
1038 client = client or cls.client
1039
1040 if port:
1041 kwargs['port_id'] = port['id']
1042
1043 trunk = client.create_trunk(subports=subports, **kwargs)['trunk']
1044 # Save client reference for later deletion
1045 trunk['client'] = client
1046 cls.trunks.append(trunk)
1047 return trunk
1048
1049 @classmethod
Huifeng Le1c9f40b2018-11-07 01:14:21 +08001050 def delete_trunk(cls, trunk, client=None, detach_parent_port=True):
Federico Ressi82e83e32018-07-03 14:19:55 +02001051 """Delete network trunk
1052
1053 :param trunk: dictionary containing trunk ID (trunk['id'])
1054
1055 :param client: client to be used for connecting to networking service
1056 """
1057 client = client or trunk.get('client') or cls.client
1058 trunk.update(client.show_trunk(trunk['id'])['trunk'])
1059
1060 if not trunk['admin_state_up']:
1061 # Cannot touch trunk before admin_state_up is True
1062 client.update_trunk(trunk['id'], admin_state_up=True)
1063 if trunk['sub_ports']:
1064 # Removes trunk ports before deleting it
1065 cls._try_delete_resource(client.remove_subports, trunk['id'],
1066 trunk['sub_ports'])
1067
1068 # we have to detach the interface from the server before
1069 # the trunk can be deleted.
1070 parent_port = {'id': trunk['port_id']}
1071
1072 def is_parent_port_detached():
1073 parent_port.update(client.show_port(parent_port['id'])['port'])
1074 return not parent_port['device_id']
1075
Huifeng Le1c9f40b2018-11-07 01:14:21 +08001076 if detach_parent_port and not is_parent_port_detached():
Federico Ressi82e83e32018-07-03 14:19:55 +02001077 # this could probably happen when trunk is deleted and parent port
1078 # has been assigned to a VM that is still running. Here we are
1079 # assuming that device_id points to such VM.
1080 cls.os_primary.compute.InterfacesClient().delete_interface(
1081 parent_port['device_id'], parent_port['id'])
1082 utils.wait_until_true(is_parent_port_detached)
1083
1084 client.delete_trunk(trunk['id'])
1085
Harald Jensåsc9782fa2019-06-03 22:35:41 +02001086 @classmethod
1087 def create_conntrack_helper(cls, router_id, helper, protocol, port,
1088 client=None):
1089 """Create a conntrack helper
1090
1091 Create a conntrack helper and schedule it for later deletion. If a
1092 client is passed, then it is used for deleteing the CTH too.
1093
1094 :param router_id: The ID of the Neutron router associated to the
1095 conntrack helper.
1096
1097 :param helper: The conntrack helper module alias
1098
1099 :param protocol: The conntrack helper IP protocol used in the conntrack
1100 helper.
1101
1102 :param port: The conntrack helper IP protocol port number for the
1103 conntrack helper.
1104
1105 :param client: network client to be used for creating and cleaning up
1106 the conntrack helper.
1107 """
1108
1109 client = client or cls.client
1110
1111 cth = client.create_conntrack_helper(router_id, helper, protocol,
1112 port)['conntrack_helper']
1113
1114 # save ID of router associated with conntrack helper for final cleanup
1115 cth['router_id'] = router_id
1116
1117 # save client to be used later in cls.delete_conntrack_helper for final
1118 # cleanup
1119 cth['client'] = client
1120 cls.conntrack_helpers.append(cth)
1121 return cth
1122
1123 @classmethod
1124 def delete_conntrack_helper(cls, cth, client=None):
1125 """Delete conntrack helper
1126
1127 :param client: Client to be used
1128 If client is not given it will use the client used to create the
1129 conntrack helper, or cls.client if unknown.
1130 """
1131
1132 client = client or cth.get('client') or cls.client
1133 client.delete_conntrack_helper(cth['router_id'], cth['id'])
1134
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001135
1136class BaseAdminNetworkTest(BaseNetworkTest):
1137
1138 credentials = ['primary', 'admin']
1139
1140 @classmethod
1141 def setup_clients(cls):
1142 super(BaseAdminNetworkTest, cls).setup_clients()
fumihiko kakumaa216fc12017-07-14 10:43:29 +09001143 cls.admin_client = cls.os_admin.network_client
Jakub Libosvarf5758012017-08-15 13:45:30 +00001144 cls.identity_admin_client = cls.os_admin.projects_client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001145
1146 @classmethod
1147 def create_metering_label(cls, name, description):
1148 """Wrapper utility that returns a test metering label."""
1149 body = cls.admin_client.create_metering_label(
1150 description=description,
1151 name=data_utils.rand_name("metering-label"))
1152 metering_label = body['metering_label']
1153 cls.metering_labels.append(metering_label)
1154 return metering_label
1155
1156 @classmethod
1157 def create_metering_label_rule(cls, remote_ip_prefix, direction,
1158 metering_label_id):
1159 """Wrapper utility that returns a test metering label rule."""
1160 body = cls.admin_client.create_metering_label_rule(
1161 remote_ip_prefix=remote_ip_prefix, direction=direction,
1162 metering_label_id=metering_label_id)
1163 metering_label_rule = body['metering_label_rule']
1164 cls.metering_label_rules.append(metering_label_rule)
1165 return metering_label_rule
1166
1167 @classmethod
Kailun Qineaaf9782018-12-20 04:45:01 +08001168 def create_network_segment_range(cls, name, shared,
1169 project_id, network_type,
1170 physical_network, minimum,
1171 maximum):
1172 """Wrapper utility that returns a test network segment range."""
1173 network_segment_range_args = {'name': name,
1174 'shared': shared,
1175 'project_id': project_id,
1176 'network_type': network_type,
1177 'physical_network': physical_network,
1178 'minimum': minimum,
1179 'maximum': maximum}
1180 body = cls.admin_client.create_network_segment_range(
1181 **network_segment_range_args)
1182 network_segment_range = body['network_segment_range']
1183 cls.network_segment_ranges.append(network_segment_range)
1184 return network_segment_range
1185
1186 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001187 def create_flavor(cls, name, description, service_type):
1188 """Wrapper utility that returns a test flavor."""
1189 body = cls.admin_client.create_flavor(
1190 description=description, service_type=service_type,
1191 name=name)
1192 flavor = body['flavor']
1193 cls.flavors.append(flavor)
1194 return flavor
1195
1196 @classmethod
1197 def create_service_profile(cls, description, metainfo, driver):
1198 """Wrapper utility that returns a test service profile."""
1199 body = cls.admin_client.create_service_profile(
1200 driver=driver, metainfo=metainfo, description=description)
1201 service_profile = body['service_profile']
1202 cls.service_profiles.append(service_profile)
1203 return service_profile
1204
1205 @classmethod
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001206 def create_log(cls, name, description=None,
1207 resource_type='security_group', resource_id=None,
1208 target_id=None, event='ALL', enabled=True):
1209 """Wrapper utility that returns a test log object."""
1210 log_args = {'name': name,
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001211 'resource_type': resource_type,
1212 'resource_id': resource_id,
1213 'target_id': target_id,
1214 'event': event,
1215 'enabled': enabled}
Slawek Kaplonskid9fe3022021-08-11 15:25:16 +02001216 if description:
1217 log_args['description'] = description
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001218 body = cls.admin_client.create_log(**log_args)
1219 log_object = body['log']
1220 cls.log_objects.append(log_object)
1221 return log_object
1222
1223 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001224 def get_unused_ip(cls, net_id, ip_version=None):
Gary Kotton011345f2016-06-15 08:04:31 -07001225 """Get an unused ip address in a allocation pool of net"""
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001226 body = cls.admin_client.list_ports(network_id=net_id)
1227 ports = body['ports']
1228 used_ips = []
1229 for port in ports:
1230 used_ips.extend(
1231 [fixed_ip['ip_address'] for fixed_ip in port['fixed_ips']])
1232 body = cls.admin_client.list_subnets(network_id=net_id)
1233 subnets = body['subnets']
1234
1235 for subnet in subnets:
1236 if ip_version and subnet['ip_version'] != ip_version:
1237 continue
1238 cidr = subnet['cidr']
1239 allocation_pools = subnet['allocation_pools']
1240 iterators = []
1241 if allocation_pools:
1242 for allocation_pool in allocation_pools:
1243 iterators.append(netaddr.iter_iprange(
1244 allocation_pool['start'], allocation_pool['end']))
1245 else:
1246 net = netaddr.IPNetwork(cidr)
1247
1248 def _iterip():
1249 for ip in net:
1250 if ip not in (net.network, net.broadcast):
1251 yield ip
1252 iterators.append(iter(_iterip()))
1253
1254 for iterator in iterators:
1255 for ip in iterator:
1256 if str(ip) not in used_ips:
1257 return str(ip)
1258
1259 message = (
1260 "net(%s) has no usable IP address in allocation pools" % net_id)
1261 raise exceptions.InvalidConfiguration(message)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001262
Lajos Katona2f904652018-08-23 14:04:56 +02001263 @classmethod
1264 def create_provider_network(cls, physnet_name, start_segmentation_id,
1265 max_attempts=30):
1266 segmentation_id = start_segmentation_id
Lajos Katona7eb67252019-01-14 12:55:35 +01001267 for attempts in range(max_attempts):
Lajos Katona2f904652018-08-23 14:04:56 +02001268 try:
Lajos Katona7eb67252019-01-14 12:55:35 +01001269 return cls.create_network(
Lajos Katona2f904652018-08-23 14:04:56 +02001270 name=data_utils.rand_name('test_net'),
1271 shared=True,
1272 provider_network_type='vlan',
1273 provider_physical_network=physnet_name,
1274 provider_segmentation_id=segmentation_id)
Lajos Katona2f904652018-08-23 14:04:56 +02001275 except lib_exc.Conflict:
Lajos Katona2f904652018-08-23 14:04:56 +02001276 segmentation_id += 1
1277 if segmentation_id > 4095:
1278 raise lib_exc.TempestException(
1279 "No free segmentation id was found for provider "
1280 "network creation!")
1281 time.sleep(CONF.network.build_interval)
Lajos Katona7eb67252019-01-14 12:55:35 +01001282 LOG.exception("Failed to create provider network after "
1283 "%d attempts", max_attempts)
1284 raise lib_exc.TimeoutException
Lajos Katona2f904652018-08-23 14:04:56 +02001285
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001286
Sławek Kapłońskiff294062016-12-04 15:00:54 +00001287def require_qos_rule_type(rule_type):
1288 def decorator(f):
1289 @functools.wraps(f)
1290 def wrapper(self, *func_args, **func_kwargs):
1291 if rule_type not in self.get_supported_qos_rule_types():
1292 raise self.skipException(
1293 "%s rule type is required." % rule_type)
1294 return f(self, *func_args, **func_kwargs)
1295 return wrapper
1296 return decorator
1297
1298
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001299def _require_sorting(f):
1300 @functools.wraps(f)
1301 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301302 if not tutils.is_extension_enabled("sorting", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001303 self.skipTest('Sorting feature is required')
1304 return f(self, *args, **kwargs)
1305 return inner
1306
1307
1308def _require_pagination(f):
1309 @functools.wraps(f)
1310 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301311 if not tutils.is_extension_enabled("pagination", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001312 self.skipTest('Pagination feature is required')
1313 return f(self, *args, **kwargs)
1314 return inner
1315
1316
1317class BaseSearchCriteriaTest(BaseNetworkTest):
1318
1319 # This should be defined by subclasses to reflect resource name to test
1320 resource = None
1321
Armando Migliaccio57581c62016-07-01 10:13:19 -07001322 field = 'name'
1323
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001324 # NOTE(ihrachys): some names, like those starting with an underscore (_)
1325 # are sorted differently depending on whether the plugin implements native
1326 # sorting support, or not. So we avoid any such cases here, sticking to
1327 # alphanumeric. Also test a case when there are multiple resources with the
1328 # same name
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001329 resource_names = ('test1', 'abc1', 'test10', '123test') + ('test1',)
1330
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001331 force_tenant_isolation = True
1332
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001333 list_kwargs = {}
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001334
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001335 list_as_admin = False
1336
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001337 def assertSameOrder(self, original, actual):
1338 # gracefully handle iterators passed
1339 original = list(original)
1340 actual = list(actual)
1341 self.assertEqual(len(original), len(actual))
1342 for expected, res in zip(original, actual):
Armando Migliaccio57581c62016-07-01 10:13:19 -07001343 self.assertEqual(expected[self.field], res[self.field])
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001344
1345 @utils.classproperty
1346 def plural_name(self):
1347 return '%ss' % self.resource
1348
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001349 @property
1350 def list_client(self):
1351 return self.admin_client if self.list_as_admin else self.client
1352
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001353 def list_method(self, *args, **kwargs):
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001354 method = getattr(self.list_client, 'list_%s' % self.plural_name)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001355 kwargs.update(self.list_kwargs)
1356 return method(*args, **kwargs)
1357
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001358 def get_bare_url(self, url):
1359 base_url = self.client.base_url
zheng.yong74e760a2019-05-22 14:16:14 +08001360 base_url_normalized = utils.normalize_url(base_url)
1361 url_normalized = utils.normalize_url(url)
1362 self.assertTrue(url_normalized.startswith(base_url_normalized))
1363 return url_normalized[len(base_url_normalized):]
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001364
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001365 @classmethod
1366 def _extract_resources(cls, body):
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001367 return body[cls.plural_name]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001368
1369 def _test_list_sorts(self, direction):
1370 sort_args = {
1371 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001372 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001373 }
1374 body = self.list_method(**sort_args)
1375 resources = self._extract_resources(body)
1376 self.assertNotEmpty(
1377 resources, "%s list returned is empty" % self.resource)
Armando Migliaccio57581c62016-07-01 10:13:19 -07001378 retrieved_names = [res[self.field] for res in resources]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001379 expected = sorted(retrieved_names)
1380 if direction == constants.SORT_DIRECTION_DESC:
1381 expected = list(reversed(expected))
1382 self.assertEqual(expected, retrieved_names)
1383
1384 @_require_sorting
1385 def _test_list_sorts_asc(self):
1386 self._test_list_sorts(constants.SORT_DIRECTION_ASC)
1387
1388 @_require_sorting
1389 def _test_list_sorts_desc(self):
1390 self._test_list_sorts(constants.SORT_DIRECTION_DESC)
1391
1392 @_require_pagination
1393 def _test_list_pagination(self):
1394 for limit in range(1, len(self.resource_names) + 1):
1395 pagination_args = {
1396 'limit': limit,
1397 }
1398 body = self.list_method(**pagination_args)
1399 resources = self._extract_resources(body)
1400 self.assertEqual(limit, len(resources))
1401
1402 @_require_pagination
1403 def _test_list_no_pagination_limit_0(self):
1404 pagination_args = {
1405 'limit': 0,
1406 }
1407 body = self.list_method(**pagination_args)
1408 resources = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001409 self.assertGreaterEqual(len(resources), len(self.resource_names))
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001410
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001411 def _test_list_pagination_iteratively(self, lister):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001412 # first, collect all resources for later comparison
1413 sort_args = {
1414 'sort_dir': constants.SORT_DIRECTION_ASC,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001415 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001416 }
1417 body = self.list_method(**sort_args)
1418 expected_resources = self._extract_resources(body)
1419 self.assertNotEmpty(expected_resources)
1420
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001421 resources = lister(
1422 len(expected_resources), sort_args
1423 )
1424
1425 # finally, compare that the list retrieved in one go is identical to
1426 # the one containing pagination results
1427 self.assertSameOrder(expected_resources, resources)
1428
1429 def _list_all_with_marker(self, niterations, sort_args):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001430 # paginate resources one by one, using last fetched resource as a
1431 # marker
1432 resources = []
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001433 for i in range(niterations):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001434 pagination_args = sort_args.copy()
1435 pagination_args['limit'] = 1
1436 if resources:
1437 pagination_args['marker'] = resources[-1]['id']
1438 body = self.list_method(**pagination_args)
1439 resources_ = self._extract_resources(body)
1440 self.assertEqual(1, len(resources_))
1441 resources.extend(resources_)
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001442 return resources
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001443
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001444 @_require_pagination
1445 @_require_sorting
1446 def _test_list_pagination_with_marker(self):
1447 self._test_list_pagination_iteratively(self._list_all_with_marker)
1448
1449 def _list_all_with_hrefs(self, niterations, sort_args):
1450 # paginate resources one by one, using next href links
1451 resources = []
1452 prev_links = {}
1453
1454 for i in range(niterations):
1455 if prev_links:
1456 uri = self.get_bare_url(prev_links['next'])
1457 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001458 sort_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001459 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001460 self.plural_name, limit=1, **sort_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001461 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001462 self.plural_name, uri
1463 )
1464 resources_ = self._extract_resources(body)
1465 self.assertEqual(1, len(resources_))
1466 resources.extend(resources_)
1467
1468 # The last element is empty and does not contain 'next' link
1469 uri = self.get_bare_url(prev_links['next'])
1470 prev_links, body = self.client.get_uri_with_links(
1471 self.plural_name, uri
1472 )
1473 self.assertNotIn('next', prev_links)
1474
1475 # Now walk backwards and compare results
1476 resources2 = []
1477 for i in range(niterations):
1478 uri = self.get_bare_url(prev_links['previous'])
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001479 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001480 self.plural_name, uri
1481 )
1482 resources_ = self._extract_resources(body)
1483 self.assertEqual(1, len(resources_))
1484 resources2.extend(resources_)
1485
1486 self.assertSameOrder(resources, reversed(resources2))
1487
1488 return resources
1489
1490 @_require_pagination
1491 @_require_sorting
1492 def _test_list_pagination_with_href_links(self):
1493 self._test_list_pagination_iteratively(self._list_all_with_hrefs)
1494
1495 @_require_pagination
1496 @_require_sorting
1497 def _test_list_pagination_page_reverse_with_href_links(
1498 self, direction=constants.SORT_DIRECTION_ASC):
1499 pagination_args = {
1500 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001501 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001502 }
1503 body = self.list_method(**pagination_args)
1504 expected_resources = self._extract_resources(body)
1505
1506 page_size = 2
1507 pagination_args['limit'] = page_size
1508
1509 prev_links = {}
1510 resources = []
1511 num_resources = len(expected_resources)
1512 niterations = int(math.ceil(float(num_resources) / page_size))
1513 for i in range(niterations):
1514 if prev_links:
1515 uri = self.get_bare_url(prev_links['previous'])
1516 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001517 pagination_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001518 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001519 self.plural_name, page_reverse=True, **pagination_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001520 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001521 self.plural_name, uri
1522 )
1523 resources_ = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001524 self.assertGreaterEqual(page_size, len(resources_))
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001525 resources.extend(reversed(resources_))
1526
1527 self.assertSameOrder(expected_resources, reversed(resources))
1528
1529 @_require_pagination
1530 @_require_sorting
1531 def _test_list_pagination_page_reverse_asc(self):
1532 self._test_list_pagination_page_reverse(
1533 direction=constants.SORT_DIRECTION_ASC)
1534
1535 @_require_pagination
1536 @_require_sorting
1537 def _test_list_pagination_page_reverse_desc(self):
1538 self._test_list_pagination_page_reverse(
1539 direction=constants.SORT_DIRECTION_DESC)
1540
1541 def _test_list_pagination_page_reverse(self, direction):
1542 pagination_args = {
1543 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001544 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001545 'limit': 3,
1546 }
1547 body = self.list_method(**pagination_args)
1548 expected_resources = self._extract_resources(body)
1549
1550 pagination_args['limit'] -= 1
1551 pagination_args['marker'] = expected_resources[-1]['id']
1552 pagination_args['page_reverse'] = True
1553 body = self.list_method(**pagination_args)
1554
1555 self.assertSameOrder(
1556 # the last entry is not included in 2nd result when used as a
1557 # marker
1558 expected_resources[:-1],
1559 self._extract_resources(body))
Victor Morales1be97b42016-09-05 08:50:06 -05001560
Hongbin Lu54f55922018-07-12 19:05:39 +00001561 @tutils.requires_ext(extension="filter-validation", service="network")
1562 def _test_list_validation_filters(
1563 self, validation_args, filter_is_valid=True):
1564 if not filter_is_valid:
1565 self.assertRaises(lib_exc.BadRequest, self.list_method,
1566 **validation_args)
1567 else:
1568 body = self.list_method(**validation_args)
1569 resources = self._extract_resources(body)
1570 for resource in resources:
1571 self.assertIn(resource['name'], self.resource_names)