blob: 4441dd1667a92ec6f25441fcf6cdd4decbe78ffd [file] [log] [blame]
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001# Copyright 2012 OpenStack Foundation
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Ihar Hrachyshka59382252016-04-05 15:54:33 +020016import functools
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +020017import math
Lajos Katona2f904652018-08-23 14:04:56 +020018import time
Ihar Hrachyshka59382252016-04-05 15:54:33 +020019
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000020import netaddr
Chandan Kumarc125fd12017-11-15 19:41:01 +053021from neutron_lib import constants as const
Lajos Katona2f904652018-08-23 14:04:56 +020022from oslo_log import log
Chandan Kumarc125fd12017-11-15 19:41:01 +053023from tempest.common import utils as tutils
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000024from tempest.lib.common.utils import data_utils
25from tempest.lib import exceptions as lib_exc
26from tempest import test
27
Chandan Kumar667d3d32017-09-22 12:24:06 +053028from neutron_tempest_plugin.api import clients
29from neutron_tempest_plugin.common import constants
30from neutron_tempest_plugin.common import utils
31from neutron_tempest_plugin import config
32from neutron_tempest_plugin import exceptions
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000033
34CONF = config.CONF
35
Lajos Katona2f904652018-08-23 14:04:56 +020036LOG = log.getLogger(__name__)
37
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000038
39class BaseNetworkTest(test.BaseTestCase):
40
Brian Haleyae328b92018-10-09 19:51:54 -040041 """Base class for Neutron tests that use the Tempest Neutron REST client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000042
43 Per the Neutron API Guide, API v1.x was removed from the source code tree
44 (docs.openstack.org/api/openstack-network/2.0/content/Overview-d1e71.html)
45 Therefore, v2.x of the Neutron API is assumed. It is also assumed that the
46 following options are defined in the [network] section of etc/tempest.conf:
47
48 project_network_cidr with a block of cidr's from which smaller blocks
49 can be allocated for tenant networks
50
51 project_network_mask_bits with the mask bits to be used to partition
52 the block defined by tenant-network_cidr
53
54 Finally, it is assumed that the following option is defined in the
55 [service_available] section of etc/tempest.conf
56
57 neutron as True
58 """
59
60 force_tenant_isolation = False
61 credentials = ['primary']
62
63 # Default to ipv4.
Federico Ressi0ddc93b2018-04-09 12:01:48 +020064 _ip_version = const.IP_VERSION_4
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000065
Federico Ressi61b564e2018-07-06 08:10:31 +020066 # Derive from BaseAdminNetworkTest class to have this initialized
67 admin_client = None
68
Federico Ressia69dcd52018-07-06 09:45:34 +020069 external_network_id = CONF.network.public_network_id
70
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000071 @classmethod
72 def get_client_manager(cls, credential_type=None, roles=None,
73 force_new=None):
Genadi Chereshnyacc395c02016-07-25 12:17:37 +030074 manager = super(BaseNetworkTest, cls).get_client_manager(
75 credential_type=credential_type,
76 roles=roles,
77 force_new=force_new
78 )
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000079 # Neutron uses a different clients manager than the one in the Tempest
Jens Harbott860b46a2017-11-15 21:23:15 +000080 # save the original in case mixed tests need it
81 if credential_type == 'primary':
82 cls.os_tempest = manager
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000083 return clients.Manager(manager.credentials)
84
85 @classmethod
86 def skip_checks(cls):
87 super(BaseNetworkTest, cls).skip_checks()
88 if not CONF.service_available.neutron:
89 raise cls.skipException("Neutron support is required")
Federico Ressi0ddc93b2018-04-09 12:01:48 +020090 if (cls._ip_version == const.IP_VERSION_6 and
91 not CONF.network_feature_enabled.ipv6):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000092 raise cls.skipException("IPv6 Tests are disabled.")
Jakub Libosvar1982aa12017-05-30 11:15:33 +000093 for req_ext in getattr(cls, 'required_extensions', []):
Chandan Kumarc125fd12017-11-15 19:41:01 +053094 if not tutils.is_extension_enabled(req_ext, 'network'):
Jakub Libosvar1982aa12017-05-30 11:15:33 +000095 msg = "%s extension not enabled." % req_ext
96 raise cls.skipException(msg)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +000097
98 @classmethod
99 def setup_credentials(cls):
100 # Create no network resources for these test.
101 cls.set_network_resources()
102 super(BaseNetworkTest, cls).setup_credentials()
103
104 @classmethod
105 def setup_clients(cls):
106 super(BaseNetworkTest, cls).setup_clients()
fumihiko kakumaa216fc12017-07-14 10:43:29 +0900107 cls.client = cls.os_primary.network_client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000108
109 @classmethod
110 def resource_setup(cls):
111 super(BaseNetworkTest, cls).resource_setup()
112
113 cls.networks = []
Miguel Lavalle124378b2016-09-21 16:41:47 -0500114 cls.admin_networks = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000115 cls.subnets = []
Kevin Bentonba3651c2017-09-01 17:13:01 -0700116 cls.admin_subnets = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000117 cls.ports = []
118 cls.routers = []
119 cls.floating_ips = []
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200120 cls.port_forwardings = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000121 cls.metering_labels = []
122 cls.service_profiles = []
123 cls.flavors = []
124 cls.metering_label_rules = []
125 cls.qos_rules = []
126 cls.qos_policies = []
127 cls.ethertype = "IPv" + str(cls._ip_version)
128 cls.address_scopes = []
129 cls.admin_address_scopes = []
130 cls.subnetpools = []
131 cls.admin_subnetpools = []
Itzik Brownbac51dc2016-10-31 12:25:04 +0000132 cls.security_groups = []
Dongcan Ye2de722e2018-07-04 11:01:37 +0000133 cls.admin_security_groups = []
Chandan Kumarc125fd12017-11-15 19:41:01 +0530134 cls.projects = []
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700135 cls.log_objects = []
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200136 cls.reserved_subnet_cidrs = set()
Federico Ressiab286e42018-06-19 09:52:10 +0200137 cls.keypairs = []
Federico Ressi82e83e32018-07-03 14:19:55 +0200138 cls.trunks = []
Kailun Qineaaf9782018-12-20 04:45:01 +0800139 cls.network_segment_ranges = []
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200140 cls.conntrack_helpers = []
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000141
142 @classmethod
143 def resource_cleanup(cls):
144 if CONF.service_available.neutron:
Federico Ressi82e83e32018-07-03 14:19:55 +0200145 # Clean up trunks
146 for trunk in cls.trunks:
147 cls._try_delete_resource(cls.delete_trunk, trunk)
148
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200149 # Clean up port forwardings
150 for pf in cls.port_forwardings:
151 cls._try_delete_resource(cls.delete_port_forwarding, pf)
152
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000153 # Clean up floating IPs
154 for floating_ip in cls.floating_ips:
Federico Ressia69dcd52018-07-06 09:45:34 +0200155 cls._try_delete_resource(cls.delete_floatingip, floating_ip)
156
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200157 # Clean up conntrack helpers
158 for cth in cls.conntrack_helpers:
159 cls._try_delete_resource(cls.delete_conntrack_helper, cth)
160
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000161 # Clean up routers
162 for router in cls.routers:
163 cls._try_delete_resource(cls.delete_router,
164 router)
165 # Clean up metering label rules
166 for metering_label_rule in cls.metering_label_rules:
167 cls._try_delete_resource(
168 cls.admin_client.delete_metering_label_rule,
169 metering_label_rule['id'])
170 # Clean up metering labels
171 for metering_label in cls.metering_labels:
172 cls._try_delete_resource(
173 cls.admin_client.delete_metering_label,
174 metering_label['id'])
175 # Clean up flavors
176 for flavor in cls.flavors:
177 cls._try_delete_resource(
178 cls.admin_client.delete_flavor,
179 flavor['id'])
180 # Clean up service profiles
181 for service_profile in cls.service_profiles:
182 cls._try_delete_resource(
183 cls.admin_client.delete_service_profile,
184 service_profile['id'])
185 # Clean up ports
186 for port in cls.ports:
187 cls._try_delete_resource(cls.client.delete_port,
188 port['id'])
189 # Clean up subnets
190 for subnet in cls.subnets:
191 cls._try_delete_resource(cls.client.delete_subnet,
192 subnet['id'])
Kevin Bentonba3651c2017-09-01 17:13:01 -0700193 # Clean up admin subnets
194 for subnet in cls.admin_subnets:
195 cls._try_delete_resource(cls.admin_client.delete_subnet,
196 subnet['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000197 # Clean up networks
198 for network in cls.networks:
Federico Ressi61b564e2018-07-06 08:10:31 +0200199 cls._try_delete_resource(cls.delete_network, network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000200
Miguel Lavalle124378b2016-09-21 16:41:47 -0500201 # Clean up admin networks
202 for network in cls.admin_networks:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000203 cls._try_delete_resource(cls.admin_client.delete_network,
204 network['id'])
205
Itzik Brownbac51dc2016-10-31 12:25:04 +0000206 # Clean up security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200207 for security_group in cls.security_groups:
208 cls._try_delete_resource(cls.delete_security_group,
209 security_group)
Itzik Brownbac51dc2016-10-31 12:25:04 +0000210
Dongcan Ye2de722e2018-07-04 11:01:37 +0000211 # Clean up admin security groups
Federico Ressi4c590d72018-10-10 14:01:08 +0200212 for security_group in cls.admin_security_groups:
213 cls._try_delete_resource(cls.delete_security_group,
214 security_group,
215 client=cls.admin_client)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000216
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000217 for subnetpool in cls.subnetpools:
218 cls._try_delete_resource(cls.client.delete_subnetpool,
219 subnetpool['id'])
220
221 for subnetpool in cls.admin_subnetpools:
222 cls._try_delete_resource(cls.admin_client.delete_subnetpool,
223 subnetpool['id'])
224
225 for address_scope in cls.address_scopes:
226 cls._try_delete_resource(cls.client.delete_address_scope,
227 address_scope['id'])
228
229 for address_scope in cls.admin_address_scopes:
230 cls._try_delete_resource(
231 cls.admin_client.delete_address_scope,
232 address_scope['id'])
233
Chandan Kumarc125fd12017-11-15 19:41:01 +0530234 for project in cls.projects:
235 cls._try_delete_resource(
236 cls.identity_admin_client.delete_project,
237 project['id'])
238
Sławek Kapłońskie100c4d2017-08-23 21:18:34 +0000239 # Clean up QoS rules
240 for qos_rule in cls.qos_rules:
241 cls._try_delete_resource(cls.admin_client.delete_qos_rule,
242 qos_rule['id'])
243 # Clean up QoS policies
244 # as all networks and ports are already removed, QoS policies
245 # shouldn't be "in use"
246 for qos_policy in cls.qos_policies:
247 cls._try_delete_resource(cls.admin_client.delete_qos_policy,
248 qos_policy['id'])
249
Nguyen Phuong An67993fc2017-11-24 11:30:25 +0700250 # Clean up log_objects
251 for log_object in cls.log_objects:
252 cls._try_delete_resource(cls.admin_client.delete_log,
253 log_object['id'])
254
Federico Ressiab286e42018-06-19 09:52:10 +0200255 for keypair in cls.keypairs:
256 cls._try_delete_resource(cls.delete_keypair, keypair)
257
Kailun Qineaaf9782018-12-20 04:45:01 +0800258 # Clean up network_segment_ranges
259 for network_segment_range in cls.network_segment_ranges:
260 cls._try_delete_resource(
261 cls.admin_client.delete_network_segment_range,
262 network_segment_range['id'])
263
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000264 super(BaseNetworkTest, cls).resource_cleanup()
265
266 @classmethod
267 def _try_delete_resource(cls, delete_callable, *args, **kwargs):
268 """Cleanup resources in case of test-failure
269
270 Some resources are explicitly deleted by the test.
271 If the test failed to delete a resource, this method will execute
272 the appropriate delete methods. Otherwise, the method ignores NotFound
273 exceptions thrown for resources that were correctly deleted by the
274 test.
275
276 :param delete_callable: delete method
277 :param args: arguments for delete method
278 :param kwargs: keyword arguments for delete method
279 """
280 try:
281 delete_callable(*args, **kwargs)
282 # if resource is not found, this means it was deleted in the test
283 except lib_exc.NotFound:
284 pass
285
286 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200287 def create_network(cls, network_name=None, client=None, external=None,
288 shared=None, provider_network_type=None,
289 provider_physical_network=None,
290 provider_segmentation_id=None, **kwargs):
291 """Create a network.
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000292
Federico Ressi61b564e2018-07-06 08:10:31 +0200293 When client is not provider and admin_client is attribute is not None
294 (for example when using BaseAdminNetworkTest base class) and using any
295 of the convenience parameters (external, shared, provider_network_type,
296 provider_physical_network and provider_segmentation_id) it silently
297 uses admin_client. If the network is not shared then it uses the same
298 project_id as regular client.
299
300 :param network_name: Human-readable name of the network
301
302 :param client: client to be used for connecting to network service
303
304 :param external: indicates whether the network has an external routing
305 facility that's not managed by the networking service.
306
307 :param shared: indicates whether this resource is shared across all
308 projects. By default, only administrative users can change this value.
309 If True and admin_client attribute is not None, then the network is
310 created under administrative project.
311
312 :param provider_network_type: the type of physical network that this
313 network should be mapped to. For example, 'flat', 'vlan', 'vxlan', or
314 'gre'. Valid values depend on a networking back-end.
315
316 :param provider_physical_network: the physical network where this
317 network should be implemented. The Networking API v2.0 does not provide
318 a way to list available physical networks. For example, the Open
319 vSwitch plug-in configuration file defines a symbolic name that maps to
320 specific bridges on each compute host.
321
322 :param provider_segmentation_id: The ID of the isolated segment on the
323 physical network. The network_type attribute defines the segmentation
324 model. For example, if the network_type value is 'vlan', this ID is a
325 vlan identifier. If the network_type value is 'gre', this ID is a gre
326 key.
327
328 :param **kwargs: extra parameters to be forwarded to network service
329 """
330
331 name = (network_name or kwargs.pop('name', None) or
332 data_utils.rand_name('test-network-'))
333
334 # translate convenience parameters
335 admin_client_required = False
336 if provider_network_type:
337 admin_client_required = True
338 kwargs['provider:network_type'] = provider_network_type
339 if provider_physical_network:
340 admin_client_required = True
341 kwargs['provider:physical_network'] = provider_physical_network
342 if provider_segmentation_id:
343 admin_client_required = True
344 kwargs['provider:segmentation_id'] = provider_segmentation_id
345 if external is not None:
346 admin_client_required = True
347 kwargs['router:external'] = bool(external)
348 if shared is not None:
349 admin_client_required = True
350 kwargs['shared'] = bool(shared)
351
352 if not client:
353 if admin_client_required and cls.admin_client:
354 # For convenience silently switch to admin client
355 client = cls.admin_client
356 if not shared:
357 # Keep this network visible from current project
358 project_id = (kwargs.get('project_id') or
359 kwargs.get('tenant_id') or
360 cls.client.tenant_id)
361 kwargs.update(project_id=project_id, tenant_id=project_id)
362 else:
363 # Use default client
364 client = cls.client
365
366 network = client.create_network(name=name, **kwargs)['network']
367 network['client'] = client
368 cls.networks.append(network)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000369 return network
370
371 @classmethod
Federico Ressi61b564e2018-07-06 08:10:31 +0200372 def delete_network(cls, network, client=None):
373 client = client or network.get('client') or cls.client
374 client.delete_network(network['id'])
375
376 @classmethod
377 def create_shared_network(cls, network_name=None, **kwargs):
378 return cls.create_network(name=network_name, shared=True, **kwargs)
Miguel Lavalle124378b2016-09-21 16:41:47 -0500379
380 @classmethod
381 def create_network_keystone_v3(cls, network_name=None, project_id=None,
382 tenant_id=None, client=None):
Federico Ressi61b564e2018-07-06 08:10:31 +0200383 params = {}
384 if project_id:
385 params['project_id'] = project_id
386 if tenant_id:
387 params['tenant_id'] = tenant_id
388 return cls.create_network(name=network_name, client=client, **params)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000389
390 @classmethod
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200391 def create_subnet(cls, network, gateway='', cidr=None, mask_bits=None,
Federico Ressi98f20ec2018-05-11 06:09:49 +0200392 ip_version=None, client=None, reserve_cidr=True,
393 **kwargs):
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200394 """Wrapper utility that returns a test subnet.
395
396 Convenient wrapper for client.create_subnet method. It reserves and
397 allocates CIDRs to avoid creating overlapping subnets.
398
399 :param network: network where to create the subnet
400 network['id'] must contain the ID of the network
401
402 :param gateway: gateway IP address
403 It can be a str or a netaddr.IPAddress
404 If gateway is not given, then it will use default address for
405 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 +0200406 if gateway is given as None then no gateway will be assigned
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200407
408 :param cidr: CIDR of the subnet to create
409 It can be either None, a str or a netaddr.IPNetwork instance
410
411 :param mask_bits: CIDR prefix length
412 It can be either None or a numeric value.
413 If cidr parameter is given then mask_bits is used to determinate a
414 sequence of valid CIDR to use as generated.
415 Please see netaddr.IPNetwork.subnet method documentation[1]
416
417 :param ip_version: ip version of generated subnet CIDRs
418 It can be None, IP_VERSION_4 or IP_VERSION_6
419 It has to match given either given CIDR and gateway
420
421 :param ip_version: numeric value (either IP_VERSION_4 or IP_VERSION_6)
422 this value must match CIDR and gateway IP versions if any of them is
423 given
424
425 :param client: client to be used to connect to network service
426
Federico Ressi98f20ec2018-05-11 06:09:49 +0200427 :param reserve_cidr: if True then it reserves assigned CIDR to avoid
428 using the same CIDR for further subnets in the scope of the same
429 test case class
430
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200431 :param **kwargs: optional parameters to be forwarded to wrapped method
432
433 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
434 """
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000435
436 # allow tests to use admin client
437 if not client:
438 client = cls.client
439
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200440 if gateway:
441 gateway_ip = netaddr.IPAddress(gateway)
442 if ip_version:
443 if ip_version != gateway_ip.version:
444 raise ValueError(
445 "Gateway IP version doesn't match IP version")
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000446 else:
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200447 ip_version = gateway_ip.version
Sławek Kapłońskid98e27d2018-05-07 16:16:28 +0200448 else:
449 ip_version = ip_version or cls._ip_version
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200450
451 for subnet_cidr in cls.get_subnet_cidrs(
452 ip_version=ip_version, cidr=cidr, mask_bits=mask_bits):
Federico Ressi98f20ec2018-05-11 06:09:49 +0200453 if gateway is not None:
454 kwargs['gateway_ip'] = str(gateway or (subnet_cidr.ip + 1))
Slawek Kaplonski21f53422018-11-02 16:02:09 +0100455 else:
456 kwargs['gateway_ip'] = None
Federico Ressi98f20ec2018-05-11 06:09:49 +0200457 try:
458 body = client.create_subnet(
459 network_id=network['id'],
460 cidr=str(subnet_cidr),
461 ip_version=subnet_cidr.version,
462 **kwargs)
463 break
464 except lib_exc.BadRequest as e:
465 if 'overlaps with another subnet' not in str(e):
466 raise
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000467 else:
468 message = 'Available CIDR for subnet creation could not be found'
469 raise ValueError(message)
470 subnet = body['subnet']
Kevin Bentonba3651c2017-09-01 17:13:01 -0700471 if client is cls.client:
472 cls.subnets.append(subnet)
473 else:
474 cls.admin_subnets.append(subnet)
Federico Ressi98f20ec2018-05-11 06:09:49 +0200475 if reserve_cidr:
476 cls.reserve_subnet_cidr(subnet_cidr)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000477 return subnet
478
479 @classmethod
Federico Ressi0ddc93b2018-04-09 12:01:48 +0200480 def reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
481 """Reserve given subnet CIDR making sure it is not used by create_subnet
482
483 :param addr: the CIDR address to be reserved
484 It can be a str or netaddr.IPNetwork instance
485
486 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
487 parameters
488 """
489
490 if not cls.try_reserve_subnet_cidr(addr, **ipnetwork_kwargs):
491 raise ValueError('Subnet CIDR already reserved: %r'.format(
492 addr))
493
494 @classmethod
495 def try_reserve_subnet_cidr(cls, addr, **ipnetwork_kwargs):
496 """Reserve given subnet CIDR if it hasn't been reserved before
497
498 :param addr: the CIDR address to be reserved
499 It can be a str or netaddr.IPNetwork instance
500
501 :param **ipnetwork_kwargs: optional netaddr.IPNetwork constructor
502 parameters
503
504 :return: True if it wasn't reserved before, False elsewhere.
505 """
506
507 subnet_cidr = netaddr.IPNetwork(addr, **ipnetwork_kwargs)
508 if subnet_cidr in cls.reserved_subnet_cidrs:
509 return False
510 else:
511 cls.reserved_subnet_cidrs.add(subnet_cidr)
512 return True
513
514 @classmethod
515 def get_subnet_cidrs(
516 cls, cidr=None, mask_bits=None, ip_version=None):
517 """Iterate over a sequence of unused subnet CIDR for IP version
518
519 :param cidr: CIDR of the subnet to create
520 It can be either None, a str or a netaddr.IPNetwork instance
521
522 :param mask_bits: CIDR prefix length
523 It can be either None or a numeric value.
524 If cidr parameter is given then mask_bits is used to determinate a
525 sequence of valid CIDR to use as generated.
526 Please see netaddr.IPNetwork.subnet method documentation[1]
527
528 :param ip_version: ip version of generated subnet CIDRs
529 It can be None, IP_VERSION_4 or IP_VERSION_6
530 It has to match given CIDR if given
531
532 :return: iterator over reserved CIDRs of type netaddr.IPNetwork
533
534 [1] http://netaddr.readthedocs.io/en/latest/tutorial_01.html#supernets-and-subnets # noqa
535 """
536
537 if cidr:
538 # Generate subnet CIDRs starting from given CIDR
539 # checking it is of requested IP version
540 cidr = netaddr.IPNetwork(cidr, version=ip_version)
541 else:
542 # Generate subnet CIDRs starting from configured values
543 ip_version = ip_version or cls._ip_version
544 if ip_version == const.IP_VERSION_4:
545 mask_bits = mask_bits or config.safe_get_config_value(
546 'network', 'project_network_mask_bits')
547 cidr = netaddr.IPNetwork(config.safe_get_config_value(
548 'network', 'project_network_cidr'))
549 elif ip_version == const.IP_VERSION_6:
550 mask_bits = config.safe_get_config_value(
551 'network', 'project_network_v6_mask_bits')
552 cidr = netaddr.IPNetwork(config.safe_get_config_value(
553 'network', 'project_network_v6_cidr'))
554 else:
555 raise ValueError('Invalid IP version: {!r}'.format(ip_version))
556
557 if mask_bits:
558 subnet_cidrs = cidr.subnet(mask_bits)
559 else:
560 subnet_cidrs = iter([cidr])
561
562 for subnet_cidr in subnet_cidrs:
563 if subnet_cidr not in cls.reserved_subnet_cidrs:
564 yield subnet_cidr
565
566 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000567 def create_port(cls, network, **kwargs):
568 """Wrapper utility that returns a test port."""
Edan Davidd75e48e2018-01-03 02:49:52 -0500569 if CONF.network.port_vnic_type and 'binding:vnic_type' not in kwargs:
570 kwargs['binding:vnic_type'] = CONF.network.port_vnic_type
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000571 body = cls.client.create_port(network_id=network['id'],
572 **kwargs)
573 port = body['port']
574 cls.ports.append(port)
575 return port
576
577 @classmethod
578 def update_port(cls, port, **kwargs):
579 """Wrapper utility that updates a test port."""
580 body = cls.client.update_port(port['id'],
581 **kwargs)
582 return body['port']
583
584 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300585 def _create_router_with_client(
586 cls, client, router_name=None, admin_state_up=False,
587 external_network_id=None, enable_snat=None, **kwargs
588 ):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000589 ext_gw_info = {}
590 if external_network_id:
591 ext_gw_info['network_id'] = external_network_id
YAMAMOTO Takashi9bd4f972017-06-20 12:49:30 +0900592 if enable_snat is not None:
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000593 ext_gw_info['enable_snat'] = enable_snat
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300594 body = client.create_router(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000595 router_name, external_gateway_info=ext_gw_info,
596 admin_state_up=admin_state_up, **kwargs)
597 router = body['router']
598 cls.routers.append(router)
599 return router
600
601 @classmethod
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300602 def create_router(cls, *args, **kwargs):
603 return cls._create_router_with_client(cls.client, *args, **kwargs)
604
605 @classmethod
606 def create_admin_router(cls, *args, **kwargs):
rajat294495c042017-06-28 15:37:16 +0530607 return cls._create_router_with_client(cls.os_admin.network_client,
Genadi Chereshnyac0411e92016-07-11 16:59:42 +0300608 *args, **kwargs)
609
610 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200611 def create_floatingip(cls, external_network_id=None, port=None,
612 client=None, **kwargs):
613 """Creates a floating IP.
614
615 Create a floating IP and schedule it for later deletion.
616 If a client is passed, then it is used for deleting the IP too.
617
618 :param external_network_id: network ID where to create
619 By default this is 'CONF.network.public_network_id'.
620
621 :param port: port to bind floating IP to
622 This is translated to 'port_id=port['id']'
623 By default it is None.
624
625 :param client: network client to be used for creating and cleaning up
626 the floating IP.
627
628 :param **kwargs: additional creation parameters to be forwarded to
629 networking server.
630 """
631
632 client = client or cls.client
633 external_network_id = (external_network_id or
634 cls.external_network_id)
635
636 if port:
Federico Ressi47f6ae42018-09-24 16:19:14 +0200637 port_id = kwargs.setdefault('port_id', port['id'])
638 if port_id != port['id']:
639 message = "Port ID specified twice: {!s} != {!s}".format(
640 port_id, port['id'])
641 raise ValueError(message)
Federico Ressia69dcd52018-07-06 09:45:34 +0200642
643 fip = client.create_floatingip(external_network_id,
644 **kwargs)['floatingip']
645
646 # save client to be used later in cls.delete_floatingip
647 # for final cleanup
648 fip['client'] = client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000649 cls.floating_ips.append(fip)
650 return fip
651
652 @classmethod
Federico Ressia69dcd52018-07-06 09:45:34 +0200653 def delete_floatingip(cls, floating_ip, client=None):
654 """Delete floating IP
655
656 :param client: Client to be used
657 If client is not given it will use the client used to create
658 the floating IP, or cls.client if unknown.
659 """
660
661 client = client or floating_ip.get('client') or cls.client
662 client.delete_floatingip(floating_ip['id'])
663
664 @classmethod
Slawek Kaplonski003fcae2019-05-26 22:38:35 +0200665 def create_port_forwarding(cls, fip_id, internal_port_id,
666 internal_port, external_port,
667 internal_ip_address=None, protocol="tcp",
668 client=None):
669 """Creates a port forwarding.
670
671 Create a port forwarding and schedule it for later deletion.
672 If a client is passed, then it is used for deleting the PF too.
673
674 :param fip_id: The ID of the floating IP address.
675
676 :param internal_port_id: The ID of the Neutron port associated to
677 the floating IP port forwarding.
678
679 :param internal_port: The TCP/UDP/other protocol port number of the
680 Neutron port fixed IP address associated to the floating ip
681 port forwarding.
682
683 :param external_port: The TCP/UDP/other protocol port number of
684 the port forwarding floating IP address.
685
686 :param internal_ip_address: The fixed IPv4 address of the Neutron
687 port associated to the floating IP port forwarding.
688
689 :param protocol: The IP protocol used in the floating IP port
690 forwarding.
691
692 :param client: network client to be used for creating and cleaning up
693 the floating IP port forwarding.
694 """
695
696 client = client or cls.client
697
698 pf = client.create_port_forwarding(
699 fip_id, internal_port_id, internal_port, external_port,
700 internal_ip_address, protocol)['port_forwarding']
701
702 # save ID of floating IP associated with port forwarding for final
703 # cleanup
704 pf['floatingip_id'] = fip_id
705
706 # save client to be used later in cls.delete_port_forwarding
707 # for final cleanup
708 pf['client'] = client
709 cls.port_forwardings.append(pf)
710 return pf
711
712 @classmethod
713 def delete_port_forwarding(cls, pf, client=None):
714 """Delete port forwarding
715
716 :param client: Client to be used
717 If client is not given it will use the client used to create
718 the port forwarding, or cls.client if unknown.
719 """
720
721 client = client or pf.get('client') or cls.client
722 client.delete_port_forwarding(pf['floatingip_id'], pf['id'])
723
724 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000725 def create_router_interface(cls, router_id, subnet_id):
726 """Wrapper utility that returns a router interface."""
727 interface = cls.client.add_router_interface_with_subnet_id(
728 router_id, subnet_id)
729 return interface
730
731 @classmethod
Bence Romsics46bd3af2019-09-13 10:52:41 +0200732 def add_extra_routes_atomic(cls, *args, **kwargs):
733 return cls.client.add_extra_routes_atomic(*args, **kwargs)
734
735 @classmethod
736 def remove_extra_routes_atomic(cls, *args, **kwargs):
737 return cls.client.remove_extra_routes_atomic(*args, **kwargs)
738
739 @classmethod
Sławek Kapłońskiff294062016-12-04 15:00:54 +0000740 def get_supported_qos_rule_types(cls):
741 body = cls.client.list_qos_rule_types()
742 return [rule_type['type'] for rule_type in body['rule_types']]
743
744 @classmethod
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +0200745 def create_qos_policy(cls, name, description=None, shared=False,
Hirofumi Ichihara39a6ee12017-08-23 13:55:12 +0900746 tenant_id=None, is_default=False):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000747 """Wrapper utility that returns a test QoS policy."""
748 body = cls.admin_client.create_qos_policy(
Hirofumi Ichihara39a6ee12017-08-23 13:55:12 +0900749 name, description, shared, tenant_id, is_default)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000750 qos_policy = body['policy']
751 cls.qos_policies.append(qos_policy)
752 return qos_policy
753
754 @classmethod
Sławek Kapłoński153f3452017-03-24 22:04:53 +0000755 def create_qos_bandwidth_limit_rule(cls, policy_id, max_kbps,
756 max_burst_kbps,
Chandan Kumarc125fd12017-11-15 19:41:01 +0530757 direction=const.EGRESS_DIRECTION):
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000758 """Wrapper utility that returns a test QoS bandwidth limit rule."""
759 body = cls.admin_client.create_bandwidth_limit_rule(
Sławek Kapłoński153f3452017-03-24 22:04:53 +0000760 policy_id, max_kbps, max_burst_kbps, direction)
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000761 qos_rule = body['bandwidth_limit_rule']
762 cls.qos_rules.append(qos_rule)
763 return qos_rule
764
765 @classmethod
Lajos Katona2f904652018-08-23 14:04:56 +0200766 def create_qos_minimum_bandwidth_rule(cls, policy_id, min_kbps,
767 direction=const.EGRESS_DIRECTION):
768 """Wrapper utility that creates and returns a QoS min bw rule."""
769 body = cls.admin_client.create_minimum_bandwidth_rule(
770 policy_id, direction, min_kbps)
771 qos_rule = body['minimum_bandwidth_rule']
772 cls.qos_rules.append(qos_rule)
773 return qos_rule
774
775 @classmethod
Jakub Libosvar83704832017-12-06 16:02:28 +0000776 def delete_router(cls, router, client=None):
777 client = client or cls.client
Aditya Vaja49819a72018-11-26 14:20:10 -0800778 if 'routes' in router:
779 client.remove_router_extra_routes(router['id'])
Jakub Libosvar83704832017-12-06 16:02:28 +0000780 body = client.list_router_interfaces(router['id'])
Chandan Kumarc125fd12017-11-15 19:41:01 +0530781 interfaces = [port for port in body['ports']
782 if port['device_owner'] in const.ROUTER_INTERFACE_OWNERS]
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000783 for i in interfaces:
784 try:
Jakub Libosvar83704832017-12-06 16:02:28 +0000785 client.remove_router_interface_with_subnet_id(
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000786 router['id'], i['fixed_ips'][0]['subnet_id'])
787 except lib_exc.NotFound:
788 pass
Jakub Libosvar83704832017-12-06 16:02:28 +0000789 client.delete_router(router['id'])
Daniel Mellado3c0aeab2016-01-29 11:30:25 +0000790
791 @classmethod
792 def create_address_scope(cls, name, is_admin=False, **kwargs):
793 if is_admin:
794 body = cls.admin_client.create_address_scope(name=name, **kwargs)
795 cls.admin_address_scopes.append(body['address_scope'])
796 else:
797 body = cls.client.create_address_scope(name=name, **kwargs)
798 cls.address_scopes.append(body['address_scope'])
799 return body['address_scope']
800
801 @classmethod
802 def create_subnetpool(cls, name, is_admin=False, **kwargs):
803 if is_admin:
804 body = cls.admin_client.create_subnetpool(name, **kwargs)
805 cls.admin_subnetpools.append(body['subnetpool'])
806 else:
807 body = cls.client.create_subnetpool(name, **kwargs)
808 cls.subnetpools.append(body['subnetpool'])
809 return body['subnetpool']
810
Chandan Kumarc125fd12017-11-15 19:41:01 +0530811 @classmethod
812 def create_project(cls, name=None, description=None):
813 test_project = name or data_utils.rand_name('test_project_')
814 test_description = description or data_utils.rand_name('desc_')
815 project = cls.identity_admin_client.create_project(
816 name=test_project,
817 description=test_description)['project']
818 cls.projects.append(project)
Dongcan Ye2de722e2018-07-04 11:01:37 +0000819 # Create a project will create a default security group.
Dongcan Ye2de722e2018-07-04 11:01:37 +0000820 sgs_list = cls.admin_client.list_security_groups(
821 tenant_id=project['id'])['security_groups']
Federico Ressi4c590d72018-10-10 14:01:08 +0200822 for security_group in sgs_list:
823 # Make sure delete_security_group method will use
824 # the admin client for this group
825 security_group['client'] = cls.admin_client
826 cls.security_groups.append(security_group)
Chandan Kumarc125fd12017-11-15 19:41:01 +0530827 return project
828
829 @classmethod
Federico Ressi4c590d72018-10-10 14:01:08 +0200830 def create_security_group(cls, name=None, project=None, client=None,
831 **kwargs):
832 if project:
833 client = client or cls.admin_client
834 project_id = kwargs.setdefault('project_id', project['id'])
835 tenant_id = kwargs.setdefault('tenant_id', project['id'])
836 if project_id != project['id'] or tenant_id != project['id']:
837 raise ValueError('Project ID specified multiple times')
838 else:
839 client = client or cls.client
840
841 name = name or data_utils.rand_name(cls.__name__)
842 security_group = client.create_security_group(name=name, **kwargs)[
843 'security_group']
844 security_group['client'] = client
845 cls.security_groups.append(security_group)
846 return security_group
847
848 @classmethod
849 def delete_security_group(cls, security_group, client=None):
850 client = client or security_group.get('client') or cls.client
851 client.delete_security_group(security_group['id'])
852
853 @classmethod
854 def create_security_group_rule(cls, security_group=None, project=None,
855 client=None, ip_version=None, **kwargs):
856 if project:
857 client = client or cls.admin_client
858 project_id = kwargs.setdefault('project_id', project['id'])
859 tenant_id = kwargs.setdefault('tenant_id', project['id'])
860 if project_id != project['id'] or tenant_id != project['id']:
861 raise ValueError('Project ID specified multiple times')
862
863 if 'security_group_id' not in kwargs:
864 security_group = (security_group or
865 cls.get_security_group(client=client))
866
867 if security_group:
868 client = client or security_group.get('client')
869 security_group_id = kwargs.setdefault('security_group_id',
870 security_group['id'])
871 if security_group_id != security_group['id']:
872 raise ValueError('Security group ID specified multiple times.')
873
874 ip_version = ip_version or cls._ip_version
875 default_params = (
876 constants.DEFAULT_SECURITY_GROUP_RULE_PARAMS[ip_version])
877 for key, value in default_params.items():
878 kwargs.setdefault(key, value)
879
880 client = client or cls.client
881 return client.create_security_group_rule(**kwargs)[
882 'security_group_rule']
883
884 @classmethod
885 def get_security_group(cls, name='default', client=None):
886 client = client or cls.client
887 security_groups = client.list_security_groups()['security_groups']
888 for security_group in security_groups:
889 if security_group['name'] == name:
890 return security_group
891 raise ValueError("No such security group named {!r}".format(name))
Chandan Kumarc125fd12017-11-15 19:41:01 +0530892
Federico Ressiab286e42018-06-19 09:52:10 +0200893 @classmethod
894 def create_keypair(cls, client=None, name=None, **kwargs):
895 client = client or cls.os_primary.keypairs_client
896 name = name or data_utils.rand_name('keypair-test')
897 keypair = client.create_keypair(name=name, **kwargs)['keypair']
898
899 # save client for later cleanup
900 keypair['client'] = client
901 cls.keypairs.append(keypair)
902 return keypair
903
904 @classmethod
905 def delete_keypair(cls, keypair, client=None):
906 client = (client or keypair.get('client') or
907 cls.os_primary.keypairs_client)
908 client.delete_keypair(keypair_name=keypair['name'])
909
Federico Ressi82e83e32018-07-03 14:19:55 +0200910 @classmethod
911 def create_trunk(cls, port=None, subports=None, client=None, **kwargs):
912 """Create network trunk
913
914 :param port: dictionary containing parent port ID (port['id'])
915 :param client: client to be used for connecting to networking service
916 :param **kwargs: extra parameters to be forwarded to network service
917
918 :returns: dictionary containing created trunk details
919 """
920 client = client or cls.client
921
922 if port:
923 kwargs['port_id'] = port['id']
924
925 trunk = client.create_trunk(subports=subports, **kwargs)['trunk']
926 # Save client reference for later deletion
927 trunk['client'] = client
928 cls.trunks.append(trunk)
929 return trunk
930
931 @classmethod
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800932 def delete_trunk(cls, trunk, client=None, detach_parent_port=True):
Federico Ressi82e83e32018-07-03 14:19:55 +0200933 """Delete network trunk
934
935 :param trunk: dictionary containing trunk ID (trunk['id'])
936
937 :param client: client to be used for connecting to networking service
938 """
939 client = client or trunk.get('client') or cls.client
940 trunk.update(client.show_trunk(trunk['id'])['trunk'])
941
942 if not trunk['admin_state_up']:
943 # Cannot touch trunk before admin_state_up is True
944 client.update_trunk(trunk['id'], admin_state_up=True)
945 if trunk['sub_ports']:
946 # Removes trunk ports before deleting it
947 cls._try_delete_resource(client.remove_subports, trunk['id'],
948 trunk['sub_ports'])
949
950 # we have to detach the interface from the server before
951 # the trunk can be deleted.
952 parent_port = {'id': trunk['port_id']}
953
954 def is_parent_port_detached():
955 parent_port.update(client.show_port(parent_port['id'])['port'])
956 return not parent_port['device_id']
957
Huifeng Le1c9f40b2018-11-07 01:14:21 +0800958 if detach_parent_port and not is_parent_port_detached():
Federico Ressi82e83e32018-07-03 14:19:55 +0200959 # this could probably happen when trunk is deleted and parent port
960 # has been assigned to a VM that is still running. Here we are
961 # assuming that device_id points to such VM.
962 cls.os_primary.compute.InterfacesClient().delete_interface(
963 parent_port['device_id'], parent_port['id'])
964 utils.wait_until_true(is_parent_port_detached)
965
966 client.delete_trunk(trunk['id'])
967
Harald Jensåsc9782fa2019-06-03 22:35:41 +0200968 @classmethod
969 def create_conntrack_helper(cls, router_id, helper, protocol, port,
970 client=None):
971 """Create a conntrack helper
972
973 Create a conntrack helper and schedule it for later deletion. If a
974 client is passed, then it is used for deleteing the CTH too.
975
976 :param router_id: The ID of the Neutron router associated to the
977 conntrack helper.
978
979 :param helper: The conntrack helper module alias
980
981 :param protocol: The conntrack helper IP protocol used in the conntrack
982 helper.
983
984 :param port: The conntrack helper IP protocol port number for the
985 conntrack helper.
986
987 :param client: network client to be used for creating and cleaning up
988 the conntrack helper.
989 """
990
991 client = client or cls.client
992
993 cth = client.create_conntrack_helper(router_id, helper, protocol,
994 port)['conntrack_helper']
995
996 # save ID of router associated with conntrack helper for final cleanup
997 cth['router_id'] = router_id
998
999 # save client to be used later in cls.delete_conntrack_helper for final
1000 # cleanup
1001 cth['client'] = client
1002 cls.conntrack_helpers.append(cth)
1003 return cth
1004
1005 @classmethod
1006 def delete_conntrack_helper(cls, cth, client=None):
1007 """Delete conntrack helper
1008
1009 :param client: Client to be used
1010 If client is not given it will use the client used to create the
1011 conntrack helper, or cls.client if unknown.
1012 """
1013
1014 client = client or cth.get('client') or cls.client
1015 client.delete_conntrack_helper(cth['router_id'], cth['id'])
1016
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001017
1018class BaseAdminNetworkTest(BaseNetworkTest):
1019
1020 credentials = ['primary', 'admin']
1021
1022 @classmethod
1023 def setup_clients(cls):
1024 super(BaseAdminNetworkTest, cls).setup_clients()
fumihiko kakumaa216fc12017-07-14 10:43:29 +09001025 cls.admin_client = cls.os_admin.network_client
Jakub Libosvarf5758012017-08-15 13:45:30 +00001026 cls.identity_admin_client = cls.os_admin.projects_client
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001027
1028 @classmethod
1029 def create_metering_label(cls, name, description):
1030 """Wrapper utility that returns a test metering label."""
1031 body = cls.admin_client.create_metering_label(
1032 description=description,
1033 name=data_utils.rand_name("metering-label"))
1034 metering_label = body['metering_label']
1035 cls.metering_labels.append(metering_label)
1036 return metering_label
1037
1038 @classmethod
1039 def create_metering_label_rule(cls, remote_ip_prefix, direction,
1040 metering_label_id):
1041 """Wrapper utility that returns a test metering label rule."""
1042 body = cls.admin_client.create_metering_label_rule(
1043 remote_ip_prefix=remote_ip_prefix, direction=direction,
1044 metering_label_id=metering_label_id)
1045 metering_label_rule = body['metering_label_rule']
1046 cls.metering_label_rules.append(metering_label_rule)
1047 return metering_label_rule
1048
1049 @classmethod
Kailun Qineaaf9782018-12-20 04:45:01 +08001050 def create_network_segment_range(cls, name, shared,
1051 project_id, network_type,
1052 physical_network, minimum,
1053 maximum):
1054 """Wrapper utility that returns a test network segment range."""
1055 network_segment_range_args = {'name': name,
1056 'shared': shared,
1057 'project_id': project_id,
1058 'network_type': network_type,
1059 'physical_network': physical_network,
1060 'minimum': minimum,
1061 'maximum': maximum}
1062 body = cls.admin_client.create_network_segment_range(
1063 **network_segment_range_args)
1064 network_segment_range = body['network_segment_range']
1065 cls.network_segment_ranges.append(network_segment_range)
1066 return network_segment_range
1067
1068 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001069 def create_flavor(cls, name, description, service_type):
1070 """Wrapper utility that returns a test flavor."""
1071 body = cls.admin_client.create_flavor(
1072 description=description, service_type=service_type,
1073 name=name)
1074 flavor = body['flavor']
1075 cls.flavors.append(flavor)
1076 return flavor
1077
1078 @classmethod
1079 def create_service_profile(cls, description, metainfo, driver):
1080 """Wrapper utility that returns a test service profile."""
1081 body = cls.admin_client.create_service_profile(
1082 driver=driver, metainfo=metainfo, description=description)
1083 service_profile = body['service_profile']
1084 cls.service_profiles.append(service_profile)
1085 return service_profile
1086
1087 @classmethod
Nguyen Phuong An67993fc2017-11-24 11:30:25 +07001088 def create_log(cls, name, description=None,
1089 resource_type='security_group', resource_id=None,
1090 target_id=None, event='ALL', enabled=True):
1091 """Wrapper utility that returns a test log object."""
1092 log_args = {'name': name,
1093 'description': description,
1094 'resource_type': resource_type,
1095 'resource_id': resource_id,
1096 'target_id': target_id,
1097 'event': event,
1098 'enabled': enabled}
1099 body = cls.admin_client.create_log(**log_args)
1100 log_object = body['log']
1101 cls.log_objects.append(log_object)
1102 return log_object
1103
1104 @classmethod
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001105 def get_unused_ip(cls, net_id, ip_version=None):
Gary Kotton011345f2016-06-15 08:04:31 -07001106 """Get an unused ip address in a allocation pool of net"""
Daniel Mellado3c0aeab2016-01-29 11:30:25 +00001107 body = cls.admin_client.list_ports(network_id=net_id)
1108 ports = body['ports']
1109 used_ips = []
1110 for port in ports:
1111 used_ips.extend(
1112 [fixed_ip['ip_address'] for fixed_ip in port['fixed_ips']])
1113 body = cls.admin_client.list_subnets(network_id=net_id)
1114 subnets = body['subnets']
1115
1116 for subnet in subnets:
1117 if ip_version and subnet['ip_version'] != ip_version:
1118 continue
1119 cidr = subnet['cidr']
1120 allocation_pools = subnet['allocation_pools']
1121 iterators = []
1122 if allocation_pools:
1123 for allocation_pool in allocation_pools:
1124 iterators.append(netaddr.iter_iprange(
1125 allocation_pool['start'], allocation_pool['end']))
1126 else:
1127 net = netaddr.IPNetwork(cidr)
1128
1129 def _iterip():
1130 for ip in net:
1131 if ip not in (net.network, net.broadcast):
1132 yield ip
1133 iterators.append(iter(_iterip()))
1134
1135 for iterator in iterators:
1136 for ip in iterator:
1137 if str(ip) not in used_ips:
1138 return str(ip)
1139
1140 message = (
1141 "net(%s) has no usable IP address in allocation pools" % net_id)
1142 raise exceptions.InvalidConfiguration(message)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001143
Lajos Katona2f904652018-08-23 14:04:56 +02001144 @classmethod
1145 def create_provider_network(cls, physnet_name, start_segmentation_id,
1146 max_attempts=30):
1147 segmentation_id = start_segmentation_id
Lajos Katona7eb67252019-01-14 12:55:35 +01001148 for attempts in range(max_attempts):
Lajos Katona2f904652018-08-23 14:04:56 +02001149 try:
Lajos Katona7eb67252019-01-14 12:55:35 +01001150 return cls.create_network(
Lajos Katona2f904652018-08-23 14:04:56 +02001151 name=data_utils.rand_name('test_net'),
1152 shared=True,
1153 provider_network_type='vlan',
1154 provider_physical_network=physnet_name,
1155 provider_segmentation_id=segmentation_id)
Lajos Katona2f904652018-08-23 14:04:56 +02001156 except lib_exc.Conflict:
Lajos Katona2f904652018-08-23 14:04:56 +02001157 segmentation_id += 1
1158 if segmentation_id > 4095:
1159 raise lib_exc.TempestException(
1160 "No free segmentation id was found for provider "
1161 "network creation!")
1162 time.sleep(CONF.network.build_interval)
Lajos Katona7eb67252019-01-14 12:55:35 +01001163 LOG.exception("Failed to create provider network after "
1164 "%d attempts", max_attempts)
1165 raise lib_exc.TimeoutException
Lajos Katona2f904652018-08-23 14:04:56 +02001166
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001167
Sławek Kapłońskiff294062016-12-04 15:00:54 +00001168def require_qos_rule_type(rule_type):
1169 def decorator(f):
1170 @functools.wraps(f)
1171 def wrapper(self, *func_args, **func_kwargs):
1172 if rule_type not in self.get_supported_qos_rule_types():
1173 raise self.skipException(
1174 "%s rule type is required." % rule_type)
1175 return f(self, *func_args, **func_kwargs)
1176 return wrapper
1177 return decorator
1178
1179
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001180def _require_sorting(f):
1181 @functools.wraps(f)
1182 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301183 if not tutils.is_extension_enabled("sorting", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001184 self.skipTest('Sorting feature is required')
1185 return f(self, *args, **kwargs)
1186 return inner
1187
1188
1189def _require_pagination(f):
1190 @functools.wraps(f)
1191 def inner(self, *args, **kwargs):
Chandan Kumarc125fd12017-11-15 19:41:01 +05301192 if not tutils.is_extension_enabled("pagination", "network"):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001193 self.skipTest('Pagination feature is required')
1194 return f(self, *args, **kwargs)
1195 return inner
1196
1197
1198class BaseSearchCriteriaTest(BaseNetworkTest):
1199
1200 # This should be defined by subclasses to reflect resource name to test
1201 resource = None
1202
Armando Migliaccio57581c62016-07-01 10:13:19 -07001203 field = 'name'
1204
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001205 # NOTE(ihrachys): some names, like those starting with an underscore (_)
1206 # are sorted differently depending on whether the plugin implements native
1207 # sorting support, or not. So we avoid any such cases here, sticking to
1208 # alphanumeric. Also test a case when there are multiple resources with the
1209 # same name
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001210 resource_names = ('test1', 'abc1', 'test10', '123test') + ('test1',)
1211
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001212 force_tenant_isolation = True
1213
Ihar Hrachyshkaa8fe5a12016-05-24 14:50:58 +02001214 list_kwargs = {}
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001215
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001216 list_as_admin = False
1217
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001218 def assertSameOrder(self, original, actual):
1219 # gracefully handle iterators passed
1220 original = list(original)
1221 actual = list(actual)
1222 self.assertEqual(len(original), len(actual))
1223 for expected, res in zip(original, actual):
Armando Migliaccio57581c62016-07-01 10:13:19 -07001224 self.assertEqual(expected[self.field], res[self.field])
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001225
1226 @utils.classproperty
1227 def plural_name(self):
1228 return '%ss' % self.resource
1229
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001230 @property
1231 def list_client(self):
1232 return self.admin_client if self.list_as_admin else self.client
1233
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001234 def list_method(self, *args, **kwargs):
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001235 method = getattr(self.list_client, 'list_%s' % self.plural_name)
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001236 kwargs.update(self.list_kwargs)
1237 return method(*args, **kwargs)
1238
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001239 def get_bare_url(self, url):
1240 base_url = self.client.base_url
zheng.yong74e760a2019-05-22 14:16:14 +08001241 base_url_normalized = utils.normalize_url(base_url)
1242 url_normalized = utils.normalize_url(url)
1243 self.assertTrue(url_normalized.startswith(base_url_normalized))
1244 return url_normalized[len(base_url_normalized):]
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001245
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001246 @classmethod
1247 def _extract_resources(cls, body):
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001248 return body[cls.plural_name]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001249
1250 def _test_list_sorts(self, direction):
1251 sort_args = {
1252 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001253 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001254 }
1255 body = self.list_method(**sort_args)
1256 resources = self._extract_resources(body)
1257 self.assertNotEmpty(
1258 resources, "%s list returned is empty" % self.resource)
Armando Migliaccio57581c62016-07-01 10:13:19 -07001259 retrieved_names = [res[self.field] for res in resources]
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001260 expected = sorted(retrieved_names)
1261 if direction == constants.SORT_DIRECTION_DESC:
1262 expected = list(reversed(expected))
1263 self.assertEqual(expected, retrieved_names)
1264
1265 @_require_sorting
1266 def _test_list_sorts_asc(self):
1267 self._test_list_sorts(constants.SORT_DIRECTION_ASC)
1268
1269 @_require_sorting
1270 def _test_list_sorts_desc(self):
1271 self._test_list_sorts(constants.SORT_DIRECTION_DESC)
1272
1273 @_require_pagination
1274 def _test_list_pagination(self):
1275 for limit in range(1, len(self.resource_names) + 1):
1276 pagination_args = {
1277 'limit': limit,
1278 }
1279 body = self.list_method(**pagination_args)
1280 resources = self._extract_resources(body)
1281 self.assertEqual(limit, len(resources))
1282
1283 @_require_pagination
1284 def _test_list_no_pagination_limit_0(self):
1285 pagination_args = {
1286 'limit': 0,
1287 }
1288 body = self.list_method(**pagination_args)
1289 resources = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001290 self.assertGreaterEqual(len(resources), len(self.resource_names))
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001291
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001292 def _test_list_pagination_iteratively(self, lister):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001293 # first, collect all resources for later comparison
1294 sort_args = {
1295 'sort_dir': constants.SORT_DIRECTION_ASC,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001296 'sort_key': self.field
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001297 }
1298 body = self.list_method(**sort_args)
1299 expected_resources = self._extract_resources(body)
1300 self.assertNotEmpty(expected_resources)
1301
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001302 resources = lister(
1303 len(expected_resources), sort_args
1304 )
1305
1306 # finally, compare that the list retrieved in one go is identical to
1307 # the one containing pagination results
1308 self.assertSameOrder(expected_resources, resources)
1309
1310 def _list_all_with_marker(self, niterations, sort_args):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001311 # paginate resources one by one, using last fetched resource as a
1312 # marker
1313 resources = []
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001314 for i in range(niterations):
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001315 pagination_args = sort_args.copy()
1316 pagination_args['limit'] = 1
1317 if resources:
1318 pagination_args['marker'] = resources[-1]['id']
1319 body = self.list_method(**pagination_args)
1320 resources_ = self._extract_resources(body)
1321 self.assertEqual(1, len(resources_))
1322 resources.extend(resources_)
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001323 return resources
Ihar Hrachyshka59382252016-04-05 15:54:33 +02001324
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001325 @_require_pagination
1326 @_require_sorting
1327 def _test_list_pagination_with_marker(self):
1328 self._test_list_pagination_iteratively(self._list_all_with_marker)
1329
1330 def _list_all_with_hrefs(self, niterations, sort_args):
1331 # paginate resources one by one, using next href links
1332 resources = []
1333 prev_links = {}
1334
1335 for i in range(niterations):
1336 if prev_links:
1337 uri = self.get_bare_url(prev_links['next'])
1338 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001339 sort_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001340 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001341 self.plural_name, limit=1, **sort_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001342 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001343 self.plural_name, uri
1344 )
1345 resources_ = self._extract_resources(body)
1346 self.assertEqual(1, len(resources_))
1347 resources.extend(resources_)
1348
1349 # The last element is empty and does not contain 'next' link
1350 uri = self.get_bare_url(prev_links['next'])
1351 prev_links, body = self.client.get_uri_with_links(
1352 self.plural_name, uri
1353 )
1354 self.assertNotIn('next', prev_links)
1355
1356 # Now walk backwards and compare results
1357 resources2 = []
1358 for i in range(niterations):
1359 uri = self.get_bare_url(prev_links['previous'])
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001360 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001361 self.plural_name, uri
1362 )
1363 resources_ = self._extract_resources(body)
1364 self.assertEqual(1, len(resources_))
1365 resources2.extend(resources_)
1366
1367 self.assertSameOrder(resources, reversed(resources2))
1368
1369 return resources
1370
1371 @_require_pagination
1372 @_require_sorting
1373 def _test_list_pagination_with_href_links(self):
1374 self._test_list_pagination_iteratively(self._list_all_with_hrefs)
1375
1376 @_require_pagination
1377 @_require_sorting
1378 def _test_list_pagination_page_reverse_with_href_links(
1379 self, direction=constants.SORT_DIRECTION_ASC):
1380 pagination_args = {
1381 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001382 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001383 }
1384 body = self.list_method(**pagination_args)
1385 expected_resources = self._extract_resources(body)
1386
1387 page_size = 2
1388 pagination_args['limit'] = page_size
1389
1390 prev_links = {}
1391 resources = []
1392 num_resources = len(expected_resources)
1393 niterations = int(math.ceil(float(num_resources) / page_size))
1394 for i in range(niterations):
1395 if prev_links:
1396 uri = self.get_bare_url(prev_links['previous'])
1397 else:
Ihar Hrachyshka7f79fe62016-06-07 21:23:44 +02001398 pagination_args.update(self.list_kwargs)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001399 uri = self.list_client.build_uri(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001400 self.plural_name, page_reverse=True, **pagination_args)
Ihar Hrachyshkab7940d92016-06-10 13:44:22 +02001401 prev_links, body = self.list_client.get_uri_with_links(
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001402 self.plural_name, uri
1403 )
1404 resources_ = self._extract_resources(body)
Béla Vancsicsf1806182016-08-23 07:36:18 +02001405 self.assertGreaterEqual(page_size, len(resources_))
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001406 resources.extend(reversed(resources_))
1407
1408 self.assertSameOrder(expected_resources, reversed(resources))
1409
1410 @_require_pagination
1411 @_require_sorting
1412 def _test_list_pagination_page_reverse_asc(self):
1413 self._test_list_pagination_page_reverse(
1414 direction=constants.SORT_DIRECTION_ASC)
1415
1416 @_require_pagination
1417 @_require_sorting
1418 def _test_list_pagination_page_reverse_desc(self):
1419 self._test_list_pagination_page_reverse(
1420 direction=constants.SORT_DIRECTION_DESC)
1421
1422 def _test_list_pagination_page_reverse(self, direction):
1423 pagination_args = {
1424 'sort_dir': direction,
Armando Migliaccio57581c62016-07-01 10:13:19 -07001425 'sort_key': self.field,
Ihar Hrachyshkaaeb03a02016-05-18 20:03:18 +02001426 'limit': 3,
1427 }
1428 body = self.list_method(**pagination_args)
1429 expected_resources = self._extract_resources(body)
1430
1431 pagination_args['limit'] -= 1
1432 pagination_args['marker'] = expected_resources[-1]['id']
1433 pagination_args['page_reverse'] = True
1434 body = self.list_method(**pagination_args)
1435
1436 self.assertSameOrder(
1437 # the last entry is not included in 2nd result when used as a
1438 # marker
1439 expected_resources[:-1],
1440 self._extract_resources(body))
Victor Morales1be97b42016-09-05 08:50:06 -05001441
Hongbin Lu54f55922018-07-12 19:05:39 +00001442 @tutils.requires_ext(extension="filter-validation", service="network")
1443 def _test_list_validation_filters(
1444 self, validation_args, filter_is_valid=True):
1445 if not filter_is_valid:
1446 self.assertRaises(lib_exc.BadRequest, self.list_method,
1447 **validation_args)
1448 else:
1449 body = self.list_method(**validation_args)
1450 resources = self._extract_resources(body)
1451 for resource in resources:
1452 self.assertIn(resource['name'], self.resource_names)