blob: 56a28e2b3d2e8e8fcd0511b465156d9bf283b688 [file] [log] [blame]
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +02001# Copyright 2015 Mirantis Inc.
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
Luigi Toscanod91870b2020-10-03 15:17:23 +020016from collections import OrderedDict
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020017import random
18import re
19
lkuchlan1d1461d2020-08-04 11:19:11 +030020from netaddr import ip
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020021from tempest import config
lkuchlan4a803162022-11-16 11:34:33 +020022from tempest.lib.common.utils import data_utils
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020023import testtools
24
lkuchlan7b63ec72022-12-11 11:57:59 +020025from manila_tempest_tests import utils
26
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020027CONF = config.CONF
Douglas Viroelb7e27e72019-08-06 19:40:37 -030028SHARE_NETWORK_SUBNETS_MICROVERSION = '2.51'
silvacarlossca4dd9f2020-03-11 13:57:18 +000029SHARE_REPLICA_QUOTAS_MICROVERSION = "2.53"
Kiran Pawarfe868062025-06-11 13:33:02 +000030ENCRYPTION_KEYS_QUOTA_MICROVERSION = "2.90"
silvacarloss6e575682020-02-18 19:52:35 -030031EXPERIMENTAL = {'X-OpenStack-Manila-API-Experimental': 'True'}
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020032
33
Luigi Toscanod91870b2020-10-03 15:17:23 +020034def deduplicate(items):
35 """De-duplicate a list of items while preserving the order.
36
37 It is useful when passing a list of items to ddt.data, in order
38 to remove duplicated elements which may be specified as constants.
39 """
40 return list(OrderedDict.fromkeys(items))
41
42
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +020043def get_microversion_as_tuple(microversion_str):
44 """Transforms string-like microversion to two-value tuple of integers.
45
46 Tuple of integers useful for microversion comparisons.
47 """
48 regex = r"^([1-9]\d*)\.([1-9]\d*|0)$"
49 match = re.match(regex, microversion_str)
50 if not match:
51 raise ValueError(
52 "Microversion does not fit template 'x.y' - %s" % microversion_str)
53 return int(match.group(1)), int(match.group(2))
54
55
56def is_microversion_gt(left, right):
57 """Is microversion for left is greater than the right one."""
58 return get_microversion_as_tuple(left) > get_microversion_as_tuple(right)
59
60
61def is_microversion_ge(left, right):
62 """Is microversion for left is greater than or equal to the right one."""
63 return get_microversion_as_tuple(left) >= get_microversion_as_tuple(right)
64
65
66def is_microversion_eq(left, right):
67 """Is microversion for left is equal to the right one."""
68 return get_microversion_as_tuple(left) == get_microversion_as_tuple(right)
69
70
71def is_microversion_ne(left, right):
72 """Is microversion for left is not equal to the right one."""
73 return get_microversion_as_tuple(left) != get_microversion_as_tuple(right)
74
75
76def is_microversion_le(left, right):
77 """Is microversion for left is less than or equal to the right one."""
78 return get_microversion_as_tuple(left) <= get_microversion_as_tuple(right)
79
80
81def is_microversion_lt(left, right):
82 """Is microversion for left is less than the right one."""
83 return get_microversion_as_tuple(left) < get_microversion_as_tuple(right)
84
85
86def is_microversion_supported(microversion):
87 bottom = get_microversion_as_tuple(CONF.share.min_api_microversion)
88 microversion = get_microversion_as_tuple(microversion)
89 top = get_microversion_as_tuple(CONF.share.max_api_microversion)
90 return bottom <= microversion <= top
91
92
93def skip_if_microversion_not_supported(microversion):
94 """Decorator for tests that are microversion-specific."""
95 if not is_microversion_supported(microversion):
96 reason = ("Skipped. Test requires microversion '%s'." % microversion)
97 return testtools.skip(reason)
98 return lambda f: f
99
100
Andrec1a3c0e2022-01-29 14:46:53 +0000101def skip_if_is_microversion_ge(left, right):
102 """Skip if version for left is greater than or equal to the right one."""
103
104 if is_microversion_ge(left, right):
105 reason = ("Skipped. Test requires microversion "
106 "< than '%s'." % right)
107 return testtools.skip(reason)
108 return lambda f: f
109
110
lkuchlana3b6f7a2020-01-07 10:45:45 +0200111def check_skip_if_microversion_not_supported(microversion):
Goutham Pacha Ravia0acf252021-05-27 19:57:55 -0700112 """Callable method for tests that are microversion-specific."""
lkuchlana3b6f7a2020-01-07 10:45:45 +0200113 if not is_microversion_supported(microversion):
114 reason = ("Skipped. Test requires microversion '%s'." % microversion)
115 raise testtools.TestCase.skipException(reason)
116
117
zhongjun72974ff2016-05-04 11:47:03 +0800118def rand_ip(network=False):
Valeriy Ponomaryovfcde7712015-12-14 18:06:13 +0200119 """This uses the TEST-NET-3 range of reserved IP addresses.
120
121 Using this range, which are reserved solely for use in
122 documentation and example source code, should avoid any potential
123 conflicts in real-world testing.
124 """
zhongjun72974ff2016-05-04 11:47:03 +0800125 test_net_3 = '203.0.113.'
haixin48895812020-09-30 13:50:37 +0800126 address = test_net_3 + str(random.randint(0, 255))
zhongjun72974ff2016-05-04 11:47:03 +0800127 if network:
haixin48895812020-09-30 13:50:37 +0800128 mask_length = str(random.randint(24, 32))
zhongjun72974ff2016-05-04 11:47:03 +0800129 address = '/'.join((address, mask_length))
130 ip_network = ip.IPNetwork(address)
haixin48895812020-09-30 13:50:37 +0800131 return '/'.join((str(ip_network.network), mask_length))
zhongjun72974ff2016-05-04 11:47:03 +0800132 return address
133
134
135def rand_ipv6_ip(network=False):
136 """This uses the IPv6 documentation range of 2001:DB8::/32"""
Raissa Sarmento80f5fbf2017-10-16 14:38:36 +0100137 ran_add = ["%x" % random.randrange(0, 16 ** 4) for i in range(6)]
zhongjun72974ff2016-05-04 11:47:03 +0800138 address = "2001:0DB8:" + ":".join(ran_add)
139 if network:
haixin48895812020-09-30 13:50:37 +0800140 mask_length = str(random.randint(32, 128))
zhongjun72974ff2016-05-04 11:47:03 +0800141 address = '/'.join((address, mask_length))
142 ip_network = ip.IPNetwork(address)
haixin48895812020-09-30 13:50:37 +0800143 return '/'.join((str(ip_network.network), mask_length))
zhongjun72974ff2016-05-04 11:47:03 +0800144 return address
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300145
146
lkuchlan7b63ec72022-12-11 11:57:59 +0200147def generate_share_network_data():
148 data = {
149 "name": data_utils.rand_name("sn-name"),
150 "description": data_utils.rand_name("sn-desc"),
151 "neutron_net_id": data_utils.rand_name("net-id"),
152 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
153 }
154 return data
155
156
157def generate_subnet_data():
158 data = {
159 "neutron_net_id": data_utils.rand_name("net-id"),
160 "neutron_subnet_id": data_utils.rand_name("subnet-id"),
161 }
162 return data
163
164
165def generate_security_service_data(set_ou=False):
166 data = {
167 "name": data_utils.rand_name("ss-name"),
168 "description": data_utils.rand_name("ss-desc"),
169 "dns_ip": utils.rand_ip(),
170 "server": utils.rand_ip(),
171 "domain": data_utils.rand_name("ss-domain"),
172 "user": data_utils.rand_name("ss-user"),
173 "password": data_utils.rand_name("ss-password"),
174 }
175 if set_ou:
176 data["ou"] = data_utils.rand_name("ss-ou")
177
178 return data
179
180
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300181def choose_matching_backend(share, pools, share_type):
182 extra_specs = {}
183 # fix extra specs with string values instead of boolean
184 for k, v in share_type['extra_specs'].items():
haixin48895812020-09-30 13:50:37 +0800185 extra_specs[k] = (True if str(v).lower() == 'true'
186 else False if str(v).lower() == 'false'
Rodrigo Barbieric9abf282016-08-24 22:01:31 -0300187 else v)
188 selected_pool = next(
189 (x for x in pools if (x['name'] != share['host'] and all(
190 y in x['capabilities'].items() for y in extra_specs.items()))),
191 None)
192
193 return selected_pool
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300194
195
196def get_configured_extra_specs(variation=None):
197 """Retrieve essential extra specs according to configuration in tempest.
198
199 :param variation: can assume possible values: None to be as configured in
200 tempest; 'opposite_driver_modes' for as configured in tempest but
201 inverse driver mode; 'invalid' for inverse as configured in tempest,
202 ideal for negative tests.
203 :return: dict containing essential extra specs.
204 """
205
206 extra_specs = {'storage_protocol': CONF.share.capability_storage_protocol}
207
208 if variation == 'invalid':
209 extra_specs['driver_handles_share_servers'] = (
210 not CONF.share.multitenancy_enabled)
211 extra_specs['snapshot_support'] = (
212 not CONF.share.capability_snapshot_support)
213
214 elif variation == 'opposite_driver_modes':
215 extra_specs['driver_handles_share_servers'] = (
216 not CONF.share.multitenancy_enabled)
217 extra_specs['snapshot_support'] = (
218 CONF.share.capability_snapshot_support)
219
220 else:
221 extra_specs['driver_handles_share_servers'] = (
222 CONF.share.multitenancy_enabled)
223 extra_specs['snapshot_support'] = (
224 CONF.share.capability_snapshot_support)
Victoria Martinez de la Cruzf6bc6fa2018-02-01 11:27:00 -0500225 extra_specs['create_share_from_snapshot_support'] = (
226 CONF.share.capability_create_share_from_snapshot_support)
Rodrigo Barbieri58d9de32016-09-06 13:16:47 -0300227
228 return extra_specs
Lucio Seki37056942019-01-24 15:40:20 -0200229
230
lkuchlan4a803162022-11-16 11:34:33 +0200231def get_access_rule_data_from_config(protocol):
232 """Get the first available access type/to combination from config.
233
234 This method opportunistically picks the first configured protocol
235 to create the share. Do not use this method in tests where you need
236 to test depth and breadth in the access types and access recipients.
237 """
238
239 if protocol in CONF.share.enable_ip_rules_for_protocols:
240 access_type = "ip"
241 access_to = rand_ip()
242 elif protocol in CONF.share.enable_user_rules_for_protocols:
243 access_type = "user"
244 access_to = CONF.share.username_for_user_rules
245 elif protocol in CONF.share.enable_cert_rules_for_protocols:
246 access_type = "cert"
247 access_to = "client3.com"
248 elif protocol in CONF.share.enable_cephx_rules_for_protocols:
249 access_type = "cephx"
250 access_to = data_utils.rand_name("cephx-id")
251 else:
252 message = "Unrecognized protocol and access rules configuration."
253 raise testtools.TestCase.skipException(message)
254
255 return access_type, access_to
256
257
Douglas Viroelbd4e78c2019-09-02 17:16:30 -0300258def replication_with_multitenancy_support():
259 return (share_network_subnets_are_supported() and
260 CONF.share.multitenancy_enabled)
261
262
Lucio Seki37056942019-01-24 15:40:20 -0200263def skip_if_manage_not_supported_for_version(
264 version=CONF.share.max_api_microversion):
265 if (is_microversion_lt(version, "2.49")
266 and CONF.share.multitenancy_enabled):
267 raise testtools.TestCase.skipException(
268 "Share manage tests with multitenancy are disabled for "
269 "microversion < 2.49")
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300270
271
272def share_network_subnets_are_supported():
273 return is_microversion_supported(SHARE_NETWORK_SUBNETS_MICROVERSION)
274
275
silvacarlossca4dd9f2020-03-11 13:57:18 +0000276def share_replica_quotas_are_supported():
277 return is_microversion_supported(SHARE_REPLICA_QUOTAS_MICROVERSION)
278
279
Kiran Pawarfe868062025-06-11 13:33:02 +0000280def encryption_keys_quota_supported():
281 return is_microversion_supported(ENCRYPTION_KEYS_QUOTA_MICROVERSION)
282
283
Douglas Viroelb7e27e72019-08-06 19:40:37 -0300284def share_network_get_default_subnet(share_network):
285 return next((
286 subnet for subnet in share_network.get('share_network_subnets', [])
287 if subnet['availability_zone'] is None), None)
silvacarloss6e575682020-02-18 19:52:35 -0300288
289
290def get_extra_headers(request_version, graduation_version):
291 headers = None
292 extra_headers = False
293 if is_microversion_lt(request_version, graduation_version):
294 headers = EXPERIMENTAL
295 extra_headers = True
296 return headers, extra_headers