|
0 |
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
1 |
# not use this file except in compliance with the License. You may obtain
|
|
2 |
# a copy of the License at
|
|
3 |
#
|
|
4 |
# http://www.apache.org/licenses/LICENSE-2.0
|
|
5 |
#
|
|
6 |
# Unless required by applicable law or agreed to in writing, software
|
|
7 |
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
8 |
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
9 |
# License for the specific language governing permissions and limitations
|
|
10 |
# under the License.
|
|
11 |
|
|
12 |
from oslo_serialization import base64
|
|
13 |
from oslo_serialization import jsonutils as json
|
|
14 |
|
|
15 |
from tempest.common import waiters
|
|
16 |
from tempest import config
|
|
17 |
from tempest.lib.common.utils import data_utils
|
|
18 |
from tempest.lib import decorators
|
|
19 |
from tempest.lib import exceptions
|
|
20 |
|
|
21 |
from cinder_tempest_plugin.rbac.v3 import base as rbac_base
|
|
22 |
|
|
23 |
CONF = config.CONF
|
|
24 |
|
|
25 |
|
|
26 |
class RbacV3BackupsTests(rbac_base.VolumeV3RbacBaseTests):
|
|
27 |
@classmethod
|
|
28 |
def skip_checks(cls):
|
|
29 |
super(RbacV3BackupsTests, cls).skip_checks()
|
|
30 |
if not CONF.volume_feature_enabled.backup:
|
|
31 |
raise cls.skipException("Cinder backup feature disabled")
|
|
32 |
|
|
33 |
@classmethod
|
|
34 |
def setup_clients(cls):
|
|
35 |
super().setup_clients()
|
|
36 |
admin_client = cls.os_project_admin
|
|
37 |
cls.admin_backups_client = admin_client.backups_client_latest
|
|
38 |
cls.admin_volumes_client = admin_client.volumes_client_latest
|
|
39 |
|
|
40 |
@classmethod
|
|
41 |
def resource_setup(cls):
|
|
42 |
super(RbacV3BackupsTests, cls).resource_setup()
|
|
43 |
cls.volume_id = cls.create_volume(client=cls.admin_volumes_client)
|
|
44 |
backup = cls.create_backup(
|
|
45 |
volume_id=cls.volume_id, backup_client=cls.admin_backups_client
|
|
46 |
)
|
|
47 |
cls.backup_id = backup['id']
|
|
48 |
cls.backup_name = backup['name']
|
|
49 |
|
|
50 |
|
|
51 |
class RbacV3BackupsTests33(RbacV3BackupsTests):
|
|
52 |
"""Test API with microversion greater than 3.3"""
|
|
53 |
min_microversion = '3.3'
|
|
54 |
|
|
55 |
def _encode_backup(self, backup):
|
|
56 |
retval = json.dumps(backup)
|
|
57 |
return base64.encode_as_text(retval)
|
|
58 |
|
|
59 |
def _decode_url(self, backup_url):
|
|
60 |
return json.loads(base64.decode_as_text(backup_url))
|
|
61 |
|
|
62 |
def _modify_backup_url(self, backup_url, changes):
|
|
63 |
backup = self._decode_url(backup_url)
|
|
64 |
backup.update(changes)
|
|
65 |
return self._encode_backup(backup)
|
|
66 |
|
|
67 |
def _list_backups(self, expected_status):
|
|
68 |
"""List all backups"""
|
|
69 |
backups = self.do_request(
|
|
70 |
method='list_backups', expected_status=expected_status
|
|
71 |
)['backups']
|
|
72 |
backup_list = [
|
|
73 |
b['id'] for b in backups if b['name'] == self.backup_name
|
|
74 |
]
|
|
75 |
|
|
76 |
self.assertNotEmpty(
|
|
77 |
backup_list, f"Backup {self.backup_name} not found"
|
|
78 |
)
|
|
79 |
|
|
80 |
def _list_project_backups(self, expected_status):
|
|
81 |
"""List all backups for a project"""
|
|
82 |
backups = self.do_request(
|
|
83 |
method='list_backups',
|
|
84 |
expected_status=expected_status,
|
|
85 |
project_id=self.client.project_id
|
|
86 |
)['backups']
|
|
87 |
backup_list = [
|
|
88 |
b['id'] for b in backups if b['name'] == self.backup_name
|
|
89 |
]
|
|
90 |
|
|
91 |
self.assertNotEmpty(
|
|
92 |
backup_list, f"Backup {self.backup_name} not found"
|
|
93 |
)
|
|
94 |
|
|
95 |
def _show_backup(self, expected_status):
|
|
96 |
"""Show backup details"""
|
|
97 |
backup = self.do_request(
|
|
98 |
method='show_backup',
|
|
99 |
expected_status=expected_status,
|
|
100 |
backup_id=self.backup_id
|
|
101 |
)['backup']
|
|
102 |
self.assertNotEmpty(backup, f"Backup {self.backup_name} not found")
|
|
103 |
|
|
104 |
def _delete_backup(self, expected_status):
|
|
105 |
"""Delete a backup"""
|
|
106 |
add_cleanup = True if expected_status == exceptions.Forbidden\
|
|
107 |
else False
|
|
108 |
volume_id = self.create_volume(client=self.admin_volumes_client)
|
|
109 |
backup = self.create_backup(
|
|
110 |
volume_id=volume_id,
|
|
111 |
backup_client=self.admin_backups_client,
|
|
112 |
add_cleanup=add_cleanup
|
|
113 |
)
|
|
114 |
|
|
115 |
self.do_request(
|
|
116 |
method='delete_backup',
|
|
117 |
expected_status=expected_status,
|
|
118 |
backup_id=backup['id']
|
|
119 |
)
|
|
120 |
|
|
121 |
def _restore_backup(self, expected_status):
|
|
122 |
"""Restore a backup"""
|
|
123 |
res = self.do_request(
|
|
124 |
method='restore_backup',
|
|
125 |
expected_status=expected_status,
|
|
126 |
backup_id=self.backup_id,
|
|
127 |
name='new-backup-vol'
|
|
128 |
)
|
|
129 |
if expected_status != exceptions.Forbidden:
|
|
130 |
waiters.wait_for_volume_resource_status(
|
|
131 |
self.admin_backups_client,
|
|
132 |
self.backup_id, 'available'
|
|
133 |
)
|
|
134 |
self.delete_resource(
|
|
135 |
client=self.admin_volumes_client,
|
|
136 |
volume_id=res['restore']['volume_id']
|
|
137 |
)
|
|
138 |
|
|
139 |
def _create_backup(self, expected_status):
|
|
140 |
"""Create a backup"""
|
|
141 |
res = self.do_request(
|
|
142 |
method='create_backup',
|
|
143 |
expected_status=expected_status,
|
|
144 |
volume_id=self.volume_id
|
|
145 |
)
|
|
146 |
if expected_status != exceptions.Forbidden:
|
|
147 |
backup = res['backup']
|
|
148 |
waiters.wait_for_volume_resource_status(
|
|
149 |
self.admin_backups_client, backup['id'], 'available'
|
|
150 |
)
|
|
151 |
self.admin_backups_client.delete_backup(backup_id=backup['id'])
|
|
152 |
|
|
153 |
def _export_backup(self, expected_status):
|
|
154 |
"""Export a backup"""
|
|
155 |
self.do_request(
|
|
156 |
method='export_backup',
|
|
157 |
expected_status=expected_status,
|
|
158 |
backup_id=self.backup_id
|
|
159 |
)
|
|
160 |
|
|
161 |
def _import_backup(self, expected_status):
|
|
162 |
"""Import a backup"""
|
|
163 |
volume_id = self.create_volume(client=self.admin_volumes_client)
|
|
164 |
backup = self.create_backup(
|
|
165 |
volume_id=volume_id,
|
|
166 |
backup_client=self.admin_backups_client
|
|
167 |
)
|
|
168 |
|
|
169 |
export_backup = (
|
|
170 |
self.admin_backups_client.export_backup(
|
|
171 |
backup['id']
|
|
172 |
)['backup-record']
|
|
173 |
)
|
|
174 |
waiters.wait_for_volume_resource_status(
|
|
175 |
self.admin_backups_client, backup['id'], 'available'
|
|
176 |
)
|
|
177 |
self.assertTrue(
|
|
178 |
export_backup['backup_service'].startswith('cinder.backup.drivers')
|
|
179 |
)
|
|
180 |
# NOTE(ybenshim): Backups are imported with the same backup id
|
|
181 |
# (important for incremental backups among other things), so we cannot
|
|
182 |
# import the exported backup information as it is, because that Backup
|
|
183 |
# ID already exists. So we'll fake the data by changing the backup id
|
|
184 |
# in the exported backup DB info we have retrieved before importing it
|
|
185 |
# back.
|
|
186 |
|
|
187 |
new_id = data_utils.rand_uuid()
|
|
188 |
new_url = self._modify_backup_url(
|
|
189 |
export_backup['backup_url'], {'id': new_id})
|
|
190 |
|
|
191 |
res = self.do_request(
|
|
192 |
method='import_backup',
|
|
193 |
expected_status=expected_status,
|
|
194 |
backup_service=export_backup['backup_service'],
|
|
195 |
backup_url=new_url
|
|
196 |
)
|
|
197 |
if expected_status != exceptions.Forbidden:
|
|
198 |
new_backup = res['backup']
|
|
199 |
waiters.wait_for_volume_resource_status(
|
|
200 |
self.client, new_backup['id'], 'available'
|
|
201 |
)
|
|
202 |
self.delete_resource(
|
|
203 |
client=self.admin_backups_client,
|
|
204 |
backup_id=new_backup['id']
|
|
205 |
)
|
|
206 |
|
|
207 |
def _reset_backup_status(self, expected_status):
|
|
208 |
"""Reset a backup status"""
|
|
209 |
new_status = 'error'
|
|
210 |
volume_id = self.create_volume(client=self.admin_volumes_client)
|
|
211 |
backup = self.create_backup(
|
|
212 |
volume_id=volume_id,
|
|
213 |
backup_client=self.admin_backups_client
|
|
214 |
)
|
|
215 |
|
|
216 |
self.do_request(
|
|
217 |
method='reset_backup_status',
|
|
218 |
expected_status=expected_status,
|
|
219 |
backup_id=backup['id'],
|
|
220 |
status=new_status
|
|
221 |
)
|
|
222 |
|
|
223 |
|
|
224 |
class ProjectReaderTests33(RbacV3BackupsTests33):
|
|
225 |
credentials = ['project_reader', 'project_admin']
|
|
226 |
|
|
227 |
@classmethod
|
|
228 |
def setup_clients(cls):
|
|
229 |
super().setup_clients()
|
|
230 |
cls.client = cls.os_project_reader.backups_client_latest
|
|
231 |
|
|
232 |
@decorators.idempotent_id('9dd02d4b-d6f8-45ca-a95e-534dbd586aab')
|
|
233 |
def test_list_backups(self):
|
|
234 |
"""List all backups"""
|
|
235 |
self._list_backups(expected_status=200)
|
|
236 |
|
|
237 |
@decorators.idempotent_id('9ba2e970-c08b-4c1c-b912-2f3b1373ae6e')
|
|
238 |
def test_list_project_backups(self):
|
|
239 |
"""List all backups for a project"""
|
|
240 |
self._list_project_backups(expected_status=200)
|
|
241 |
|
|
242 |
@decorators.idempotent_id('e88f8971-2892-4a54-80bb-dd21b18f19e9')
|
|
243 |
def test_show_backup(self):
|
|
244 |
"""Show backup details"""
|
|
245 |
self._show_backup(expected_status=200)
|
|
246 |
|
|
247 |
@decorators.skip_because(bug='2017110')
|
|
248 |
@decorators.idempotent_id('a9ab3279-aa5e-4ad8-b740-b80a7769d3f9')
|
|
249 |
def test_delete_backup(self):
|
|
250 |
"""Delete a backup"""
|
|
251 |
self._delete_backup(expected_status=exceptions.Forbidden)
|
|
252 |
|
|
253 |
@decorators.skip_because(bug='2017110')
|
|
254 |
@decorators.idempotent_id('0566fa4a-4e03-4cca-822f-d5a4922da2ab')
|
|
255 |
def test_restore_backup(self):
|
|
256 |
"""Restore a backup"""
|
|
257 |
self._restore_backup(expected_status=exceptions.Forbidden)
|
|
258 |
|
|
259 |
@decorators.skip_because(bug='2017110')
|
|
260 |
@decorators.idempotent_id('bad2514e-18c0-4fa0-9e35-221182ee24cf')
|
|
261 |
def test_create_backup(self):
|
|
262 |
"""Create a backup"""
|
|
263 |
self._create_backup(expected_status=exceptions.Forbidden)
|
|
264 |
|
|
265 |
@decorators.idempotent_id('ab74b8cc-5005-49b4-94f4-994567171b07')
|
|
266 |
def test_export_backup(self):
|
|
267 |
"""Export a backup"""
|
|
268 |
self._export_backup(expected_status=exceptions.Forbidden)
|
|
269 |
|
|
270 |
@decorators.idempotent_id('caaa5756-261a-4d9c-bfc2-788719630a06')
|
|
271 |
def test_import_backup(self):
|
|
272 |
"""Import a backup"""
|
|
273 |
self._import_backup(expected_status=exceptions.Forbidden)
|
|
274 |
|
|
275 |
@decorators.idempotent_id('c832ff77-8f22-499f-a7a3-0834972a1507')
|
|
276 |
def test_reset_backup_status(self):
|
|
277 |
"""Reset a backup status"""
|
|
278 |
self._reset_backup_status(expected_status=exceptions.Forbidden)
|
|
279 |
|
|
280 |
|
|
281 |
class ProjectMemberTests33(RbacV3BackupsTests33):
|
|
282 |
credentials = ['project_member', 'project_admin']
|
|
283 |
|
|
284 |
@classmethod
|
|
285 |
def setup_clients(cls):
|
|
286 |
super().setup_clients()
|
|
287 |
cls.client = cls.os_project_member.backups_client_latest
|
|
288 |
|
|
289 |
@decorators.idempotent_id('5a23c53c-924b-47f6-a5d1-ab6327391c12')
|
|
290 |
def test_list_backups(self):
|
|
291 |
"""List all backups"""
|
|
292 |
self._list_backups(expected_status=200)
|
|
293 |
|
|
294 |
@decorators.idempotent_id('c737bd7b-293c-4d8f-ada9-3b00f7e1adce')
|
|
295 |
def test_list_project_backups(self):
|
|
296 |
"""List all backups for a project"""
|
|
297 |
self._list_project_backups(expected_status=200)
|
|
298 |
|
|
299 |
@decorators.idempotent_id('9944bb15-02fa-4321-97a4-ef8cb5b5fec2')
|
|
300 |
def test_show_backup(self):
|
|
301 |
"""Show backup details"""
|
|
302 |
self._show_backup(expected_status=200)
|
|
303 |
|
|
304 |
@decorators.idempotent_id('c98dfea8-b9f2-4a84-947b-1d857c707789')
|
|
305 |
def test_delete_backup(self):
|
|
306 |
"""Delete a backup"""
|
|
307 |
self._delete_backup(expected_status=202)
|
|
308 |
|
|
309 |
@decorators.idempotent_id('7a6fd066-00e7-4140-866c-8195fbd71e87')
|
|
310 |
def test_restore_backup(self):
|
|
311 |
"""Restore a backup"""
|
|
312 |
self._restore_backup(expected_status=202)
|
|
313 |
|
|
314 |
@decorators.idempotent_id('44644140-4d05-4725-9a4b-6d1a71eda9b7')
|
|
315 |
def test_create_backup(self):
|
|
316 |
"""Create a backup"""
|
|
317 |
self._create_backup(expected_status=202)
|
|
318 |
|
|
319 |
@decorators.idempotent_id('71c7cfaf-7809-4872-b1b2-3feb90b939d4')
|
|
320 |
def test_export_backup(self):
|
|
321 |
"""Export a backup"""
|
|
322 |
self._export_backup(expected_status=exceptions.Forbidden)
|
|
323 |
|
|
324 |
@decorators.idempotent_id('f1c03c1b-2b48-4be0-8b6a-81df8a75f78c')
|
|
325 |
def test_import_backup(self):
|
|
326 |
"""Import a backup"""
|
|
327 |
self._import_backup(expected_status=exceptions.Forbidden)
|
|
328 |
|
|
329 |
@decorators.idempotent_id('307f6fe9-81ed-444a-9aae-99a571d24bf5')
|
|
330 |
def test_reset_backup_status(self):
|
|
331 |
"""Reset a backup status"""
|
|
332 |
self._reset_backup_status(expected_status=exceptions.Forbidden)
|
|
333 |
|
|
334 |
|
|
335 |
class ProjectAdminTests33(RbacV3BackupsTests33):
|
|
336 |
credentials = ['project_admin']
|
|
337 |
|
|
338 |
@classmethod
|
|
339 |
def setup_clients(cls):
|
|
340 |
super().setup_clients()
|
|
341 |
cls.client = cls.os_project_admin.backups_client_latest
|
|
342 |
|
|
343 |
@decorators.idempotent_id('81c579bc-db98-4773-9590-b742d0b00b89')
|
|
344 |
def test_list_backups(self):
|
|
345 |
"""List all backups"""
|
|
346 |
self._list_backups(expected_status=200)
|
|
347 |
|
|
348 |
@decorators.idempotent_id('602dd42d-10df-4eb2-9664-3c9c44e3b35e')
|
|
349 |
def test_list_project_backups(self):
|
|
350 |
"""List all backups for a project"""
|
|
351 |
self._list_project_backups(expected_status=200)
|
|
352 |
|
|
353 |
@decorators.idempotent_id('2094dcee-9585-4745-b045-a0f8c79fbe52')
|
|
354 |
def test_show_backup(self):
|
|
355 |
"""Show backup details"""
|
|
356 |
self._show_backup(expected_status=200)
|
|
357 |
|
|
358 |
@decorators.idempotent_id('b77a8d69-1d12-480d-a83e-5f712d7c2b74')
|
|
359 |
def test_delete_backup(self):
|
|
360 |
"""Delete a backup"""
|
|
361 |
self._delete_backup(expected_status=202)
|
|
362 |
|
|
363 |
@decorators.idempotent_id('7221d2df-338c-4932-be40-ad7166c03db1')
|
|
364 |
def test_restore_backup(self):
|
|
365 |
"""Restore a backup"""
|
|
366 |
self._restore_backup(expected_status=202)
|
|
367 |
|
|
368 |
@decorators.idempotent_id('d347fa21-a5bf-4ce5-ab6b-246c3a06a735')
|
|
369 |
def test_create_backup(self):
|
|
370 |
"""Create a backup"""
|
|
371 |
self._create_backup(expected_status=202)
|
|
372 |
|
|
373 |
@decorators.idempotent_id('e179a062-47d5-4fa8-b359-dedab2afddd8')
|
|
374 |
def test_export_backup(self):
|
|
375 |
"""Export a backup"""
|
|
376 |
self._export_backup(expected_status=200)
|
|
377 |
|
|
378 |
@decorators.idempotent_id('1be80834-2463-49fb-a763-906e8c672fd5')
|
|
379 |
def test_import_backup(self):
|
|
380 |
"""Import a backup"""
|
|
381 |
self._import_backup(expected_status=201)
|
|
382 |
|
|
383 |
@decorators.idempotent_id('88db5943-0053-489a-af30-12b139a38a0b')
|
|
384 |
def test_reset_backup_status(self):
|
|
385 |
"""Reset a backup status"""
|
|
386 |
self._reset_backup_status(expected_status=202)
|
|
387 |
|
|
388 |
|
|
389 |
class RbacV3BackupsTests39(RbacV3BackupsTests):
|
|
390 |
"""Test API with microversion greater than 3.3"""
|
|
391 |
min_microversion = '3.9'
|
|
392 |
|
|
393 |
def _update_backup(self, expected_status):
|
|
394 |
"""Update a backup"""
|
|
395 |
new_description = "Updated backup description"
|
|
396 |
update_kwargs = {"description": new_description}
|
|
397 |
self.do_request(
|
|
398 |
method='update_backup',
|
|
399 |
expected_status=expected_status,
|
|
400 |
backup_id=self.backup_id,
|
|
401 |
**update_kwargs
|
|
402 |
)
|
|
403 |
if expected_status != exceptions.Forbidden:
|
|
404 |
backup = self.admin_backups_client.show_backup(
|
|
405 |
backup_id=self.backup_id
|
|
406 |
)['backup']
|
|
407 |
self.assertEqual(
|
|
408 |
backup['description'], new_description,
|
|
409 |
f"Backup {backup['name']} description should be "
|
|
410 |
f"{new_description}"
|
|
411 |
)
|
|
412 |
|
|
413 |
|
|
414 |
class ProjectReaderTests39(RbacV3BackupsTests39):
|
|
415 |
credentials = ['project_reader', 'project_admin']
|
|
416 |
|
|
417 |
@classmethod
|
|
418 |
def setup_clients(cls):
|
|
419 |
super().setup_clients()
|
|
420 |
cls.client = cls.os_project_reader.backups_client_latest
|
|
421 |
|
|
422 |
@decorators.idempotent_id('50ccc892-6ed0-4015-b181-9f64ffa45f33')
|
|
423 |
@decorators.skip_because(bug='2017110')
|
|
424 |
def test_update_backup(self):
|
|
425 |
"""Update a backup"""
|
|
426 |
self._update_backup(expected_status=exceptions.Forbidden)
|
|
427 |
|
|
428 |
|
|
429 |
class ProjectMemberTests39(RbacV3BackupsTests39):
|
|
430 |
credentials = ['project_member', 'project_admin']
|
|
431 |
|
|
432 |
@classmethod
|
|
433 |
def setup_clients(cls):
|
|
434 |
super().setup_clients()
|
|
435 |
cls.client = cls.os_project_member.backups_client_latest
|
|
436 |
|
|
437 |
@decorators.idempotent_id('a1cdd6f2-e9bc-4f6a-a0e6-2493ac6f9f27')
|
|
438 |
def test_update_backup(self):
|
|
439 |
"""Update a backup"""
|
|
440 |
self._update_backup(expected_status=200)
|
|
441 |
|
|
442 |
|
|
443 |
class ProjectAdminTests39(RbacV3BackupsTests39):
|
|
444 |
credentials = ['project_admin']
|
|
445 |
|
|
446 |
@classmethod
|
|
447 |
def setup_clients(cls):
|
|
448 |
super().setup_clients()
|
|
449 |
cls.client = cls.os_project_admin.backups_client_latest
|
|
450 |
|
|
451 |
@decorators.idempotent_id('2686eecf-e3cd-4f23-8771-aa040ed9be4b')
|
|
452 |
def test_update_backup(self):
|
|
453 |
"""Update a backup"""
|
|
454 |
self._update_backup(expected_status=200)
|