]> git.proxmox.com Git - ceph.git/blame - ceph/qa/tasks/mgr/dashboard/test_rbd.py
import 15.2.4
[ceph.git] / ceph / qa / tasks / mgr / dashboard / test_rbd.py
CommitLineData
11fdf7f2
TL
1# -*- coding: utf-8 -*-
2# pylint: disable=too-many-public-methods
3
4from __future__ import absolute_import
5
6import time
7
e306af50 8from tasks.mgr.dashboard.helper import DashboardTestCase, JObj, JLeaf, JList
11fdf7f2
TL
9
10
11class RbdTest(DashboardTestCase):
12 AUTH_ROLES = ['pool-manager', 'block-manager']
13
14 @classmethod
15 def create_pool(cls, name, pg_num, pool_type, application='rbd'):
16 data = {
17 'pool': name,
18 'pg_num': pg_num,
19 'pool_type': pool_type,
20 'application_metadata': [application]
21 }
22 if pool_type == 'erasure':
23 data['flags'] = ['ec_overwrites']
24 cls._task_post("/api/pool", data)
25
26 @DashboardTestCase.RunAs('test', 'test', [{'rbd-image': ['create', 'update', 'delete']}])
27 def test_read_access_permissions(self):
28 self._get('/api/block/image')
29 self.assertStatus(403)
9f95a23c 30 self.get_image('pool', None, 'image')
11fdf7f2
TL
31 self.assertStatus(403)
32
33 @DashboardTestCase.RunAs('test', 'test', [{'rbd-image': ['read', 'update', 'delete']}])
34 def test_create_access_permissions(self):
9f95a23c 35 self.create_image('pool', None, 'name', 0)
11fdf7f2 36 self.assertStatus(403)
9f95a23c 37 self.create_snapshot('pool', None, 'image', 'snapshot')
11fdf7f2 38 self.assertStatus(403)
9f95a23c 39 self.copy_image('src_pool', None, 'src_image', 'dest_pool', None, 'dest_image')
11fdf7f2 40 self.assertStatus(403)
9f95a23c 41 self.clone_image('parent_pool', None, 'parent_image', 'parent_snap', 'pool', None, 'name')
11fdf7f2
TL
42 self.assertStatus(403)
43
44 @DashboardTestCase.RunAs('test', 'test', [{'rbd-image': ['read', 'create', 'delete']}])
45 def test_update_access_permissions(self):
9f95a23c 46 self.edit_image('pool', None, 'image')
11fdf7f2 47 self.assertStatus(403)
9f95a23c 48 self.update_snapshot('pool', None, 'image', 'snapshot', None, None)
11fdf7f2 49 self.assertStatus(403)
9f95a23c 50 self.rollback_snapshot('rbd', None, 'rollback_img', 'snap1')
11fdf7f2 51 self.assertStatus(403)
9f95a23c 52 self.flatten_image('pool', None, 'image')
11fdf7f2
TL
53 self.assertStatus(403)
54
55 @DashboardTestCase.RunAs('test', 'test', [{'rbd-image': ['read', 'create', 'update']}])
56 def test_delete_access_permissions(self):
9f95a23c 57 self.remove_image('pool', None, 'image')
11fdf7f2 58 self.assertStatus(403)
9f95a23c 59 self.remove_snapshot('pool', None, 'image', 'snapshot')
11fdf7f2
TL
60 self.assertStatus(403)
61
62 @classmethod
9f95a23c
TL
63 def create_namespace(cls, pool, namespace):
64 data = {'namespace': namespace}
65 return cls._post('/api/block/pool/{}/namespace'.format(pool), data)
66
67 @classmethod
68 def remove_namespace(cls, pool, namespace):
69 return cls._delete('/api/block/pool/{}/namespace/{}'.format(pool, namespace))
70
71 @classmethod
72 def create_image(cls, pool, namespace, name, size, **kwargs):
73 data = {'name': name, 'pool_name': pool, 'namespace': namespace, 'size': size}
11fdf7f2
TL
74 data.update(kwargs)
75 return cls._task_post('/api/block/image', data)
76
77 @classmethod
9f95a23c
TL
78 def get_image(cls, pool, namespace, name):
79 namespace = '{}%2F'.format(namespace) if namespace else ''
80 return cls._get('/api/block/image/{}%2F{}{}'.format(pool, namespace, name))
81
82 @classmethod
83 def clone_image(cls, parent_pool, parent_namespace, parent_image, parent_snap, pool, namespace,
84 name, **kwargs):
11fdf7f2 85 # pylint: disable=too-many-arguments
9f95a23c 86 data = {'child_image_name': name, 'child_namespace': namespace, 'child_pool_name': pool}
11fdf7f2 87 data.update(kwargs)
9f95a23c
TL
88 parent_namespace = '{}%2F'.format(parent_namespace) if parent_namespace else ''
89 return cls._task_post('/api/block/image/{}%2F{}{}/snap/{}/clone'
90 .format(parent_pool, parent_namespace, parent_image, parent_snap),
11fdf7f2
TL
91 data)
92
93 @classmethod
9f95a23c
TL
94 def copy_image(cls, src_pool, src_namespace, src_image, dest_pool, dest_namespace, dest_image,
95 **kwargs):
11fdf7f2 96 # pylint: disable=too-many-arguments
9f95a23c
TL
97 data = {'dest_image_name': dest_image,
98 'dest_pool_name': dest_pool,
99 'dest_namespace': dest_namespace}
11fdf7f2 100 data.update(kwargs)
9f95a23c
TL
101 src_namespace = '{}%2F'.format(src_namespace) if src_namespace else ''
102 return cls._task_post('/api/block/image/{}%2F{}{}/copy'
103 .format(src_pool, src_namespace, src_image), data)
11fdf7f2
TL
104
105 @classmethod
9f95a23c
TL
106 def remove_image(cls, pool, namespace, image):
107 namespace = '{}%2F'.format(namespace) if namespace else ''
108 return cls._task_delete('/api/block/image/{}%2F{}{}'.format(pool, namespace, image))
11fdf7f2
TL
109
110 # pylint: disable=too-many-arguments
111 @classmethod
9f95a23c 112 def edit_image(cls, pool, namespace, image, name=None, size=None, features=None, **kwargs):
11fdf7f2 113 kwargs.update({'name': name, 'size': size, 'features': features})
9f95a23c
TL
114 namespace = '{}%2F'.format(namespace) if namespace else ''
115 return cls._task_put('/api/block/image/{}%2F{}{}'.format(pool, namespace, image), kwargs)
11fdf7f2
TL
116
117 @classmethod
9f95a23c
TL
118 def flatten_image(cls, pool, namespace, image):
119 namespace = '{}%2F'.format(namespace) if namespace else ''
120 return cls._task_post('/api/block/image/{}%2F{}{}/flatten'.format(pool, namespace, image))
11fdf7f2
TL
121
122 @classmethod
9f95a23c
TL
123 def create_snapshot(cls, pool, namespace, image, snapshot):
124 namespace = '{}%2F'.format(namespace) if namespace else ''
125 return cls._task_post('/api/block/image/{}%2F{}{}/snap'.format(pool, namespace, image),
11fdf7f2
TL
126 {'snapshot_name': snapshot})
127
128 @classmethod
9f95a23c
TL
129 def remove_snapshot(cls, pool, namespace, image, snapshot):
130 namespace = '{}%2F'.format(namespace) if namespace else ''
131 return cls._task_delete('/api/block/image/{}%2F{}{}/snap/{}'.format(pool, namespace, image,
132 snapshot))
11fdf7f2
TL
133
134 @classmethod
9f95a23c
TL
135 def update_snapshot(cls, pool, namespace, image, snapshot, new_name, is_protected):
136 namespace = '{}%2F'.format(namespace) if namespace else ''
137 return cls._task_put('/api/block/image/{}%2F{}{}/snap/{}'.format(pool, namespace, image,
138 snapshot),
11fdf7f2
TL
139 {'new_snap_name': new_name, 'is_protected': is_protected})
140
9f95a23c
TL
141 @classmethod
142 def rollback_snapshot(cls, pool, namespace, image, snapshot):
143 namespace = '{}%2F'.format(namespace) if namespace else ''
144 return cls._task_post('/api/block/image/{}%2F{}{}/snap/{}/rollback'.format(pool,
145 namespace,
146 image,
147 snapshot))
148
11fdf7f2
TL
149 @classmethod
150 def setUpClass(cls):
151 super(RbdTest, cls).setUpClass()
eafe8130
TL
152 cls.create_pool('rbd', 2**3, 'replicated')
153 cls.create_pool('rbd_iscsi', 2**3, 'replicated')
11fdf7f2 154
9f95a23c
TL
155 cls.create_image('rbd', None, 'img1', 2**30)
156 cls.create_image('rbd', None, 'img2', 2*2**30)
157 cls.create_image('rbd_iscsi', None, 'img1', 2**30)
158 cls.create_image('rbd_iscsi', None, 'img2', 2*2**30)
11fdf7f2
TL
159
160 osd_metadata = cls.ceph_cluster.mon_manager.get_osd_metadata()
161 cls.bluestore_support = True
162 for osd in osd_metadata:
163 if osd['osd_objectstore'] != 'bluestore':
164 cls.bluestore_support = False
165 break
166
167 @classmethod
168 def tearDownClass(cls):
169 super(RbdTest, cls).tearDownClass()
170 cls._ceph_cmd(['osd', 'pool', 'delete', 'rbd', 'rbd', '--yes-i-really-really-mean-it'])
171 cls._ceph_cmd(['osd', 'pool', 'delete', 'rbd_iscsi', 'rbd_iscsi',
172 '--yes-i-really-really-mean-it'])
173 cls._ceph_cmd(['osd', 'pool', 'delete', 'rbd_data', 'rbd_data',
174 '--yes-i-really-really-mean-it'])
175
176 @classmethod
177 def create_image_in_trash(cls, pool, name, delay=0):
9f95a23c
TL
178 cls.create_image(pool, None, name, 10240)
179 img = cls._get('/api/block/image/{}%2F{}'.format(pool, name))
11fdf7f2 180
9f95a23c
TL
181 cls._task_post("/api/block/image/{}%2F{}/move_trash".format(pool, name),
182 {'delay': delay})
11fdf7f2
TL
183
184 return img['id']
185
186 @classmethod
9f95a23c
TL
187 def remove_trash(cls, pool, image_id, force=False):
188 return cls._task_delete('/api/block/image/trash/{}%2F{}/?force={}'.format(
189 pool, image_id, force))
190
191 @classmethod
192 def restore_trash(cls, pool, namespace, image_id, new_image_name):
193 data = {'new_image_name': new_image_name}
194 namespace = '{}%2F'.format(namespace) if namespace else ''
195 return cls._task_post('/api/block/image/trash/{}%2F{}{}/restore'.format(pool,
196 namespace,
197 image_id), data)
198
199 @classmethod
200 def purge_trash(cls, pool):
201 return cls._task_post('/api/block/image/trash/purge?pool_name={}'.format(pool))
11fdf7f2
TL
202
203 @classmethod
204 def get_trash(cls, pool, image_id):
205 trash = cls._get('/api/block/image/trash/?pool_name={}'.format(pool))
206 if isinstance(trash, list):
207 for pool in trash:
208 for image in pool['value']:
209 if image['id'] == image_id:
210 return image
211
212 return None
213
214 def _validate_image(self, img, **kwargs):
215 """
216 Example of an RBD image json:
217
218 {
219 "size": 1073741824,
220 "obj_size": 4194304,
221 "num_objs": 256,
222 "order": 22,
223 "block_name_prefix": "rbd_data.10ae2ae8944a",
224 "name": "img1",
225 "pool_name": "rbd",
226 "features": 61,
227 "features_name": ["deep-flatten", "exclusive-lock", "fast-diff", "layering",
228 "object-map"]
229 }
230 """
231 schema = JObj(sub_elems={
232 'size': JLeaf(int),
233 'obj_size': JLeaf(int),
234 'num_objs': JLeaf(int),
235 'order': JLeaf(int),
236 'block_name_prefix': JLeaf(str),
237 'name': JLeaf(str),
238 'id': JLeaf(str),
239 'pool_name': JLeaf(str),
9f95a23c 240 'namespace': JLeaf(str, none=True),
11fdf7f2
TL
241 'features': JLeaf(int),
242 'features_name': JList(JLeaf(str)),
243 'stripe_count': JLeaf(int, none=True),
244 'stripe_unit': JLeaf(int, none=True),
245 'parent': JObj(sub_elems={'pool_name': JLeaf(str),
9f95a23c 246 'pool_namespace': JLeaf(str, none=True),
11fdf7f2
TL
247 'image_name': JLeaf(str),
248 'snap_name': JLeaf(str)}, none=True),
249 'data_pool': JLeaf(str, none=True),
250 'snapshots': JList(JLeaf(dict)),
251 'timestamp': JLeaf(str, none=True),
252 'disk_usage': JLeaf(int, none=True),
253 'total_disk_usage': JLeaf(int, none=True),
254 'configuration': JList(JObj(sub_elems={
255 'name': JLeaf(str),
256 'source': JLeaf(int),
257 'value': JLeaf(str),
258 })),
259 })
260 self.assertSchema(img, schema)
261
262 for k, v in kwargs.items():
263 if isinstance(v, list):
264 self.assertSetEqual(set(img[k]), set(v))
265 else:
266 self.assertEqual(img[k], v)
267
268 def _validate_snapshot(self, snap, **kwargs):
269 self.assertIn('id', snap)
270 self.assertIn('name', snap)
271 self.assertIn('is_protected', snap)
272 self.assertIn('timestamp', snap)
273 self.assertIn('size', snap)
274 self.assertIn('children', snap)
275
276 for k, v in kwargs.items():
277 if isinstance(v, list):
278 self.assertSetEqual(set(snap[k]), set(v))
279 else:
280 self.assertEqual(snap[k], v)
281
282 def _validate_snapshot_list(self, snap_list, snap_name=None, **kwargs):
283 found = False
284 for snap in snap_list:
285 self.assertIn('name', snap)
286 if snap_name and snap['name'] == snap_name:
287 found = True
288 self._validate_snapshot(snap, **kwargs)
289 break
290 if snap_name and not found:
291 self.fail("Snapshot {} not found".format(snap_name))
292
293 def test_list(self):
294 data = self._view_cache_get('/api/block/image')
295 self.assertStatus(200)
296 self.assertEqual(len(data), 2)
297
298 for pool_view in data:
299 self.assertEqual(pool_view['status'], 0)
300 self.assertIsNotNone(pool_view['value'])
301 self.assertIn('pool_name', pool_view)
302 self.assertIn(pool_view['pool_name'], ['rbd', 'rbd_iscsi'])
303 image_list = pool_view['value']
304 self.assertEqual(len(image_list), 2)
305
306 for img in image_list:
307 self.assertIn('name', img)
308 self.assertIn('pool_name', img)
309 self.assertIn(img['pool_name'], ['rbd', 'rbd_iscsi'])
310 if img['name'] == 'img1':
311 self._validate_image(img, size=1073741824,
312 num_objs=256, obj_size=4194304,
313 features_name=['deep-flatten',
314 'exclusive-lock',
315 'fast-diff',
316 'layering',
317 'object-map'])
318 elif img['name'] == 'img2':
319 self._validate_image(img, size=2147483648,
320 num_objs=512, obj_size=4194304,
321 features_name=['deep-flatten',
322 'exclusive-lock',
323 'fast-diff',
324 'layering',
325 'object-map'])
326 else:
327 assert False, "Unexcepted image '{}' in result list".format(img['name'])
328
329 def test_create(self):
330 rbd_name = 'test_rbd'
9f95a23c 331 self.create_image('rbd', None, rbd_name, 10240)
11fdf7f2
TL
332 self.assertStatus(201)
333
9f95a23c 334 img = self.get_image('rbd', None, 'test_rbd')
11fdf7f2
TL
335 self.assertStatus(200)
336
337 self._validate_image(img, name=rbd_name, size=10240,
338 num_objs=1, obj_size=4194304,
339 features_name=['deep-flatten',
340 'exclusive-lock',
341 'fast-diff', 'layering',
342 'object-map'])
343
9f95a23c 344 self.remove_image('rbd', None, rbd_name)
11fdf7f2
TL
345
346 def test_create_with_configuration(self):
347 pool = 'rbd'
348 image_name = 'image_with_config'
349 size = 10240
350 configuration = {
351 'rbd_qos_bps_limit': 10240,
352 'rbd_qos_bps_burst': 10240 * 2,
353 }
354 expected = [{
355 'name': 'rbd_qos_bps_limit',
356 'source': 2,
357 'value': str(10240),
358 }, {
359 'name': 'rbd_qos_bps_burst',
360 'source': 2,
361 'value': str(10240 * 2),
362 }]
363
9f95a23c 364 self.create_image(pool, None, image_name, size, configuration=configuration)
11fdf7f2 365 self.assertStatus(201)
9f95a23c 366 img = self.get_image('rbd', None, image_name)
11fdf7f2
TL
367 self.assertStatus(200)
368 for conf in expected:
369 self.assertIn(conf, img['configuration'])
370
9f95a23c 371 self.remove_image(pool, None, image_name)
11fdf7f2
TL
372
373 def test_create_rbd_in_data_pool(self):
374 if not self.bluestore_support:
375 self.skipTest('requires bluestore cluster')
376
eafe8130 377 self.create_pool('data_pool', 2**4, 'erasure')
11fdf7f2
TL
378
379 rbd_name = 'test_rbd_in_data_pool'
9f95a23c 380 self.create_image('rbd', None, rbd_name, 10240, data_pool='data_pool')
11fdf7f2
TL
381 self.assertStatus(201)
382
9f95a23c 383 img = self.get_image('rbd', None, 'test_rbd_in_data_pool')
11fdf7f2
TL
384 self.assertStatus(200)
385
386 self._validate_image(img, name=rbd_name, size=10240,
387 num_objs=1, obj_size=4194304,
388 data_pool='data_pool',
389 features_name=['data-pool', 'deep-flatten',
390 'exclusive-lock',
391 'fast-diff', 'layering',
392 'object-map'])
393
9f95a23c 394 self.remove_image('rbd', None, rbd_name)
11fdf7f2
TL
395 self.assertStatus(204)
396 self._ceph_cmd(['osd', 'pool', 'delete', 'data_pool', 'data_pool',
397 '--yes-i-really-really-mean-it'])
398
399 def test_create_rbd_twice(self):
9f95a23c 400 res = self.create_image('rbd', None, 'test_rbd_twice', 10240)
11fdf7f2 401
9f95a23c 402 res = self.create_image('rbd', None, 'test_rbd_twice', 10240)
11fdf7f2
TL
403 self.assertStatus(400)
404 self.assertEqual(res, {"code": '17', 'status': 400, "component": "rbd",
9f95a23c 405 "detail": "[errno 17] RBD image already exists (error creating image)",
11fdf7f2 406 'task': {'name': 'rbd/create',
9f95a23c 407 'metadata': {'pool_name': 'rbd', 'namespace': None,
11fdf7f2 408 'image_name': 'test_rbd_twice'}}})
9f95a23c 409 self.remove_image('rbd', None, 'test_rbd_twice')
11fdf7f2
TL
410 self.assertStatus(204)
411
412 def test_snapshots_and_clone_info(self):
9f95a23c
TL
413 self.create_snapshot('rbd', None, 'img1', 'snap1')
414 self.create_snapshot('rbd', None, 'img1', 'snap2')
11fdf7f2
TL
415 self._rbd_cmd(['snap', 'protect', 'rbd/img1@snap1'])
416 self._rbd_cmd(['clone', 'rbd/img1@snap1', 'rbd_iscsi/img1_clone'])
417
9f95a23c 418 img = self.get_image('rbd', None, 'img1')
11fdf7f2
TL
419 self.assertStatus(200)
420 self._validate_image(img, name='img1', size=1073741824,
421 num_objs=256, obj_size=4194304, parent=None,
422 features_name=['deep-flatten', 'exclusive-lock',
423 'fast-diff', 'layering',
424 'object-map'])
425 for snap in img['snapshots']:
426 if snap['name'] == 'snap1':
427 self._validate_snapshot(snap, is_protected=True)
428 self.assertEqual(len(snap['children']), 1)
429 self.assertDictEqual(snap['children'][0],
430 {'pool_name': 'rbd_iscsi',
431 'image_name': 'img1_clone'})
432 elif snap['name'] == 'snap2':
433 self._validate_snapshot(snap, is_protected=False)
434
9f95a23c 435 img = self.get_image('rbd_iscsi', None, 'img1_clone')
11fdf7f2
TL
436 self.assertStatus(200)
437 self._validate_image(img, name='img1_clone', size=1073741824,
438 num_objs=256, obj_size=4194304,
9f95a23c
TL
439 parent={'pool_name': 'rbd', 'pool_namespace': '',
440 'image_name': 'img1', 'snap_name': 'snap1'},
11fdf7f2
TL
441 features_name=['deep-flatten', 'exclusive-lock',
442 'fast-diff', 'layering',
443 'object-map'])
9f95a23c 444 self.remove_image('rbd_iscsi', None, 'img1_clone')
11fdf7f2
TL
445 self.assertStatus(204)
446
447 def test_disk_usage(self):
448 self._rbd_cmd(['bench', '--io-type', 'write', '--io-total', '50M', 'rbd/img2'])
9f95a23c 449 self.create_snapshot('rbd', None, 'img2', 'snap1')
11fdf7f2 450 self._rbd_cmd(['bench', '--io-type', 'write', '--io-total', '20M', 'rbd/img2'])
9f95a23c 451 self.create_snapshot('rbd', None, 'img2', 'snap2')
11fdf7f2 452 self._rbd_cmd(['bench', '--io-type', 'write', '--io-total', '10M', 'rbd/img2'])
9f95a23c 453 self.create_snapshot('rbd', None, 'img2', 'snap3')
11fdf7f2 454 self._rbd_cmd(['bench', '--io-type', 'write', '--io-total', '5M', 'rbd/img2'])
9f95a23c 455 img = self.get_image('rbd', None, 'img2')
11fdf7f2
TL
456 self.assertStatus(200)
457 self._validate_image(img, name='img2', size=2147483648,
458 total_disk_usage=268435456, disk_usage=67108864)
459
460 def test_delete_non_existent_image(self):
9f95a23c
TL
461 res = self.remove_image('rbd', None, 'i_dont_exist')
462 self.assertStatus(404)
463 self.assertEqual(res, {u'code': 404, "status": 404, "component": None,
464 "detail": "(404, 'Image not found')",
11fdf7f2 465 'task': {'name': 'rbd/delete',
9f95a23c 466 'metadata': {'image_spec': 'rbd/i_dont_exist'}}})
11fdf7f2
TL
467
468 def test_image_delete(self):
9f95a23c 469 self.create_image('rbd', None, 'delete_me', 2**30)
11fdf7f2 470 self.assertStatus(201)
9f95a23c 471 self.create_snapshot('rbd', None, 'delete_me', 'snap1')
11fdf7f2 472 self.assertStatus(201)
9f95a23c 473 self.create_snapshot('rbd', None, 'delete_me', 'snap2')
11fdf7f2
TL
474 self.assertStatus(201)
475
9f95a23c 476 img = self.get_image('rbd', None, 'delete_me')
11fdf7f2
TL
477 self.assertStatus(200)
478 self._validate_image(img, name='delete_me', size=2**30)
479 self.assertEqual(len(img['snapshots']), 2)
480
9f95a23c 481 self.remove_snapshot('rbd', None, 'delete_me', 'snap1')
11fdf7f2 482 self.assertStatus(204)
9f95a23c 483 self.remove_snapshot('rbd', None, 'delete_me', 'snap2')
11fdf7f2
TL
484 self.assertStatus(204)
485
9f95a23c 486 img = self.get_image('rbd', None, 'delete_me')
11fdf7f2
TL
487 self.assertStatus(200)
488 self._validate_image(img, name='delete_me', size=2**30)
489 self.assertEqual(len(img['snapshots']), 0)
490
9f95a23c
TL
491 self.remove_image('rbd', None, 'delete_me')
492 self.assertStatus(204)
493
494 def test_image_delete_with_snapshot(self):
495 self.create_image('rbd', None, 'delete_me', 2**30)
496 self.assertStatus(201)
497 self.create_snapshot('rbd', None, 'delete_me', 'snap1')
498 self.assertStatus(201)
499 self.create_snapshot('rbd', None, 'delete_me', 'snap2')
500 self.assertStatus(201)
501
502 img = self.get_image('rbd', None, 'delete_me')
503 self.assertStatus(200)
504 self._validate_image(img, name='delete_me', size=2**30)
505 self.assertEqual(len(img['snapshots']), 2)
506
507 self.remove_image('rbd', None, 'delete_me')
11fdf7f2
TL
508 self.assertStatus(204)
509
510 def test_image_rename(self):
9f95a23c 511 self.create_image('rbd', None, 'edit_img', 2**30)
11fdf7f2 512 self.assertStatus(201)
9f95a23c 513 self.get_image('rbd', None, 'edit_img')
11fdf7f2 514 self.assertStatus(200)
9f95a23c 515 self.edit_image('rbd', None, 'edit_img', 'new_edit_img')
11fdf7f2 516 self.assertStatus(200)
9f95a23c 517 self.get_image('rbd', None, 'edit_img')
11fdf7f2 518 self.assertStatus(404)
9f95a23c 519 self.get_image('rbd', None, 'new_edit_img')
11fdf7f2 520 self.assertStatus(200)
9f95a23c 521 self.remove_image('rbd', None, 'new_edit_img')
11fdf7f2
TL
522 self.assertStatus(204)
523
524 def test_image_resize(self):
9f95a23c 525 self.create_image('rbd', None, 'edit_img', 2**30)
11fdf7f2 526 self.assertStatus(201)
9f95a23c 527 img = self.get_image('rbd', None, 'edit_img')
11fdf7f2
TL
528 self.assertStatus(200)
529 self._validate_image(img, size=2**30)
9f95a23c 530 self.edit_image('rbd', None, 'edit_img', size=2*2**30)
11fdf7f2 531 self.assertStatus(200)
9f95a23c 532 img = self.get_image('rbd', None, 'edit_img')
11fdf7f2
TL
533 self.assertStatus(200)
534 self._validate_image(img, size=2*2**30)
9f95a23c 535 self.remove_image('rbd', None, 'edit_img')
11fdf7f2
TL
536 self.assertStatus(204)
537
538 def test_image_change_features(self):
9f95a23c 539 self.create_image('rbd', None, 'edit_img', 2**30, features=["layering"])
11fdf7f2 540 self.assertStatus(201)
9f95a23c 541 img = self.get_image('rbd', None, 'edit_img')
11fdf7f2
TL
542 self.assertStatus(200)
543 self._validate_image(img, features_name=["layering"])
9f95a23c 544 self.edit_image('rbd', None, 'edit_img',
11fdf7f2 545 features=["fast-diff", "object-map", "exclusive-lock"])
81eedcae 546 self.assertStatus(200)
9f95a23c 547 img = self.get_image('rbd', None, 'edit_img')
11fdf7f2
TL
548 self.assertStatus(200)
549 self._validate_image(img, features_name=['exclusive-lock',
550 'fast-diff', 'layering',
551 'object-map'])
9f95a23c 552 self.edit_image('rbd', None, 'edit_img',
11fdf7f2 553 features=["journaling", "exclusive-lock"])
81eedcae 554 self.assertStatus(200)
9f95a23c 555 img = self.get_image('rbd', None, 'edit_img')
11fdf7f2
TL
556 self.assertStatus(200)
557 self._validate_image(img, features_name=['exclusive-lock',
558 'journaling', 'layering'])
9f95a23c 559 self.remove_image('rbd', None, 'edit_img')
11fdf7f2
TL
560 self.assertStatus(204)
561
562 def test_image_change_config(self):
563 pool = 'rbd'
564 image = 'image_with_config'
565 initial_conf = {
566 'rbd_qos_bps_limit': 10240,
567 'rbd_qos_write_iops_limit': None
568 }
569 initial_expect = [{
570 'name': 'rbd_qos_bps_limit',
571 'source': 2,
572 'value': '10240',
573 }, {
574 'name': 'rbd_qos_write_iops_limit',
575 'source': 0,
576 'value': '0',
577 }]
578 new_conf = {
579 'rbd_qos_bps_limit': 0,
580 'rbd_qos_bps_burst': 20480,
581 'rbd_qos_write_iops_limit': None
582 }
583 new_expect = [{
584 'name': 'rbd_qos_bps_limit',
585 'source': 2,
586 'value': '0',
587 }, {
588 'name': 'rbd_qos_bps_burst',
589 'source': 2,
590 'value': '20480',
591 }, {
592 'name': 'rbd_qos_write_iops_limit',
593 'source': 0,
594 'value': '0',
595 }]
596
9f95a23c 597 self.create_image(pool, None, image, 2**30, configuration=initial_conf)
11fdf7f2 598 self.assertStatus(201)
9f95a23c 599 img = self.get_image(pool, None, image)
11fdf7f2
TL
600 self.assertStatus(200)
601 for conf in initial_expect:
602 self.assertIn(conf, img['configuration'])
603
9f95a23c
TL
604 self.edit_image(pool, None, image, configuration=new_conf)
605 img = self.get_image(pool, None, image)
11fdf7f2
TL
606 self.assertStatus(200)
607 for conf in new_expect:
608 self.assertIn(conf, img['configuration'])
609
9f95a23c 610 self.remove_image(pool, None, image)
11fdf7f2
TL
611 self.assertStatus(204)
612
613 def test_update_snapshot(self):
9f95a23c 614 self.create_snapshot('rbd', None, 'img1', 'snap5')
11fdf7f2 615 self.assertStatus(201)
9f95a23c 616 img = self.get_image('rbd', None, 'img1')
11fdf7f2
TL
617 self._validate_snapshot_list(img['snapshots'], 'snap5', is_protected=False)
618
9f95a23c 619 self.update_snapshot('rbd', None, 'img1', 'snap5', 'snap6', None)
11fdf7f2 620 self.assertStatus(200)
9f95a23c 621 img = self.get_image('rbd', None, 'img1')
11fdf7f2
TL
622 self._validate_snapshot_list(img['snapshots'], 'snap6', is_protected=False)
623
9f95a23c 624 self.update_snapshot('rbd', None, 'img1', 'snap6', None, True)
11fdf7f2 625 self.assertStatus(200)
9f95a23c 626 img = self.get_image('rbd', None, 'img1')
11fdf7f2
TL
627 self._validate_snapshot_list(img['snapshots'], 'snap6', is_protected=True)
628
9f95a23c 629 self.update_snapshot('rbd', None, 'img1', 'snap6', 'snap5', False)
11fdf7f2 630 self.assertStatus(200)
9f95a23c 631 img = self.get_image('rbd', None, 'img1')
11fdf7f2
TL
632 self._validate_snapshot_list(img['snapshots'], 'snap5', is_protected=False)
633
9f95a23c 634 self.remove_snapshot('rbd', None, 'img1', 'snap5')
11fdf7f2
TL
635 self.assertStatus(204)
636
637 def test_snapshot_rollback(self):
9f95a23c 638 self.create_image('rbd', None, 'rollback_img', 2**30,
11fdf7f2
TL
639 features=["layering", "exclusive-lock", "fast-diff",
640 "object-map"])
641 self.assertStatus(201)
9f95a23c 642 self.create_snapshot('rbd', None, 'rollback_img', 'snap1')
11fdf7f2
TL
643 self.assertStatus(201)
644
9f95a23c 645 img = self.get_image('rbd', None, 'rollback_img')
11fdf7f2
TL
646 self.assertStatus(200)
647 self.assertEqual(img['disk_usage'], 0)
648
649 self._rbd_cmd(['bench', '--io-type', 'write', '--io-total', '5M',
650 'rbd/rollback_img'])
651
9f95a23c 652 img = self.get_image('rbd', None, 'rollback_img')
11fdf7f2
TL
653 self.assertStatus(200)
654 self.assertGreater(img['disk_usage'], 0)
655
9f95a23c 656 self.rollback_snapshot('rbd', None, 'rollback_img', 'snap1')
11fdf7f2
TL
657 self.assertStatus([201, 200])
658
9f95a23c 659 img = self.get_image('rbd', None, 'rollback_img')
11fdf7f2
TL
660 self.assertStatus(200)
661 self.assertEqual(img['disk_usage'], 0)
662
9f95a23c 663 self.remove_snapshot('rbd', None, 'rollback_img', 'snap1')
11fdf7f2 664 self.assertStatus(204)
9f95a23c 665 self.remove_image('rbd', None, 'rollback_img')
11fdf7f2
TL
666 self.assertStatus(204)
667
668 def test_clone(self):
9f95a23c 669 self.create_image('rbd', None, 'cimg', 2**30, features=["layering"])
11fdf7f2 670 self.assertStatus(201)
9f95a23c 671 self.create_snapshot('rbd', None, 'cimg', 'snap1')
11fdf7f2 672 self.assertStatus(201)
9f95a23c 673 self.update_snapshot('rbd', None, 'cimg', 'snap1', None, True)
11fdf7f2 674 self.assertStatus(200)
9f95a23c 675 self.clone_image('rbd', None, 'cimg', 'snap1', 'rbd', None, 'cimg-clone',
11fdf7f2
TL
676 features=["layering", "exclusive-lock", "fast-diff",
677 "object-map"])
678 self.assertStatus([200, 201])
679
9f95a23c 680 img = self.get_image('rbd', None, 'cimg-clone')
11fdf7f2
TL
681 self.assertStatus(200)
682 self._validate_image(img, features_name=['exclusive-lock',
683 'fast-diff', 'layering',
684 'object-map'],
9f95a23c
TL
685 parent={'pool_name': 'rbd', 'pool_namespace': '',
686 'image_name': 'cimg', 'snap_name': 'snap1'})
11fdf7f2 687
9f95a23c 688 res = self.remove_image('rbd', None, 'cimg')
11fdf7f2
TL
689 self.assertStatus(400)
690 self.assertIn('code', res)
9f95a23c 691 self.assertEqual(res['code'], '16')
11fdf7f2 692
9f95a23c 693 self.remove_image('rbd', None, 'cimg-clone')
11fdf7f2 694 self.assertStatus(204)
9f95a23c 695 self.remove_image('rbd', None, 'cimg')
11fdf7f2
TL
696 self.assertStatus(204)
697
698 def test_copy(self):
9f95a23c 699 self.create_image('rbd', None, 'coimg', 2**30,
11fdf7f2
TL
700 features=["layering", "exclusive-lock", "fast-diff",
701 "object-map"])
702 self.assertStatus(201)
703
704 self._rbd_cmd(['bench', '--io-type', 'write', '--io-total', '5M',
705 'rbd/coimg'])
706
9f95a23c 707 self.copy_image('rbd', None, 'coimg', 'rbd_iscsi', None, 'coimg-copy',
11fdf7f2
TL
708 features=["layering", "fast-diff", "exclusive-lock",
709 "object-map"])
710 self.assertStatus([200, 201])
711
9f95a23c 712 img = self.get_image('rbd', None, 'coimg')
11fdf7f2
TL
713 self.assertStatus(200)
714 self._validate_image(img, features_name=['layering', 'exclusive-lock',
715 'fast-diff', 'object-map'])
716
9f95a23c 717 img_copy = self.get_image('rbd_iscsi', None, 'coimg-copy')
11fdf7f2
TL
718 self._validate_image(img_copy, features_name=['exclusive-lock',
719 'fast-diff', 'layering',
720 'object-map'],
721 disk_usage=img['disk_usage'])
722
9f95a23c 723 self.remove_image('rbd', None, 'coimg')
11fdf7f2 724 self.assertStatus(204)
9f95a23c 725 self.remove_image('rbd_iscsi', None, 'coimg-copy')
11fdf7f2
TL
726 self.assertStatus(204)
727
728 def test_flatten(self):
9f95a23c
TL
729 self.create_snapshot('rbd', None, 'img1', 'snapf')
730 self.update_snapshot('rbd', None, 'img1', 'snapf', None, True)
731 self.clone_image('rbd', None, 'img1', 'snapf', 'rbd_iscsi', None, 'img1_snapf_clone')
11fdf7f2 732
9f95a23c 733 img = self.get_image('rbd_iscsi', None, 'img1_snapf_clone')
11fdf7f2
TL
734 self.assertStatus(200)
735 self.assertIsNotNone(img['parent'])
736
9f95a23c 737 self.flatten_image('rbd_iscsi', None, 'img1_snapf_clone')
11fdf7f2
TL
738 self.assertStatus([200, 201])
739
9f95a23c 740 img = self.get_image('rbd_iscsi', None, 'img1_snapf_clone')
11fdf7f2
TL
741 self.assertStatus(200)
742 self.assertIsNone(img['parent'])
743
9f95a23c
TL
744 self.update_snapshot('rbd', None, 'img1', 'snapf', None, False)
745 self.remove_snapshot('rbd', None, 'img1', 'snapf')
11fdf7f2
TL
746 self.assertStatus(204)
747
9f95a23c 748 self.remove_image('rbd_iscsi', None, 'img1_snapf_clone')
11fdf7f2
TL
749 self.assertStatus(204)
750
751 def test_default_features(self):
752 default_features = self._get('/api/block/image/default_features')
9f95a23c
TL
753 self.assertEqual(default_features, [
754 'deep-flatten', 'exclusive-lock', 'fast-diff', 'layering', 'object-map'])
11fdf7f2 755
9f95a23c
TL
756 def test_image_with_namespace(self):
757 self.create_namespace('rbd', 'ns')
758 self.create_image('rbd', 'ns', 'test', 10240)
11fdf7f2
TL
759 self.assertStatus(201)
760
9f95a23c 761 img = self.get_image('rbd', 'ns', 'test')
11fdf7f2
TL
762 self.assertStatus(200)
763
9f95a23c
TL
764 self._validate_image(img, name='test', size=10240,
765 pool_name='rbd', namespace='ns',
11fdf7f2
TL
766 num_objs=1, obj_size=4194304,
767 features_name=['deep-flatten',
768 'exclusive-lock',
769 'fast-diff', 'layering',
770 'object-map'])
771
9f95a23c
TL
772 self.remove_image('rbd', 'ns', 'test')
773 self.remove_namespace('rbd', 'ns')
11fdf7f2
TL
774
775 def test_move_image_to_trash(self):
776 id = self.create_image_in_trash('rbd', 'test_rbd')
777 self.assertStatus(200)
778
9f95a23c 779 self.get_image('rbd', None, 'test_rbd')
11fdf7f2
TL
780 self.assertStatus(404)
781
782 time.sleep(1)
783
784 image = self.get_trash('rbd', id)
785 self.assertIsNotNone(image)
786
9f95a23c 787 self.remove_trash('rbd', id)
11fdf7f2
TL
788
789 def test_list_trash(self):
790 id = self.create_image_in_trash('rbd', 'test_rbd', 0)
791 data = self._get('/api/block/image/trash/?pool_name={}'.format('rbd'))
792 self.assertStatus(200)
793 self.assertIsInstance(data, list)
794 self.assertIsNotNone(data)
795
9f95a23c 796 self.remove_trash('rbd', id)
11fdf7f2
TL
797 self.assertStatus(204)
798
799 def test_restore_trash(self):
800 id = self.create_image_in_trash('rbd', 'test_rbd')
801
9f95a23c 802 self.restore_trash('rbd', None, id, 'test_rbd')
11fdf7f2 803
9f95a23c 804 self.get_image('rbd', None, 'test_rbd')
11fdf7f2
TL
805 self.assertStatus(200)
806
807 image = self.get_trash('rbd', id)
808 self.assertIsNone(image)
809
9f95a23c 810 self.remove_image('rbd', None, 'test_rbd')
11fdf7f2
TL
811
812 def test_remove_expired_trash(self):
813 id = self.create_image_in_trash('rbd', 'test_rbd', 0)
9f95a23c 814 self.remove_trash('rbd', id, False)
11fdf7f2
TL
815 self.assertStatus(204)
816
817 image = self.get_trash('rbd', id)
818 self.assertIsNone(image)
819
820 def test_remove_not_expired_trash(self):
821 id = self.create_image_in_trash('rbd', 'test_rbd', 9999)
9f95a23c 822 self.remove_trash('rbd', id, False)
11fdf7f2
TL
823 self.assertStatus(400)
824
825 time.sleep(1)
826
827 image = self.get_trash('rbd', id)
828 self.assertIsNotNone(image)
829
9f95a23c 830 self.remove_trash('rbd', id, True)
11fdf7f2
TL
831
832 def test_remove_not_expired_trash_with_force(self):
833 id = self.create_image_in_trash('rbd', 'test_rbd', 9999)
9f95a23c 834 self.remove_trash('rbd', id, True)
11fdf7f2
TL
835 self.assertStatus(204)
836
837 image = self.get_trash('rbd', id)
838 self.assertIsNone(image)
839
840 def test_purge_trash(self):
841 id_expired = self.create_image_in_trash('rbd', 'test_rbd_expired', 0)
842 id_not_expired = self.create_image_in_trash('rbd', 'test_rbd', 9999)
843
844 time.sleep(1)
845
9f95a23c 846 self.purge_trash('rbd')
801d1391 847 self.assertStatus([200, 201])
11fdf7f2
TL
848
849 time.sleep(1)
850
851 trash_not_expired = self.get_trash('rbd', id_not_expired)
852 self.assertIsNotNone(trash_not_expired)
853
801d1391 854 self.wait_until_equal(lambda: self.get_trash('rbd', id_expired), None, 60)
9f95a23c
TL
855
856 def test_list_namespaces(self):
857 self.create_namespace('rbd', 'ns')
858
859 namespaces = self._get('/api/block/pool/rbd/namespace')
860 self.assertStatus(200)
861 self.assertEqual(len(namespaces), 1)
862
863 self.remove_namespace('rbd', 'ns')