blob: 8ee0015b2371a402c33a1aa8ae61df87a5b68452 [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 Saienko4c3bc2f2021-10-22 14:34:41 +0300109 # NOTE(vsaienko): when using static accounts we need
110 # to fill *_id information like project_id, user_id
111 # by authenticating in keystone
112 cls.client.auth_provider.get_token()
113
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000114 @classmethod
115 def resource_setup(cls):
116 super(BaseNetworkTest, cls).resource_setup()
117
118 cls.networks = []
Miguel Lavalle124378b2016-09-21 16:41:47 -0500119 cls.admin_networks = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000120 cls.subnets = []
Kevin Bentonba3651c2017-09-01 17:13:01 -0700121 cls.admin_subnets = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000122 cls.ports = []
123 cls.routers = []
124 cls.floating_ips = []
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200125 cls.port_forwardings = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000126 cls.metering_labels = []
127 cls.service_profiles = []
128 cls.flavors = []
129 cls.metering_label_rules = []
130 cls.qos_rules = []
131 cls.qos_policies = []
132 cls.ethertype = "IPv" + str(cls._ip_version)
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600133 cls.address_groups = []
134 cls.admin_address_groups = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000135 cls.address_scopes = []
136 cls.admin_address_scopes = []
137 cls.subnetpools = []
138 cls.admin_subnetpools = []
Itzik Brownbac51dc2016-10-31 12:25:04 +0000139 cls.security_groups = []
Dongcan Ye2de722e2018-07-04 11:01:37 +0000140 cls.admin_security_groups = []
Chandan Kumarc125fd12017-11-15 19:41:01 +0530141 cls.projects = []
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700142 cls.log_objects = []
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200143 cls.reserved_subnet_cidrs = set()
Federico Ressiab286e42018-06-19 09:52:10 +0200144 cls.keypairs = []
Federico Ressi82e83e32018-07-03 14:19:55 +0200145 cls.trunks = []
Kailun Qineaaf9782018-12-20 04:45:01 +0800146 cls.network_segment_ranges = []
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200147 cls.conntrack_helpers = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000148
149 @classmethod
yangjianfeng23e40c22020-11-22 08:42:18 +0000150 def reserve_external_subnet_cidrs(cls):
151 client = cls.os_admin.network_client
152 ext_nets = client.list_networks(
153 **{"router:external": True})['networks']
154 for ext_net in ext_nets:
155 ext_subnets = client.list_subnets(
156 network_id=ext_net['id'])['subnets']
157 for ext_subnet in ext_subnets:
158 cls.reserve_subnet_cidr(ext_subnet['cidr'])
159
160 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000161 def resource_cleanup(cls):
162 if CONF.service_available.neutron:
Federico Ressi82e83e32018-07-03 14:19:55 +0200163 # Clean up trunks
164 for trunk in cls.trunks:
165 cls._try_delete_resource(cls.delete_trunk, trunk)
166
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200167 # Clean up port forwardings
168 for pf in cls.port_forwardings:
169 cls._try_delete_resource(cls.delete_port_forwarding, pf)
170
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000171 # Clean up floating IPs
172 for floating_ip in cls.floating_ips:
Federico Ressia69dcd52018-07-06 09:45:34 +0200173 cls._try_delete_resource(cls.delete_floatingip, floating_ip)
174
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200175 # Clean up conntrack helpers
176 for cth in cls.conntrack_helpers:
177 cls._try_delete_resource(cls.delete_conntrack_helper, cth)
178
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000179 # Clean up routers
180 for router in cls.routers:
181 cls._try_delete_resource(cls.delete_router,
182 router)
183 # Clean up metering label rules
184 for metering_label_rule in cls.metering_label_rules:
185 cls._try_delete_resource(
186 cls.admin_client.delete_metering_label_rule,
187 metering_label_rule['id'])
188 # Clean up metering labels
189 for metering_label in cls.metering_labels:
190 cls._try_delete_resource(
191 cls.admin_client.delete_metering_label,
192 metering_label['id'])
193 # Clean up flavors
194 for flavor in cls.flavors:
195 cls._try_delete_resource(
196 cls.admin_client.delete_flavor,
197 flavor['id'])
198 # Clean up service profiles
199 for service_profile in cls.service_profiles:
200 cls._try_delete_resource(
201 cls.admin_client.delete_service_profile,
202 service_profile['id'])
203 # Clean up ports
204 for port in cls.ports:
205 cls._try_delete_resource(cls.client.delete_port,
206 port['id'])
207 # Clean up subnets
208 for subnet in cls.subnets:
209 cls._try_delete_resource(cls.client.delete_subnet,
210 subnet['id'])
Kevin Bentonba3651c2017-09-01 17:13:01 -0700211 # Clean up admin subnets
212 for subnet in cls.admin_subnets:
213 cls._try_delete_resource(cls.admin_client.delete_subnet,
214 subnet['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000215 # Clean up networks
216 for network in cls.networks:
Federico Ressi61b564e2018-07-06 08:10:31 +0200217 cls._try_delete_resource(cls.delete_network, network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000218
Miguel Lavalle124378b2016-09-21 16:41:47 -0500219 # Clean up admin networks
220 for network in cls.admin_networks:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000221 cls._try_delete_resource(cls.admin_client.delete_network,
222 network['id'])
223
Itzik Brownbac51dc2016-10-31 12:25:04 +0000224 # Clean up security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200225 for security_group in cls.security_groups:
226 cls._try_delete_resource(cls.delete_security_group,
227 security_group)
Itzik Brownbac51dc2016-10-31 12:25:04 +0000228
Dongcan Ye2de722e2018-07-04 11:01:37 +0000229 # Clean up admin security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200230 for security_group in cls.admin_security_groups:
231 cls._try_delete_resource(cls.delete_security_group,
232 security_group,
233 client=cls.admin_client)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000234
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000235 for subnetpool in cls.subnetpools:
236 cls._try_delete_resource(cls.client.delete_subnetpool,
237 subnetpool['id'])
238
239 for subnetpool in cls.admin_subnetpools:
240 cls._try_delete_resource(cls.admin_client.delete_subnetpool,
241 subnetpool['id'])
242
243 for address_scope in cls.address_scopes:
244 cls._try_delete_resource(cls.client.delete_address_scope,
245 address_scope['id'])
246
247 for address_scope in cls.admin_address_scopes:
248 cls._try_delete_resource(
249 cls.admin_client.delete_address_scope,
250 address_scope['id'])
251
Chandan Kumarc125fd12017-11-15 19:41:01 +0530252 for project in cls.projects:
253 cls._try_delete_resource(
254 cls.identity_admin_client.delete_project,
255 project['id'])
256
Sławek Kapłońskie100c4d2017-08-23 21:18:34 +0000257 # Clean up QoS rules
258 for qos_rule in cls.qos_rules:
259 cls._try_delete_resource(cls.admin_client.delete_qos_rule,
260 qos_rule['id'])
261 # Clean up QoS policies
262 # as all networks and ports are already removed, QoS policies
263 # shouldn't be "in use"
264 for qos_policy in cls.qos_policies:
265 cls._try_delete_resource(cls.admin_client.delete_qos_policy,
266 qos_policy['id'])
267
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700268 # Clean up log_objects
269 for log_object in cls.log_objects:
270 cls._try_delete_resource(cls.admin_client.delete_log,
271 log_object['id'])
272
Federico Ressiab286e42018-06-19 09:52:10 +0200273 for keypair in cls.keypairs:
274 cls._try_delete_resource(cls.delete_keypair, keypair)
275
Kailun Qineaaf9782018-12-20 04:45:01 +0800276 # Clean up network_segment_ranges
277 for network_segment_range in cls.network_segment_ranges:
278 cls._try_delete_resource(
279 cls.admin_client.delete_network_segment_range,
280 network_segment_range['id'])
281
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000282 super(BaseNetworkTest, cls).resource_cleanup()
283
284 @classmethod
285 def _try_delete_resource(cls, delete_callable, *args, **kwargs):
286 """Cleanup resources in case of test-failure
287
288 Some resources are explicitly deleted by the test.
289 If the test failed to delete a resource, this method will execute
290 the appropriate delete methods. Otherwise, the method ignores NotFound
291 exceptions thrown for resources that were correctly deleted by the
292 test.
293
294 :param delete_callable: delete method
295 :param args: arguments for delete method
296 :param kwargs: keyword arguments for delete method
297 """
298 try:
299 delete_callable(*args, **kwargs)
300 # if resource is not found, this means it was deleted in the test
301 except lib_exc.NotFound:
302 pass
303
304 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200305 def create_network(cls, network_name=None, client=None, external=None,
306 shared=None, provider_network_type=None,
307 provider_physical_network=None,
308 provider_segmentation_id=None, **kwargs):
309 """Create a network.
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000310
Federico Ressi61b564e2018-07-06 08:10:31 +0200311 When client is not provider and admin_client is attribute is not None
312 (for example when using BaseAdminNetworkTest base class) and using any
313 of the convenience parameters (external, shared, provider_network_type,
314 provider_physical_network and provider_segmentation_id) it silently
315 uses admin_client. If the network is not shared then it uses the same
316 project_id as regular client.
317
318 :param network_name: Human-readable name of the network
319
320 :param client: client to be used for connecting to network service
321
322 :param external: indicates whether the network has an external routing
323 facility that's not managed by the networking service.
324
325 :param shared: indicates whether this resource is shared across all
326 projects. By default, only administrative users can change this value.
327 If True and admin_client attribute is not None, then the network is
328 created under administrative project.
329
330 :param provider_network_type: the type of physical network that this
331 network should be mapped to. For example, 'flat', 'vlan', 'vxlan', or
332 'gre'. Valid values depend on a networking back-end.
333
334 :param provider_physical_network: the physical network where this
335 network should be implemented. The Networking API v2.0 does not provide
336 a way to list available physical networks. For example, the Open
337 vSwitch plug-in configuration file defines a symbolic name that maps to
338 specific bridges on each compute host.
339
340 :param provider_segmentation_id: The ID of the isolated segment on the
341 physical network. The network_type attribute defines the segmentation
342 model. For example, if the network_type value is 'vlan', this ID is a
343 vlan identifier. If the network_type value is 'gre', this ID is a gre
344 key.
345
346 :param **kwargs: extra parameters to be forwarded to network service
347 """
348
349 name = (network_name or kwargs.pop('name', None) or
350 data_utils.rand_name('test-network-'))
351
352 # translate convenience parameters
353 admin_client_required = False
354 if provider_network_type:
355 admin_client_required = True
356 kwargs['provider:network_type'] = provider_network_type
357 if provider_physical_network:
358 admin_client_required = True
359 kwargs['provider:physical_network'] = provider_physical_network
360 if provider_segmentation_id:
361 admin_client_required = True
362 kwargs['provider:segmentation_id'] = provider_segmentation_id
363 if external is not None:
364 admin_client_required = True
365 kwargs['router:external'] = bool(external)
366 if shared is not None:
367 admin_client_required = True
368 kwargs['shared'] = bool(shared)
369
370 if not client:
371 if admin_client_required and cls.admin_client:
372 # For convenience silently switch to admin client
373 client = cls.admin_client
374 if not shared:
375 # Keep this network visible from current project
376 project_id = (kwargs.get('project_id') or
377 kwargs.get('tenant_id') or
378 cls.client.tenant_id)
379 kwargs.update(project_id=project_id, tenant_id=project_id)
380 else:
381 # Use default client
382 client = cls.client
383
384 network = client.create_network(name=name, **kwargs)['network']
385 network['client'] = client
386 cls.networks.append(network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000387 return network
388
389 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200390 def delete_network(cls, network, client=None):
391 client = client or network.get('client') or cls.client
392 client.delete_network(network['id'])
393
394 @classmethod
395 def create_shared_network(cls, network_name=None, **kwargs):
396 return cls.create_network(name=network_name, shared=True, **kwargs)
Miguel Lavalle124378b2016-09-21 16:41:47 -0500397
398 @classmethod
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200399 def create_subnet(cls, network, gateway='', cidr=None, mask_bits=None,
Federico Ressi98f20ec2018-05-11 06:09:49 +0200400 ip_version=None, client=None, reserve_cidr=True,
401 **kwargs):
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200402 """Wrapper utility that returns a test subnet.
403
404 Convenient wrapper for client.create_subnet method. It reserves and
405 allocates CIDRs to avoid creating overlapping subnets.
406
407 :param network: network where to create the subnet
408 network['id'] must contain the ID of the network
409
410 :param gateway: gateway IP address
411 It can be a str or a netaddr.IPAddress
412 If gateway is not given, then it will use default address for
413 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 +0200414 if gateway is given as None then no gateway will be assigned
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200415
416 :param cidr: CIDR of the subnet to create
417 It can be either None, a str or a netaddr.IPNetwork instance
418
419 :param mask_bits: CIDR prefix length
420 It can be either None or a numeric value.
421 If cidr parameter is given then mask_bits is used to determinate a
422 sequence of valid CIDR to use as generated.
423 Please see netaddr.IPNetwork.subnet method documentation[1]
424
425 :param ip_version: ip version of generated subnet CIDRs
426 It can be None, IP_VERSION_4 or IP_VERSION_6
427 It has to match given either given CIDR and gateway
428
429 :param ip_version: numeric value (either IP_VERSION_4 or IP_VERSION_6)
430 this value must match CIDR and gateway IP versions if any of them is
431 given
432
433 :param client: client to be used to connect to network service
434
Federico Ressi98f20ec2018-05-11 06:09:49 +0200435 :param reserve_cidr: if True then it reserves assigned CIDR to avoid
436 using the same CIDR for further subnets in the scope of the same
437 test case class
438
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200439 :param **kwargs: optional parameters to be forwarded to wrapped method
440
441 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
442 """
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000443
444 # allow tests to use admin client
445 if not client:
446 client = cls.client
447
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200448 if gateway:
449 gateway_ip = netaddr.IPAddress(gateway)
450 if ip_version:
451 if ip_version != gateway_ip.version:
452 raise ValueError(
453 "Gateway IP version doesn't match IP version")
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000454 else:
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200455 ip_version = gateway_ip.version
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200456 else:
457 ip_version = ip_version or cls._ip_version
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200458
459 for subnet_cidr in cls.get_subnet_cidrs(
460 ip_version=ip_version, cidr=cidr, mask_bits=mask_bits):
Federico Ressi98f20ec2018-05-11 06:09:49 +0200461 if gateway is not None:
462 kwargs['gateway_ip'] = str(gateway or (subnet_cidr.ip + 1))
Slawek Kaplonski21f53422018-11-02 16:02:09 +0100463 else:
464 kwargs['gateway_ip'] = None
Federico Ressi98f20ec2018-05-11 06:09:49 +0200465 try:
466 body = client.create_subnet(
467 network_id=network['id'],
468 cidr=str(subnet_cidr),
469 ip_version=subnet_cidr.version,
470 **kwargs)
471 break
472 except lib_exc.BadRequest as e:
473 if 'overlaps with another subnet' not in str(e):
474 raise
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000475 else:
476 message = 'Available CIDR for subnet creation could not be found'
477 raise ValueError(message)
478 subnet = body['subnet']
Kevin Bentonba3651c2017-09-01 17:13:01 -0700479 if client is cls.client:
480 cls.subnets.append(subnet)
481 else:
482 cls.admin_subnets.append(subnet)
Federico Ressi98f20ec2018-05-11 06:09:49 +0200483 if reserve_cidr:
484 cls.reserve_subnet_cidr(subnet_cidr)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000485 return subnet
486
487 @classmethod
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200488 def reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
489 """Reserve given subnet CIDR making sure it is not used by create_subnet
490
491 :param addr: the CIDR address to be reserved
492 It can be a str or netaddr.IPNetwork instance
493
494 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
495 parameters
496 """
497
498 if not cls.try_reserve_subnet_cidr(addr, **ipnetwork_kwargs):
Bernard Cafarellic3bec862020-09-10 13:59:49 +0200499 raise ValueError('Subnet CIDR already reserved: {0!r}'.format(
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200500 addr))
501
502 @classmethod
503 def try_reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
504 """Reserve given subnet CIDR if it hasn't been reserved before
505
506 :param addr: the CIDR address to be reserved
507 It can be a str or netaddr.IPNetwork instance
508
509 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
510 parameters
511
512 :return: True if it wasn't reserved before, False elsewhere.
513 """
514
515 subnet_cidr = netaddr.IPNetwork(addr, **ipnetwork_kwargs)
516 if subnet_cidr in cls.reserved_subnet_cidrs:
517 return False
518 else:
519 cls.reserved_subnet_cidrs.add(subnet_cidr)
520 return True
521
522 @classmethod
523 def get_subnet_cidrs(
524 cls, cidr=None, mask_bits=None, ip_version=None):
525 """Iterate over a sequence of unused subnet CIDR for IP version
526
527 :param cidr: CIDR of the subnet to create
528 It can be either None, a str or a netaddr.IPNetwork instance
529
530 :param mask_bits: CIDR prefix length
531 It can be either None or a numeric value.
532 If cidr parameter is given then mask_bits is used to determinate a
533 sequence of valid CIDR to use as generated.
534 Please see netaddr.IPNetwork.subnet method documentation[1]
535
536 :param ip_version: ip version of generated subnet CIDRs
537 It can be None, IP_VERSION_4 or IP_VERSION_6
538 It has to match given CIDR if given
539
540 :return: iterator over reserved CIDRs of type netaddr.IPNetwork
541
542 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
543 """
544
545 if cidr:
546 # Generate subnet CIDRs starting from given CIDR
547 # checking it is of requested IP version
548 cidr = netaddr.IPNetwork(cidr, version=ip_version)
549 else:
550 # Generate subnet CIDRs starting from configured values
551 ip_version = ip_version or cls._ip_version
552 if ip_version == const.IP_VERSION_4:
553 mask_bits = mask_bits or config.safe_get_config_value(
554 'network', 'project_network_mask_bits')
555 cidr = netaddr.IPNetwork(config.safe_get_config_value(
556 'network', 'project_network_cidr'))
557 elif ip_version == const.IP_VERSION_6:
558 mask_bits = config.safe_get_config_value(
559 'network', 'project_network_v6_mask_bits')
560 cidr = netaddr.IPNetwork(config.safe_get_config_value(
561 'network', 'project_network_v6_cidr'))
562 else:
563 raise ValueError('Invalid IP version: {!r}'.format(ip_version))
564
565 if mask_bits:
566 subnet_cidrs = cidr.subnet(mask_bits)
567 else:
568 subnet_cidrs = iter([cidr])
569
570 for subnet_cidr in subnet_cidrs:
571 if subnet_cidr not in cls.reserved_subnet_cidrs:
572 yield subnet_cidr
573
574 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000575 def create_port(cls, network, **kwargs):
576 """Wrapper utility that returns a test port."""
Edan Davidd75e48e2018-01-03 02:49:52 -0500577 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
578 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
Glenn Van de Water5d9b1402020-09-16 15:14:14 +0200579 if CONF.network.port_profile and 'binding:profile' not in kwargs:
580 kwargs['binding:profile'] = CONF.network.port_profile
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000581 body = cls.client.create_port(network_id=network['id'],
582 **kwargs)
583 port = body['port']
584 cls.ports.append(port)
585 return port
586
587 @classmethod
588 def update_port(cls, port, **kwargs):
589 """Wrapper utility that updates a test port."""
590 body = cls.client.update_port(port['id'],
591 **kwargs)
592 return body['port']
593
594 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300595 def _create_router_with_client(
596 cls, client, router_name=None, admin_state_up=False,
597 external_network_id=None, enable_snat=None, **kwargs
598 ):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000599 ext_gw_info = {}
600 if external_network_id:
601 ext_gw_info['network_id'] = external_network_id
YAMAMOTO Takashi9bd4f972017-06-20 12:49:30 +0900602 if enable_snat is not None:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000603 ext_gw_info['enable_snat'] = enable_snat
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300604 body = client.create_router(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000605 router_name, external_gateway_info=ext_gw_info,
606 admin_state_up=admin_state_up, **kwargs)
607 router = body['router']
608 cls.routers.append(router)
609 return router
610
611 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300612 def create_router(cls, *args, **kwargs):
613 return cls._create_router_with_client(cls.client, *args, **kwargs)
614
615 @classmethod
616 def create_admin_router(cls, *args, **kwargs):
rajat294495c042017-06-28 15:37:16 +0530617 return cls._create_router_with_client(cls.os_admin.network_client,
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300618 *args, **kwargs)
619
620 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200621 def create_floatingip(cls, external_network_id=None, port=None,
622 client=None, **kwargs):
623 """Creates a floating IP.
624
625 Create a floating IP and schedule it for later deletion.
626 If a client is passed, then it is used for deleting the IP too.
627
628 :param external_network_id: network ID where to create
629 By default this is 'CONF.network.public_network_id'.
630
631 :param port: port to bind floating IP to
632 This is translated to 'port_id=port['id']'
633 By default it is None.
634
635 :param client: network client to be used for creating and cleaning up
636 the floating IP.
637
638 :param **kwargs: additional creation parameters to be forwarded to
639 networking server.
640 """
641
642 client = client or cls.client
643 external_network_id = (external_network_id or
644 cls.external_network_id)
645
646 if port:
Federico Ressi47f6ae42018-09-24 16:19:14 +0200647 port_id = kwargs.setdefault('port_id', port['id'])
648 if port_id != port['id']:
649 message = "Port ID specified twice: {!s} != {!s}".format(
650 port_id, port['id'])
651 raise ValueError(message)
Federico Ressia69dcd52018-07-06 09:45:34 +0200652
653 fip = client.create_floatingip(external_network_id,
654 **kwargs)['floatingip']
655
656 # save client to be used later in cls.delete_floatingip
657 # for final cleanup
658 fip['client'] = client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000659 cls.floating_ips.append(fip)
660 return fip
661
662 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200663 def delete_floatingip(cls, floating_ip, client=None):
664 """Delete floating IP
665
666 :param client: Client to be used
667 If client is not given it will use the client used to create
668 the floating IP, or cls.client if unknown.
669 """
670
671 client = client or floating_ip.get('client') or cls.client
672 client.delete_floatingip(floating_ip['id'])
673
674 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200675 def create_port_forwarding(cls, fip_id, internal_port_id,
676 internal_port, external_port,
677 internal_ip_address=None, protocol="tcp",
678 client=None):
679 """Creates a port forwarding.
680
681 Create a port forwarding and schedule it for later deletion.
682 If a client is passed, then it is used for deleting the PF too.
683
684 :param fip_id: The ID of the floating IP address.
685
686 :param internal_port_id: The ID of the Neutron port associated to
687 the floating IP port forwarding.
688
689 :param internal_port: The TCP/UDP/other protocol port number of the
690 Neutron port fixed IP address associated to the floating ip
691 port forwarding.
692
693 :param external_port: The TCP/UDP/other protocol port number of
694 the port forwarding floating IP address.
695
696 :param internal_ip_address: The fixed IPv4 address of the Neutron
697 port associated to the floating IP port forwarding.
698
699 :param protocol: The IP protocol used in the floating IP port
700 forwarding.
701
702 :param client: network client to be used for creating and cleaning up
703 the floating IP port forwarding.
704 """
705
706 client = client or cls.client
707
708 pf = client.create_port_forwarding(
709 fip_id, internal_port_id, internal_port, external_port,
710 internal_ip_address, protocol)['port_forwarding']
711
712 # save ID of floating IP associated with port forwarding for final
713 # cleanup
714 pf['floatingip_id'] = fip_id
715
716 # save client to be used later in cls.delete_port_forwarding
717 # for final cleanup
718 pf['client'] = client
719 cls.port_forwardings.append(pf)
720 return pf
721
722 @classmethod
Flavio Fernandesa1952c62020-10-02 06:39:08 -0400723 def update_port_forwarding(cls, fip_id, pf_id, client=None, **kwargs):
724 """Wrapper utility for update_port_forwarding."""
725 client = client or cls.client
726 return client.update_port_forwarding(fip_id, pf_id, **kwargs)
727
728 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200729 def delete_port_forwarding(cls, pf, client=None):
730 """Delete port forwarding
731
732 :param client: Client to be used
733 If client is not given it will use the client used to create
734 the port forwarding, or cls.client if unknown.
735 """
736
737 client = client or pf.get('client') or cls.client
738 client.delete_port_forwarding(pf['floatingip_id'], pf['id'])
739
740 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000741 def create_router_interface(cls, router_id, subnet_id):
742 """Wrapper utility that returns a router interface."""
743 interface = cls.client.add_router_interface_with_subnet_id(
744 router_id, subnet_id)
745 return interface
746
747 @classmethod
Bence Romsics46bd3af2019-09-13 10:52:41 +0200748 def add_extra_routes_atomic(cls, *args, **kwargs):
749 return cls.client.add_extra_routes_atomic(*args, **kwargs)
750
751 @classmethod
752 def remove_extra_routes_atomic(cls, *args, **kwargs):
753 return cls.client.remove_extra_routes_atomic(*args, **kwargs)
754
755 @classmethod
Sławek Kapłońskiff294062016-12-04 15:00:54 +0000756 def get_supported_qos_rule_types(cls):
757 body = cls.client.list_qos_rule_types()
758 return [rule_type['type'] for rule_type in body['rule_types']]
759
760 @classmethod
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +0200761 def create_qos_policy(cls, name, description=None, shared=False,
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000762 project_id=None, is_default=False):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000763 """Wrapper utility that returns a test QoS policy."""
764 body = cls.admin_client.create_qos_policy(
Rodolfo Alonso Hernandeze2d062f2020-01-14 17:11:42 +0000765 name, description, shared, project_id, is_default)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000766 qos_policy = body['policy']
767 cls.qos_policies.append(qos_policy)
768 return qos_policy
769
770 @classmethod
Sławek Kapłoński153f3452017-03-24 22:04:53 +0000771 def create_qos_bandwidth_limit_rule(cls, policy_id, max_kbps,
772 max_burst_kbps,
Chandan Kumarc125fd12017-11-15 19:41:01 +0530773 direction=const.EGRESS_DIRECTION):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000774 """Wrapper utility that returns a test QoS bandwidth limit rule."""
775 body = cls.admin_client.create_bandwidth_limit_rule(
Sławek Kapłoński153f3452017-03-24 22:04:53 +0000776 policy_id, max_kbps, max_burst_kbps, direction)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000777 qos_rule = body['bandwidth_limit_rule']
778 cls.qos_rules.append(qos_rule)
779 return qos_rule
780
781 @classmethod
Lajos Katona2f904652018-08-23 14:04:56 +0200782 def create_qos_minimum_bandwidth_rule(cls, policy_id, min_kbps,
783 direction=const.EGRESS_DIRECTION):
784 """Wrapper utility that creates and returns a QoS min bw rule."""
785 body = cls.admin_client.create_minimum_bandwidth_rule(
786 policy_id, direction, min_kbps)
787 qos_rule = body['minimum_bandwidth_rule']
788 cls.qos_rules.append(qos_rule)
789 return qos_rule
790
791 @classmethod
Jakub Libosvar83704832017-12-06 16:02:28 +0000792 def delete_router(cls, router, client=None):
793 client = client or cls.client
Aditya Vaja49819a72018-11-26 14:20:10 -0800794 if 'routes' in router:
795 client.remove_router_extra_routes(router['id'])
Jakub Libosvar83704832017-12-06 16:02:28 +0000796 body = client.list_router_interfaces(router['id'])
Chandan Kumarc125fd12017-11-15 19:41:01 +0530797 interfaces = [port for port in body['ports']
798 if port['device_owner'] in const.ROUTER_INTERFACE_OWNERS]
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000799 for i in interfaces:
800 try:
Jakub Libosvar83704832017-12-06 16:02:28 +0000801 client.remove_router_interface_with_subnet_id(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000802 router['id'], i['fixed_ips'][0]['subnet_id'])
803 except lib_exc.NotFound:
804 pass
Jakub Libosvar83704832017-12-06 16:02:28 +0000805 client.delete_router(router['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000806
807 @classmethod
808 def create_address_scope(cls, name, is_admin=False, **kwargs):
809 if is_admin:
810 body = cls.admin_client.create_address_scope(name=name, **kwargs)
811 cls.admin_address_scopes.append(body['address_scope'])
812 else:
813 body = cls.client.create_address_scope(name=name, **kwargs)
814 cls.address_scopes.append(body['address_scope'])
815 return body['address_scope']
816
817 @classmethod
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200818 def create_subnetpool(cls, name, is_admin=False, client=None, **kwargs):
819 if client is None:
820 client = cls.admin_client if is_admin else cls.client
821
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000822 if is_admin:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200823 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000824 cls.admin_subnetpools.append(body['subnetpool'])
825 else:
Igor Malinovskiyb80f1d02020-03-06 13:39:52 +0200826 body = client.create_subnetpool(name, **kwargs)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000827 cls.subnetpools.append(body['subnetpool'])
828 return body['subnetpool']
829
Chandan Kumarc125fd12017-11-15 19:41:01 +0530830 @classmethod
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600831 def create_address_group(cls, name, is_admin=False, **kwargs):
832 if is_admin:
833 body = cls.admin_client.create_address_group(name=name, **kwargs)
834 cls.admin_address_groups.append(body['address_group'])
835 else:
836 body = cls.client.create_address_group(name=name, **kwargs)
837 cls.address_groups.append(body['address_group'])
838 return body['address_group']
839
840 @classmethod
Chandan Kumarc125fd12017-11-15 19:41:01 +0530841 def create_project(cls, name=None, description=None):
842 test_project = name or data_utils.rand_name('test_project_')
843 test_description = description or data_utils.rand_name('desc_')
844 project = cls.identity_admin_client.create_project(
845 name=test_project,
846 description=test_description)['project']
847 cls.projects.append(project)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000848 # Create a project will create a default security group.
Dongcan Ye2de722e2018-07-04 11:01:37 +0000849 sgs_list = cls.admin_client.list_security_groups(
850 tenant_id=project['id'])['security_groups']
Federico Ressi4c590d72018-10-10 14:01:08 +0200851 for security_group in sgs_list:
852 # Make sure delete_security_group method will use
853 # the admin client for this group
854 security_group['client'] = cls.admin_client
855 cls.security_groups.append(security_group)
Chandan Kumarc125fd12017-11-15 19:41:01 +0530856 return project
857
858 @classmethod
Federico Ressi4c590d72018-10-10 14:01:08 +0200859 def create_security_group(cls, name=None, project=None, client=None,
860 **kwargs):
861 if project:
862 client = client or cls.admin_client
863 project_id = kwargs.setdefault('project_id', project['id'])
864 tenant_id = kwargs.setdefault('tenant_id', project['id'])
865 if project_id != project['id'] or tenant_id != project['id']:
866 raise ValueError('Project ID specified multiple times')
867 else:
868 client = client or cls.client
869
870 name = name or data_utils.rand_name(cls.__name__)
871 security_group = client.create_security_group(name=name, **kwargs)[
872 'security_group']
873 security_group['client'] = client
874 cls.security_groups.append(security_group)
875 return security_group
876
877 @classmethod
878 def delete_security_group(cls, security_group, client=None):
879 client = client or security_group.get('client') or cls.client
880 client.delete_security_group(security_group['id'])
881
882 @classmethod
883 def create_security_group_rule(cls, security_group=None, project=None,
884 client=None, ip_version=None, **kwargs):
885 if project:
886 client = client or cls.admin_client
887 project_id = kwargs.setdefault('project_id', project['id'])
888 tenant_id = kwargs.setdefault('tenant_id', project['id'])
889 if project_id != project['id'] or tenant_id != project['id']:
890 raise ValueError('Project ID specified multiple times')
891
892 if 'security_group_id' not in kwargs:
893 security_group = (security_group or
894 cls.get_security_group(client=client))
895
896 if security_group:
897 client = client or security_group.get('client')
898 security_group_id = kwargs.setdefault('security_group_id',
899 security_group['id'])
900 if security_group_id != security_group['id']:
901 raise ValueError('Security group ID specified multiple times.')
902
903 ip_version = ip_version or cls._ip_version
904 default_params = (
905 constants.DEFAULT_SECURITY_GROUP_RULE_PARAMS[ip_version])
Miguel Lavalleb1c7a3d2021-01-31 19:05:22 -0600906 if ('remote_address_group_id' in kwargs and 'remote_ip_prefix' in
907 default_params):
908 default_params.pop('remote_ip_prefix')
Federico Ressi4c590d72018-10-10 14:01:08 +0200909 for key, value in default_params.items():
910 kwargs.setdefault(key, value)
911
912 client = client or cls.client
913 return client.create_security_group_rule(**kwargs)[
914 'security_group_rule']
915
916 @classmethod
917 def get_security_group(cls, name='default', client=None):
918 client = client or cls.client
919 security_groups = client.list_security_groups()['security_groups']
920 for security_group in security_groups:
921 if security_group['name'] == name:
922 return security_group
923 raise ValueError("No such security group named {!r}".format(name))
Chandan Kumarc125fd12017-11-15 19:41:01 +0530924
Federico Ressiab286e42018-06-19 09:52:10 +0200925 @classmethod
926 def create_keypair(cls, client=None, name=None, **kwargs):
927 client = client or cls.os_primary.keypairs_client
928 name = name or data_utils.rand_name('keypair-test')
929 keypair = client.create_keypair(name=name, **kwargs)['keypair']
930
931 # save client for later cleanup
932 keypair['client'] = client
933 cls.keypairs.append(keypair)
934 return keypair
935
936 @classmethod
937 def delete_keypair(cls, keypair, client=None):
938 client = (client or keypair.get('client') or
939 cls.os_primary.keypairs_client)
940 client.delete_keypair(keypair_name=keypair['name'])
941
Federico Ressi82e83e32018-07-03 14:19:55 +0200942 @classmethod
943 def create_trunk(cls, port=None, subports=None, client=None, **kwargs):
944 """Create network trunk
945
946 :param port: dictionary containing parent port ID (port['id'])
947 :param client: client to be used for connecting to networking service
948 :param **kwargs: extra parameters to be forwarded to network service
949
950 :returns: dictionary containing created trunk details
951 """
952 client = client or cls.client
953
954 if port:
955 kwargs['port_id'] = port['id']
956
957 trunk = client.create_trunk(subports=subports, **kwargs)['trunk']
958 # Save client reference for later deletion
959 trunk['client'] = client
960 cls.trunks.append(trunk)
961 return trunk
962
963 @classmethod
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800964 def delete_trunk(cls, trunk, client=None, detach_parent_port=True):
Federico Ressi82e83e32018-07-03 14:19:55 +0200965 """Delete network trunk
966
967 :param trunk: dictionary containing trunk ID (trunk['id'])
968
969 :param client: client to be used for connecting to networking service
970 """
971 client = client or trunk.get('client') or cls.client
972 trunk.update(client.show_trunk(trunk['id'])['trunk'])
973
974 if not trunk['admin_state_up']:
975 # Cannot touch trunk before admin_state_up is True
976 client.update_trunk(trunk['id'], admin_state_up=True)
977 if trunk['sub_ports']:
978 # Removes trunk ports before deleting it
979 cls._try_delete_resource(client.remove_subports, trunk['id'],
980 trunk['sub_ports'])
981
982 # we have to detach the interface from the server before
983 # the trunk can be deleted.
984 parent_port = {'id': trunk['port_id']}
985
986 def is_parent_port_detached():
987 parent_port.update(client.show_port(parent_port['id'])['port'])
988 return not parent_port['device_id']
989
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800990 if detach_parent_port and not is_parent_port_detached():
Federico Ressi82e83e32018-07-03 14:19:55 +0200991 # this could probably happen when trunk is deleted and parent port
992 # has been assigned to a VM that is still running. Here we are
993 # assuming that device_id points to such VM.
994 cls.os_primary.compute.InterfacesClient().delete_interface(
995 parent_port['device_id'], parent_port['id'])
996 utils.wait_until_true(is_parent_port_detached)
997
998 client.delete_trunk(trunk['id'])
999
Harald Jensåsc9782fa2019-06-03 22:35:41 +02001000 @classmethod
1001 def create_conntrack_helper(cls, router_id, helper, protocol, port,
1002 client=None):
1003 """Create a conntrack helper
1004
1005 Create a conntrack helper and schedule it for later deletion. If a
1006 client is passed, then it is used for deleteing the CTH too.
1007
1008 :param router_id: The ID of the Neutron router associated to the
1009 conntrack helper.
1010
1011 :param helper: The conntrack helper module alias
1012
1013 :param protocol: The conntrack helper IP protocol used in the conntrack
1014 helper.
1015
1016 :param port: The conntrack helper IP protocol port number for the
1017 conntrack helper.
1018
1019 :param client: network client to be used for creating and cleaning up
1020 the conntrack helper.
1021 """
1022
1023 client = client or cls.client
1024
1025 cth = client.create_conntrack_helper(router_id, helper, protocol,
1026 port)['conntrack_helper']
1027
1028 # save ID of router associated with conntrack helper for final cleanup
1029 cth['router_id'] = router_id
1030
1031 # save client to be used later in cls.delete_conntrack_helper for final
1032 # cleanup
1033 cth['client'] = client
1034 cls.conntrack_helpers.append(cth)
1035 return cth
1036
1037 @classmethod
1038 def delete_conntrack_helper(cls, cth, client=None):
1039 """Delete conntrack helper
1040
1041 :param client: Client to be used
1042 If client is not given it will use the client used to create the
1043 conntrack helper, or cls.client if unknown.
1044 """
1045
1046 client = client or cth.get('client') or cls.client
1047 client.delete_conntrack_helper(cth['router_id'], cth['id'])
1048
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001049
1050class BaseAdminNetworkTest(BaseNetworkTest):
1051
1052 credentials = ['primary', 'admin']
1053
1054 @classmethod
1055 def setup_clients(cls):
1056 super(BaseAdminNetworkTest, cls).setup_clients()
fumihiko kakumaa216fc12017-07-14 10:43:29 +09001057 cls.admin_client = cls.os_admin.network_client
Jakub Libosvarf5758012017-08-15 13:45:30 +00001058 cls.identity_admin_client = cls.os_admin.projects_client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001059
1060 @classmethod
1061 def create_metering_label(cls, name, description):
1062 """Wrapper utility that returns a test metering label."""
1063 body = cls.admin_client.create_metering_label(
1064 description=description,
1065 name=data_utils.rand_name("metering-label"))
1066 metering_label = body['metering_label']
1067 cls.metering_labels.append(metering_label)
1068 return metering_label
1069
1070 @classmethod
1071 def create_metering_label_rule(cls, remote_ip_prefix, direction,
1072 metering_label_id):
1073 """Wrapper utility that returns a test metering label rule."""
1074 body = cls.admin_client.create_metering_label_rule(
1075 remote_ip_prefix=remote_ip_prefix, direction=direction,
1076 metering_label_id=metering_label_id)
1077 metering_label_rule = body['metering_label_rule']
1078 cls.metering_label_rules.append(metering_label_rule)
1079 return metering_label_rule
1080
1081 @classmethod
Kailun Qineaaf9782018-12-20 04:45:01 +08001082 def create_network_segment_range(cls, name, shared,
1083 project_id, network_type,
1084 physical_network, minimum,
1085 maximum):
1086 """Wrapper utility that returns a test network segment range."""
1087 network_segment_range_args = {'name': name,
1088 'shared': shared,
1089 'project_id': project_id,
1090 'network_type': network_type,
1091 'physical_network': physical_network,
1092 'minimum': minimum,
1093 'maximum': maximum}
1094 body = cls.admin_client.create_network_segment_range(
1095 **network_segment_range_args)
1096 network_segment_range = body['network_segment_range']
1097 cls.network_segment_ranges.append(network_segment_range)
1098 return network_segment_range
1099
1100 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001101 def create_flavor(cls, name, description, service_type):
1102 """Wrapper utility that returns a test flavor."""
1103 body = cls.admin_client.create_flavor(
1104 description=description, service_type=service_type,
1105 name=name)
1106 flavor = body['flavor']
1107 cls.flavors.append(flavor)
1108 return flavor
1109
1110 @classmethod
1111 def create_service_profile(cls, description, metainfo, driver):
1112 """Wrapper utility that returns a test service profile."""
1113 body = cls.admin_client.create_service_profile(
1114 driver=driver, metainfo=metainfo, description=description)
1115 service_profile = body['service_profile']
1116 cls.service_profiles.append(service_profile)
1117 return service_profile
1118
1119 @classmethod
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001120 def create_log(cls, name, description=None,
1121 resource_type='security_group', resource_id=None,
1122 target_id=None, event='ALL', enabled=True):
1123 """Wrapper utility that returns a test log object."""
1124 log_args = {'name': name,
1125 'description': description,
1126 'resource_type': resource_type,
1127 'resource_id': resource_id,
1128 'target_id': target_id,
1129 'event': event,
1130 'enabled': enabled}
1131 body = cls.admin_client.create_log(**log_args)
1132 log_object = body['log']
1133 cls.log_objects.append(log_object)
1134 return log_object
1135
1136 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001137 def get_unused_ip(cls, net_id, ip_version=None):
Gary Kotton011345f2016-06-15 08:04:31 -07001138 """Get an unused ip address in a allocation pool of net"""
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001139 body = cls.admin_client.list_ports(network_id=net_id)
1140 ports = body['ports']
1141 used_ips = []
1142 for port in ports:
1143 used_ips.extend(
1144 [fixed_ip['ip_address'] for fixed_ip in port['fixed_ips']])
1145 body = cls.admin_client.list_subnets(network_id=net_id)
1146 subnets = body['subnets']
1147
1148 for subnet in subnets:
1149 if ip_version and subnet['ip_version'] != ip_version:
1150 continue
1151 cidr = subnet['cidr']
1152 allocation_pools = subnet['allocation_pools']
1153 iterators = []
1154 if allocation_pools:
1155 for allocation_pool in allocation_pools:
1156 iterators.append(netaddr.iter_iprange(
1157 allocation_pool['start'], allocation_pool['end']))
1158 else:
1159 net = netaddr.IPNetwork(cidr)
1160
1161 def _iterip():
1162 for ip in net:
1163 if ip not in (net.network, net.broadcast):
1164 yield ip
1165 iterators.append(iter(_iterip()))
1166
1167 for iterator in iterators:
1168 for ip in iterator:
1169 if str(ip) not in used_ips:
1170 return str(ip)
1171
1172 message = (
1173 "net(%s) has no usable IP address in allocation pools" % net_id)
1174 raise exceptions.InvalidConfiguration(message)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001175
Lajos Katona2f904652018-08-23 14:04:56 +02001176 @classmethod
1177 def create_provider_network(cls, physnet_name, start_segmentation_id,
1178 max_attempts=30):
1179 segmentation_id = start_segmentation_id
Lajos Katona7eb67252019-01-14 12:55:35 +01001180 for attempts in range(max_attempts):
Lajos Katona2f904652018-08-23 14:04:56 +02001181 try:
Lajos Katona7eb67252019-01-14 12:55:35 +01001182 return cls.create_network(
Lajos Katona2f904652018-08-23 14:04:56 +02001183 name=data_utils.rand_name('test_net'),
1184 shared=True,
1185 provider_network_type='vlan',
1186 provider_physical_network=physnet_name,
1187 provider_segmentation_id=segmentation_id)
Lajos Katona2f904652018-08-23 14:04:56 +02001188 except lib_exc.Conflict:
Lajos Katona2f904652018-08-23 14:04:56 +02001189 segmentation_id += 1
1190 if segmentation_id > 4095:
1191 raise lib_exc.TempestException(
1192 "No free segmentation id was found for provider "
1193 "network creation!")
1194 time.sleep(CONF.network.build_interval)
Lajos Katona7eb67252019-01-14 12:55:35 +01001195 LOG.exception("Failed to create provider network after "
1196 "%d attempts", max_attempts)
1197 raise lib_exc.TimeoutException
Lajos Katona2f904652018-08-23 14:04:56 +02001198
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001199
Sławek Kapłońskiff294062016-12-04 15:00:54 +00001200def require_qos_rule_type(rule_type):
1201 def decorator(f):
1202 @functools.wraps(f)
1203 def wrapper(self, *func_args, **func_kwargs):
1204 if rule_type not in self.get_supported_qos_rule_types():
1205 raise self.skipException(
1206 "%s rule type is required." % rule_type)
1207 return f(self, *func_args, **func_kwargs)
1208 return wrapper
1209 return decorator
1210
1211
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001212def _require_sorting(f):
1213 @functools.wraps(f)
1214 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301215 if not tutils.is_extension_enabled("sorting", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001216 self.skipTest('Sorting feature is required')
1217 return f(self, *args, **kwargs)
1218 return inner
1219
1220
1221def _require_pagination(f):
1222 @functools.wraps(f)
1223 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301224 if not tutils.is_extension_enabled("pagination", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001225 self.skipTest('Pagination feature is required')
1226 return f(self, *args, **kwargs)
1227 return inner
1228
1229
1230class BaseSearchCriteriaTest(BaseNetworkTest):
1231
1232 # This should be defined by subclasses to reflect resource name to test
1233 resource = None
1234
Armando Migliaccio57581c62016-07-01 10:13:19 -07001235 field = 'name'
1236
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001237 # NOTE(ihrachys): some names, like those starting with an underscore (_)
1238 # are sorted differently depending on whether the plugin implements native
1239 # sorting support, or not. So we avoid any such cases here, sticking to
1240 # alphanumeric. Also test a case when there are multiple resources with the
1241 # same name
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001242 resource_names = ('test1', 'abc1', 'test10', '123test') + ('test1',)
1243
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001244 force_tenant_isolation = True
1245
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001246 list_kwargs = {}
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001247
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001248 list_as_admin = False
1249
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001250 def assertSameOrder(self, original, actual):
1251 # gracefully handle iterators passed
1252 original = list(original)
1253 actual = list(actual)
1254 self.assertEqual(len(original), len(actual))
1255 for expected, res in zip(original, actual):
Armando Migliaccio57581c62016-07-01 10:13:19 -07001256 self.assertEqual(expected[self.field], res[self.field])
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001257
1258 @utils.classproperty
1259 def plural_name(self):
1260 return '%ss' % self.resource
1261
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001262 @property
1263 def list_client(self):
1264 return self.admin_client if self.list_as_admin else self.client
1265
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001266 def list_method(self, *args, **kwargs):
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001267 method = getattr(self.list_client, 'list_%s' % self.plural_name)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001268 kwargs.update(self.list_kwargs)
1269 return method(*args, **kwargs)
1270
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001271 def get_bare_url(self, url):
1272 base_url = self.client.base_url
zheng.yong74e760a2019-05-22 14:16:14 +08001273 base_url_normalized = utils.normalize_url(base_url)
1274 url_normalized = utils.normalize_url(url)
1275 self.assertTrue(url_normalized.startswith(base_url_normalized))
1276 return url_normalized[len(base_url_normalized):]
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001277
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001278 @classmethod
1279 def _extract_resources(cls, body):
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001280 return body[cls.plural_name]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001281
1282 def _test_list_sorts(self, direction):
1283 sort_args = {
1284 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001285 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001286 }
1287 body = self.list_method(**sort_args)
1288 resources = self._extract_resources(body)
1289 self.assertNotEmpty(
1290 resources, "%s list returned is empty" % self.resource)
Armando Migliaccio57581c62016-07-01 10:13:19 -07001291 retrieved_names = [res[self.field] for res in resources]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001292 expected = sorted(retrieved_names)
1293 if direction == constants.SORT_DIRECTION_DESC:
1294 expected = list(reversed(expected))
1295 self.assertEqual(expected, retrieved_names)
1296
1297 @_require_sorting
1298 def _test_list_sorts_asc(self):
1299 self._test_list_sorts(constants.SORT_DIRECTION_ASC)
1300
1301 @_require_sorting
1302 def _test_list_sorts_desc(self):
1303 self._test_list_sorts(constants.SORT_DIRECTION_DESC)
1304
1305 @_require_pagination
1306 def _test_list_pagination(self):
1307 for limit in range(1, len(self.resource_names) + 1):
1308 pagination_args = {
1309 'limit': limit,
1310 }
1311 body = self.list_method(**pagination_args)
1312 resources = self._extract_resources(body)
1313 self.assertEqual(limit, len(resources))
1314
1315 @_require_pagination
1316 def _test_list_no_pagination_limit_0(self):
1317 pagination_args = {
1318 'limit': 0,
1319 }
1320 body = self.list_method(**pagination_args)
1321 resources = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001322 self.assertGreaterEqual(len(resources), len(self.resource_names))
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001323
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001324 def _test_list_pagination_iteratively(self, lister):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001325 # first, collect all resources for later comparison
1326 sort_args = {
1327 'sort_dir': constants.SORT_DIRECTION_ASC,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001328 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001329 }
1330 body = self.list_method(**sort_args)
1331 expected_resources = self._extract_resources(body)
1332 self.assertNotEmpty(expected_resources)
1333
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001334 resources = lister(
1335 len(expected_resources), sort_args
1336 )
1337
1338 # finally, compare that the list retrieved in one go is identical to
1339 # the one containing pagination results
1340 self.assertSameOrder(expected_resources, resources)
1341
1342 def _list_all_with_marker(self, niterations, sort_args):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001343 # paginate resources one by one, using last fetched resource as a
1344 # marker
1345 resources = []
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001346 for i in range(niterations):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001347 pagination_args = sort_args.copy()
1348 pagination_args['limit'] = 1
1349 if resources:
1350 pagination_args['marker'] = resources[-1]['id']
1351 body = self.list_method(**pagination_args)
1352 resources_ = self._extract_resources(body)
1353 self.assertEqual(1, len(resources_))
1354 resources.extend(resources_)
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001355 return resources
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001356
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001357 @_require_pagination
1358 @_require_sorting
1359 def _test_list_pagination_with_marker(self):
1360 self._test_list_pagination_iteratively(self._list_all_with_marker)
1361
1362 def _list_all_with_hrefs(self, niterations, sort_args):
1363 # paginate resources one by one, using next href links
1364 resources = []
1365 prev_links = {}
1366
1367 for i in range(niterations):
1368 if prev_links:
1369 uri = self.get_bare_url(prev_links['next'])
1370 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001371 sort_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001372 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001373 self.plural_name, limit=1, **sort_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001374 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001375 self.plural_name, uri
1376 )
1377 resources_ = self._extract_resources(body)
1378 self.assertEqual(1, len(resources_))
1379 resources.extend(resources_)
1380
1381 # The last element is empty and does not contain 'next' link
1382 uri = self.get_bare_url(prev_links['next'])
1383 prev_links, body = self.client.get_uri_with_links(
1384 self.plural_name, uri
1385 )
1386 self.assertNotIn('next', prev_links)
1387
1388 # Now walk backwards and compare results
1389 resources2 = []
1390 for i in range(niterations):
1391 uri = self.get_bare_url(prev_links['previous'])
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001392 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001393 self.plural_name, uri
1394 )
1395 resources_ = self._extract_resources(body)
1396 self.assertEqual(1, len(resources_))
1397 resources2.extend(resources_)
1398
1399 self.assertSameOrder(resources, reversed(resources2))
1400
1401 return resources
1402
1403 @_require_pagination
1404 @_require_sorting
1405 def _test_list_pagination_with_href_links(self):
1406 self._test_list_pagination_iteratively(self._list_all_with_hrefs)
1407
1408 @_require_pagination
1409 @_require_sorting
1410 def _test_list_pagination_page_reverse_with_href_links(
1411 self, direction=constants.SORT_DIRECTION_ASC):
1412 pagination_args = {
1413 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001414 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001415 }
1416 body = self.list_method(**pagination_args)
1417 expected_resources = self._extract_resources(body)
1418
1419 page_size = 2
1420 pagination_args['limit'] = page_size
1421
1422 prev_links = {}
1423 resources = []
1424 num_resources = len(expected_resources)
1425 niterations = int(math.ceil(float(num_resources) / page_size))
1426 for i in range(niterations):
1427 if prev_links:
1428 uri = self.get_bare_url(prev_links['previous'])
1429 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001430 pagination_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001431 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001432 self.plural_name, page_reverse=True, **pagination_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001433 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001434 self.plural_name, uri
1435 )
1436 resources_ = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001437 self.assertGreaterEqual(page_size, len(resources_))
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001438 resources.extend(reversed(resources_))
1439
1440 self.assertSameOrder(expected_resources, reversed(resources))
1441
1442 @_require_pagination
1443 @_require_sorting
1444 def _test_list_pagination_page_reverse_asc(self):
1445 self._test_list_pagination_page_reverse(
1446 direction=constants.SORT_DIRECTION_ASC)
1447
1448 @_require_pagination
1449 @_require_sorting
1450 def _test_list_pagination_page_reverse_desc(self):
1451 self._test_list_pagination_page_reverse(
1452 direction=constants.SORT_DIRECTION_DESC)
1453
1454 def _test_list_pagination_page_reverse(self, direction):
1455 pagination_args = {
1456 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001457 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001458 'limit': 3,
1459 }
1460 body = self.list_method(**pagination_args)
1461 expected_resources = self._extract_resources(body)
1462
1463 pagination_args['limit'] -= 1
1464 pagination_args['marker'] = expected_resources[-1]['id']
1465 pagination_args['page_reverse'] = True
1466 body = self.list_method(**pagination_args)
1467
1468 self.assertSameOrder(
1469 # the last entry is not included in 2nd result when used as a
1470 # marker
1471 expected_resources[:-1],
1472 self._extract_resources(body))
Victor Morales1be97b42016-09-05 08:50:06 -05001473
Hongbin Lu54f55922018-07-12 19:05:39 +00001474 @tutils.requires_ext(extension="filter-validation", service="network")
1475 def _test_list_validation_filters(
1476 self, validation_args, filter_is_valid=True):
1477 if not filter_is_valid:
1478 self.assertRaises(lib_exc.BadRequest, self.list_method,
1479 **validation_args)
1480 else:
1481 body = self.list_method(**validation_args)
1482 resources = self._extract_resources(body)
1483 for resource in resources:
1484 self.assertIn(resource['name'], self.resource_names)