]>
git.proxmox.com Git - ceph.git/blob - ceph/qa/tasks/mgr/dashboard/test_rbd.py
1 # -*- coding: utf-8 -*-
2 # pylint: disable=too-many-public-methods
4 from __future__
import absolute_import
8 from .helper
import DashboardTestCase
, JObj
, JLeaf
, JList
11 class RbdTest(DashboardTestCase
):
12 AUTH_ROLES
= ['pool-manager', 'block-manager']
15 def create_pool(cls
, name
, pg_num
, pool_type
, application
='rbd'):
19 'pool_type': pool_type
,
20 'application_metadata': [application
]
22 if pool_type
== 'erasure':
23 data
['flags'] = ['ec_overwrites']
24 cls
._task
_post
("/api/pool", data
)
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)
30 self
._get
('/api/block/image/pool/image')
31 self
.assertStatus(403)
33 @DashboardTestCase.RunAs('test', 'test', [{'rbd-image': ['read', 'update', 'delete']}])
34 def test_create_access_permissions(self
):
35 self
.create_image('pool', 'name', 0)
36 self
.assertStatus(403)
37 self
.create_snapshot('pool', 'image', 'snapshot')
38 self
.assertStatus(403)
39 self
.copy_image('src_pool', 'src_image', 'dest_pool', 'dest_image')
40 self
.assertStatus(403)
41 self
.clone_image('parent_pool', 'parent_image', 'parent_snap', 'pool', 'name')
42 self
.assertStatus(403)
44 @DashboardTestCase.RunAs('test', 'test', [{'rbd-image': ['read', 'create', 'delete']}])
45 def test_update_access_permissions(self
):
46 self
.edit_image('pool', 'image')
47 self
.assertStatus(403)
48 self
.update_snapshot('pool', 'image', 'snapshot', None, None)
49 self
.assertStatus(403)
50 self
._task
_post
('/api/block/image/rbd/rollback_img/snap/snap1/rollback')
51 self
.assertStatus(403)
52 self
.flatten_image('pool', 'image')
53 self
.assertStatus(403)
55 @DashboardTestCase.RunAs('test', 'test', [{'rbd-image': ['read', 'create', 'update']}])
56 def test_delete_access_permissions(self
):
57 self
.remove_image('pool', 'image')
58 self
.assertStatus(403)
59 self
.remove_snapshot('pool', 'image', 'snapshot')
60 self
.assertStatus(403)
63 def create_image(cls
, pool
, name
, size
, **kwargs
):
64 data
= {'name': name
, 'pool_name': pool
, 'size': size
}
66 return cls
._task
_post
('/api/block/image', data
)
69 def clone_image(cls
, parent_pool
, parent_image
, parent_snap
, pool
, name
,
71 # pylint: disable=too-many-arguments
72 data
= {'child_image_name': name
, 'child_pool_name': pool
}
74 return cls
._task
_post
('/api/block/image/{}/{}/snap/{}/clone'
75 .format(parent_pool
, parent_image
, parent_snap
),
79 def copy_image(cls
, src_pool
, src_image
, dest_pool
, dest_image
, **kwargs
):
80 # pylint: disable=too-many-arguments
81 data
= {'dest_image_name': dest_image
, 'dest_pool_name': dest_pool
}
83 return cls
._task
_post
('/api/block/image/{}/{}/copy'
84 .format(src_pool
, src_image
), data
)
87 def remove_image(cls
, pool
, image
):
88 return cls
._task
_delete
('/api/block/image/{}/{}'.format(pool
, image
))
90 # pylint: disable=too-many-arguments
92 def edit_image(cls
, pool
, image
, name
=None, size
=None, features
=None, **kwargs
):
93 kwargs
.update({'name': name
, 'size': size
, 'features': features
})
94 return cls
._task
_put
('/api/block/image/{}/{}'.format(pool
, image
), kwargs
)
97 def flatten_image(cls
, pool
, image
):
98 return cls
._task
_post
('/api/block/image/{}/{}/flatten'.format(pool
, image
))
101 def create_snapshot(cls
, pool
, image
, snapshot
):
102 return cls
._task
_post
('/api/block/image/{}/{}/snap'.format(pool
, image
),
103 {'snapshot_name': snapshot
})
106 def remove_snapshot(cls
, pool
, image
, snapshot
):
107 return cls
._task
_delete
('/api/block/image/{}/{}/snap/{}'.format(pool
, image
, snapshot
))
110 def update_snapshot(cls
, pool
, image
, snapshot
, new_name
, is_protected
):
111 return cls
._task
_put
('/api/block/image/{}/{}/snap/{}'.format(pool
, image
, snapshot
),
112 {'new_snap_name': new_name
, 'is_protected': is_protected
})
116 super(RbdTest
, cls
).setUpClass()
117 cls
.create_pool('rbd', 10, 'replicated')
118 cls
.create_pool('rbd_iscsi', 10, 'replicated')
120 cls
.create_image('rbd', 'img1', 2**30)
121 cls
.create_image('rbd', 'img2', 2*2**30)
122 cls
.create_image('rbd_iscsi', 'img1', 2**30)
123 cls
.create_image('rbd_iscsi', 'img2', 2*2**30)
125 osd_metadata
= cls
.ceph_cluster
.mon_manager
.get_osd_metadata()
126 cls
.bluestore_support
= True
127 for osd
in osd_metadata
:
128 if osd
['osd_objectstore'] != 'bluestore':
129 cls
.bluestore_support
= False
133 def tearDownClass(cls
):
134 super(RbdTest
, cls
).tearDownClass()
135 cls
._ceph
_cmd
(['osd', 'pool', 'delete', 'rbd', 'rbd', '--yes-i-really-really-mean-it'])
136 cls
._ceph
_cmd
(['osd', 'pool', 'delete', 'rbd_iscsi', 'rbd_iscsi',
137 '--yes-i-really-really-mean-it'])
138 cls
._ceph
_cmd
(['osd', 'pool', 'delete', 'rbd_data', 'rbd_data',
139 '--yes-i-really-really-mean-it'])
142 def create_image_in_trash(cls
, pool
, name
, delay
=0):
143 cls
.create_image(pool
, name
, 10240)
144 img
= cls
._get
('/api/block/image/{}/{}'.format(pool
, name
))
146 cls
._task
_post
("/api/block/image/{}/{}/move_trash".format(pool
, name
),
152 def remove_trash(cls
, pool
, image_id
, image_name
, force
=False):
153 return cls
._task
_delete
('/api/block/image/trash/{}/{}/?image_name={}&force={}'.format('rbd', image_id
, image_name
, force
))
156 def get_trash(cls
, pool
, image_id
):
157 trash
= cls
._get
('/api/block/image/trash/?pool_name={}'.format(pool
))
158 if isinstance(trash
, list):
160 for image
in pool
['value']:
161 if image
['id'] == image_id
:
166 def _validate_image(self
, img
, **kwargs
):
168 Example of an RBD image json:
175 "block_name_prefix": "rbd_data.10ae2ae8944a",
179 "features_name": ["deep-flatten", "exclusive-lock", "fast-diff", "layering",
183 schema
= JObj(sub_elems
={
185 'obj_size': JLeaf(int),
186 'num_objs': JLeaf(int),
188 'block_name_prefix': JLeaf(str),
191 'pool_name': JLeaf(str),
192 'features': JLeaf(int),
193 'features_name': JList(JLeaf(str)),
194 'stripe_count': JLeaf(int, none
=True),
195 'stripe_unit': JLeaf(int, none
=True),
196 'parent': JObj(sub_elems
={'pool_name': JLeaf(str),
197 'image_name': JLeaf(str),
198 'snap_name': JLeaf(str)}, none
=True),
199 'data_pool': JLeaf(str, none
=True),
200 'snapshots': JList(JLeaf(dict)),
201 'timestamp': JLeaf(str, none
=True),
202 'disk_usage': JLeaf(int, none
=True),
203 'total_disk_usage': JLeaf(int, none
=True),
204 'configuration': JList(JObj(sub_elems
={
206 'source': JLeaf(int),
210 self
.assertSchema(img
, schema
)
212 for k
, v
in kwargs
.items():
213 if isinstance(v
, list):
214 self
.assertSetEqual(set(img
[k
]), set(v
))
216 self
.assertEqual(img
[k
], v
)
218 def _validate_snapshot(self
, snap
, **kwargs
):
219 self
.assertIn('id', snap
)
220 self
.assertIn('name', snap
)
221 self
.assertIn('is_protected', snap
)
222 self
.assertIn('timestamp', snap
)
223 self
.assertIn('size', snap
)
224 self
.assertIn('children', snap
)
226 for k
, v
in kwargs
.items():
227 if isinstance(v
, list):
228 self
.assertSetEqual(set(snap
[k
]), set(v
))
230 self
.assertEqual(snap
[k
], v
)
232 def _validate_snapshot_list(self
, snap_list
, snap_name
=None, **kwargs
):
234 for snap
in snap_list
:
235 self
.assertIn('name', snap
)
236 if snap_name
and snap
['name'] == snap_name
:
238 self
._validate
_snapshot
(snap
, **kwargs
)
240 if snap_name
and not found
:
241 self
.fail("Snapshot {} not found".format(snap_name
))
244 data
= self
._view
_cache
_get
('/api/block/image')
245 self
.assertStatus(200)
246 self
.assertEqual(len(data
), 2)
248 for pool_view
in data
:
249 self
.assertEqual(pool_view
['status'], 0)
250 self
.assertIsNotNone(pool_view
['value'])
251 self
.assertIn('pool_name', pool_view
)
252 self
.assertIn(pool_view
['pool_name'], ['rbd', 'rbd_iscsi'])
253 image_list
= pool_view
['value']
254 self
.assertEqual(len(image_list
), 2)
256 for img
in image_list
:
257 self
.assertIn('name', img
)
258 self
.assertIn('pool_name', img
)
259 self
.assertIn(img
['pool_name'], ['rbd', 'rbd_iscsi'])
260 if img
['name'] == 'img1':
261 self
._validate
_image
(img
, size
=1073741824,
262 num_objs
=256, obj_size
=4194304,
263 features_name
=['deep-flatten',
268 elif img
['name'] == 'img2':
269 self
._validate
_image
(img
, size
=2147483648,
270 num_objs
=512, obj_size
=4194304,
271 features_name
=['deep-flatten',
277 assert False, "Unexcepted image '{}' in result list".format(img
['name'])
279 def test_create(self
):
280 rbd_name
= 'test_rbd'
281 self
.create_image('rbd', rbd_name
, 10240)
282 self
.assertStatus(201)
284 img
= self
._get
('/api/block/image/rbd/test_rbd')
285 self
.assertStatus(200)
287 self
._validate
_image
(img
, name
=rbd_name
, size
=10240,
288 num_objs
=1, obj_size
=4194304,
289 features_name
=['deep-flatten',
291 'fast-diff', 'layering',
294 self
.remove_image('rbd', rbd_name
)
296 def test_create_with_configuration(self
):
298 image_name
= 'image_with_config'
301 'rbd_qos_bps_limit': 10240,
302 'rbd_qos_bps_burst': 10240 * 2,
305 'name': 'rbd_qos_bps_limit',
309 'name': 'rbd_qos_bps_burst',
311 'value': str(10240 * 2),
314 self
.create_image(pool
, image_name
, size
, configuration
=configuration
)
315 self
.assertStatus(201)
316 img
= self
._get
('/api/block/image/rbd/{}'.format(image_name
))
317 self
.assertStatus(200)
318 for conf
in expected
:
319 self
.assertIn(conf
, img
['configuration'])
321 self
.remove_image(pool
, image_name
)
323 def test_create_rbd_in_data_pool(self
):
324 if not self
.bluestore_support
:
325 self
.skipTest('requires bluestore cluster')
327 self
.create_pool('data_pool', 12, 'erasure')
329 rbd_name
= 'test_rbd_in_data_pool'
330 self
.create_image('rbd', rbd_name
, 10240, data_pool
='data_pool')
331 self
.assertStatus(201)
333 img
= self
._get
('/api/block/image/rbd/test_rbd_in_data_pool')
334 self
.assertStatus(200)
336 self
._validate
_image
(img
, name
=rbd_name
, size
=10240,
337 num_objs
=1, obj_size
=4194304,
338 data_pool
='data_pool',
339 features_name
=['data-pool', 'deep-flatten',
341 'fast-diff', 'layering',
344 self
.remove_image('rbd', rbd_name
)
345 self
.assertStatus(204)
346 self
._ceph
_cmd
(['osd', 'pool', 'delete', 'data_pool', 'data_pool',
347 '--yes-i-really-really-mean-it'])
349 def test_create_rbd_twice(self
):
350 res
= self
.create_image('rbd', 'test_rbd_twice', 10240)
352 res
= self
.create_image('rbd', 'test_rbd_twice', 10240)
353 self
.assertStatus(400)
354 self
.assertEqual(res
, {"code": '17', 'status': 400, "component": "rbd",
355 "detail": "[errno 17] error creating image",
356 'task': {'name': 'rbd/create',
357 'metadata': {'pool_name': 'rbd',
358 'image_name': 'test_rbd_twice'}}})
359 self
.remove_image('rbd', 'test_rbd_twice')
360 self
.assertStatus(204)
362 def test_snapshots_and_clone_info(self
):
363 self
.create_snapshot('rbd', 'img1', 'snap1')
364 self
.create_snapshot('rbd', 'img1', 'snap2')
365 self
._rbd
_cmd
(['snap', 'protect', 'rbd/img1@snap1'])
366 self
._rbd
_cmd
(['clone', 'rbd/img1@snap1', 'rbd_iscsi/img1_clone'])
368 img
= self
._get
('/api/block/image/rbd/img1')
369 self
.assertStatus(200)
370 self
._validate
_image
(img
, name
='img1', size
=1073741824,
371 num_objs
=256, obj_size
=4194304, parent
=None,
372 features_name
=['deep-flatten', 'exclusive-lock',
373 'fast-diff', 'layering',
375 for snap
in img
['snapshots']:
376 if snap
['name'] == 'snap1':
377 self
._validate
_snapshot
(snap
, is_protected
=True)
378 self
.assertEqual(len(snap
['children']), 1)
379 self
.assertDictEqual(snap
['children'][0],
380 {'pool_name': 'rbd_iscsi',
381 'image_name': 'img1_clone'})
382 elif snap
['name'] == 'snap2':
383 self
._validate
_snapshot
(snap
, is_protected
=False)
385 img
= self
._get
('/api/block/image/rbd_iscsi/img1_clone')
386 self
.assertStatus(200)
387 self
._validate
_image
(img
, name
='img1_clone', size
=1073741824,
388 num_objs
=256, obj_size
=4194304,
389 parent
={'pool_name': 'rbd', 'image_name': 'img1',
390 'snap_name': 'snap1'},
391 features_name
=['deep-flatten', 'exclusive-lock',
392 'fast-diff', 'layering',
394 self
.remove_image('rbd_iscsi', 'img1_clone')
395 self
.assertStatus(204)
397 def test_disk_usage(self
):
398 self
._rbd
_cmd
(['bench', '--io-type', 'write', '--io-total', '50M', 'rbd/img2'])
399 self
.create_snapshot('rbd', 'img2', 'snap1')
400 self
._rbd
_cmd
(['bench', '--io-type', 'write', '--io-total', '20M', 'rbd/img2'])
401 self
.create_snapshot('rbd', 'img2', 'snap2')
402 self
._rbd
_cmd
(['bench', '--io-type', 'write', '--io-total', '10M', 'rbd/img2'])
403 self
.create_snapshot('rbd', 'img2', 'snap3')
404 self
._rbd
_cmd
(['bench', '--io-type', 'write', '--io-total', '5M', 'rbd/img2'])
405 img
= self
._get
('/api/block/image/rbd/img2')
406 self
.assertStatus(200)
407 self
._validate
_image
(img
, name
='img2', size
=2147483648,
408 total_disk_usage
=268435456, disk_usage
=67108864)
410 def test_delete_non_existent_image(self
):
411 res
= self
.remove_image('rbd', 'i_dont_exist')
412 self
.assertStatus(400)
413 self
.assertEqual(res
, {u
'code': u
'2', "status": 400, "component": "rbd",
414 "detail": "[errno 2] error removing image",
415 'task': {'name': 'rbd/delete',
416 'metadata': {'pool_name': 'rbd',
417 'image_name': 'i_dont_exist'}}})
419 def test_image_delete(self
):
420 self
.create_image('rbd', 'delete_me', 2**30)
421 self
.assertStatus(201)
422 self
.create_snapshot('rbd', 'delete_me', 'snap1')
423 self
.assertStatus(201)
424 self
.create_snapshot('rbd', 'delete_me', 'snap2')
425 self
.assertStatus(201)
427 img
= self
._get
('/api/block/image/rbd/delete_me')
428 self
.assertStatus(200)
429 self
._validate
_image
(img
, name
='delete_me', size
=2**30)
430 self
.assertEqual(len(img
['snapshots']), 2)
432 self
.remove_snapshot('rbd', 'delete_me', 'snap1')
433 self
.assertStatus(204)
434 self
.remove_snapshot('rbd', 'delete_me', 'snap2')
435 self
.assertStatus(204)
437 img
= self
._get
('/api/block/image/rbd/delete_me')
438 self
.assertStatus(200)
439 self
._validate
_image
(img
, name
='delete_me', size
=2**30)
440 self
.assertEqual(len(img
['snapshots']), 0)
442 self
.remove_image('rbd', 'delete_me')
443 self
.assertStatus(204)
445 def test_image_rename(self
):
446 self
.create_image('rbd', 'edit_img', 2**30)
447 self
.assertStatus(201)
448 self
._get
('/api/block/image/rbd/edit_img')
449 self
.assertStatus(200)
450 self
.edit_image('rbd', 'edit_img', 'new_edit_img')
451 self
.assertStatus(200)
452 self
._get
('/api/block/image/rbd/edit_img')
453 self
.assertStatus(404)
454 self
._get
('/api/block/image/rbd/new_edit_img')
455 self
.assertStatus(200)
456 self
.remove_image('rbd', 'new_edit_img')
457 self
.assertStatus(204)
459 def test_image_resize(self
):
460 self
.create_image('rbd', 'edit_img', 2**30)
461 self
.assertStatus(201)
462 img
= self
._get
('/api/block/image/rbd/edit_img')
463 self
.assertStatus(200)
464 self
._validate
_image
(img
, size
=2**30)
465 self
.edit_image('rbd', 'edit_img', size
=2*2**30)
466 self
.assertStatus(200)
467 img
= self
._get
('/api/block/image/rbd/edit_img')
468 self
.assertStatus(200)
469 self
._validate
_image
(img
, size
=2*2**30)
470 self
.remove_image('rbd', 'edit_img')
471 self
.assertStatus(204)
473 def test_image_change_features(self
):
474 self
.create_image('rbd', 'edit_img', 2**30, features
=["layering"])
475 self
.assertStatus(201)
476 img
= self
._get
('/api/block/image/rbd/edit_img')
477 self
.assertStatus(200)
478 self
._validate
_image
(img
, features_name
=["layering"])
479 self
.edit_image('rbd', 'edit_img',
480 features
=["fast-diff", "object-map", "exclusive-lock"])
481 self
.assertStatus(200)
482 img
= self
._get
('/api/block/image/rbd/edit_img')
483 self
.assertStatus(200)
484 self
._validate
_image
(img
, features_name
=['exclusive-lock',
485 'fast-diff', 'layering',
487 self
.edit_image('rbd', 'edit_img',
488 features
=["journaling", "exclusive-lock"])
489 self
.assertStatus(200)
490 img
= self
._get
('/api/block/image/rbd/edit_img')
491 self
.assertStatus(200)
492 self
._validate
_image
(img
, features_name
=['exclusive-lock',
493 'journaling', 'layering'])
494 self
.remove_image('rbd', 'edit_img')
495 self
.assertStatus(204)
497 def test_image_change_config(self
):
499 image
= 'image_with_config'
501 'rbd_qos_bps_limit': 10240,
502 'rbd_qos_write_iops_limit': None
505 'name': 'rbd_qos_bps_limit',
509 'name': 'rbd_qos_write_iops_limit',
514 'rbd_qos_bps_limit': 0,
515 'rbd_qos_bps_burst': 20480,
516 'rbd_qos_write_iops_limit': None
519 'name': 'rbd_qos_bps_limit',
523 'name': 'rbd_qos_bps_burst',
527 'name': 'rbd_qos_write_iops_limit',
532 self
.create_image(pool
, image
, 2**30, configuration
=initial_conf
)
533 self
.assertStatus(201)
534 img
= self
._get
('/api/block/image/{}/{}'.format(pool
, image
))
535 self
.assertStatus(200)
536 for conf
in initial_expect
:
537 self
.assertIn(conf
, img
['configuration'])
539 self
.edit_image(pool
, image
, configuration
=new_conf
)
540 img
= self
._get
('/api/block/image/{}/{}'.format(pool
, image
))
541 self
.assertStatus(200)
542 for conf
in new_expect
:
543 self
.assertIn(conf
, img
['configuration'])
545 self
.remove_image(pool
, image
)
546 self
.assertStatus(204)
548 def test_update_snapshot(self
):
549 self
.create_snapshot('rbd', 'img1', 'snap5')
550 self
.assertStatus(201)
551 img
= self
._get
('/api/block/image/rbd/img1')
552 self
._validate
_snapshot
_list
(img
['snapshots'], 'snap5', is_protected
=False)
554 self
.update_snapshot('rbd', 'img1', 'snap5', 'snap6', None)
555 self
.assertStatus(200)
556 img
= self
._get
('/api/block/image/rbd/img1')
557 self
._validate
_snapshot
_list
(img
['snapshots'], 'snap6', is_protected
=False)
559 self
.update_snapshot('rbd', 'img1', 'snap6', None, True)
560 self
.assertStatus(200)
561 img
= self
._get
('/api/block/image/rbd/img1')
562 self
._validate
_snapshot
_list
(img
['snapshots'], 'snap6', is_protected
=True)
564 self
.update_snapshot('rbd', 'img1', 'snap6', 'snap5', False)
565 self
.assertStatus(200)
566 img
= self
._get
('/api/block/image/rbd/img1')
567 self
._validate
_snapshot
_list
(img
['snapshots'], 'snap5', is_protected
=False)
569 self
.remove_snapshot('rbd', 'img1', 'snap5')
570 self
.assertStatus(204)
572 def test_snapshot_rollback(self
):
573 self
.create_image('rbd', 'rollback_img', 2**30,
574 features
=["layering", "exclusive-lock", "fast-diff",
576 self
.assertStatus(201)
577 self
.create_snapshot('rbd', 'rollback_img', 'snap1')
578 self
.assertStatus(201)
580 img
= self
._get
('/api/block/image/rbd/rollback_img')
581 self
.assertStatus(200)
582 self
.assertEqual(img
['disk_usage'], 0)
584 self
._rbd
_cmd
(['bench', '--io-type', 'write', '--io-total', '5M',
587 img
= self
._get
('/api/block/image/rbd/rollback_img')
588 self
.assertStatus(200)
589 self
.assertGreater(img
['disk_usage'], 0)
591 self
._task
_post
('/api/block/image/rbd/rollback_img/snap/snap1/rollback')
592 self
.assertStatus([201, 200])
594 img
= self
._get
('/api/block/image/rbd/rollback_img')
595 self
.assertStatus(200)
596 self
.assertEqual(img
['disk_usage'], 0)
598 self
.remove_snapshot('rbd', 'rollback_img', 'snap1')
599 self
.assertStatus(204)
600 self
.remove_image('rbd', 'rollback_img')
601 self
.assertStatus(204)
603 def test_clone(self
):
604 self
.create_image('rbd', 'cimg', 2**30, features
=["layering"])
605 self
.assertStatus(201)
606 self
.create_snapshot('rbd', 'cimg', 'snap1')
607 self
.assertStatus(201)
608 self
.update_snapshot('rbd', 'cimg', 'snap1', None, True)
609 self
.assertStatus(200)
610 self
.clone_image('rbd', 'cimg', 'snap1', 'rbd', 'cimg-clone',
611 features
=["layering", "exclusive-lock", "fast-diff",
613 self
.assertStatus([200, 201])
615 img
= self
._get
('/api/block/image/rbd/cimg-clone')
616 self
.assertStatus(200)
617 self
._validate
_image
(img
, features_name
=['exclusive-lock',
618 'fast-diff', 'layering',
620 parent
={'pool_name': 'rbd', 'image_name': 'cimg',
621 'snap_name': 'snap1'})
623 res
= self
.remove_image('rbd', 'cimg')
624 self
.assertStatus(400)
625 self
.assertIn('code', res
)
626 self
.assertEqual(res
['code'], '39')
628 self
.remove_image('rbd', 'cimg-clone')
629 self
.assertStatus(204)
630 self
.update_snapshot('rbd', 'cimg', 'snap1', None, False)
631 self
.assertStatus(200)
632 self
.remove_snapshot('rbd', 'cimg', 'snap1')
633 self
.assertStatus(204)
634 self
.remove_image('rbd', 'cimg')
635 self
.assertStatus(204)
638 self
.create_image('rbd', 'coimg', 2**30,
639 features
=["layering", "exclusive-lock", "fast-diff",
641 self
.assertStatus(201)
643 self
._rbd
_cmd
(['bench', '--io-type', 'write', '--io-total', '5M',
646 self
.copy_image('rbd', 'coimg', 'rbd_iscsi', 'coimg-copy',
647 features
=["layering", "fast-diff", "exclusive-lock",
649 self
.assertStatus([200, 201])
651 img
= self
._get
('/api/block/image/rbd/coimg')
652 self
.assertStatus(200)
653 self
._validate
_image
(img
, features_name
=['layering', 'exclusive-lock',
654 'fast-diff', 'object-map'])
656 img_copy
= self
._get
('/api/block/image/rbd_iscsi/coimg-copy')
657 self
._validate
_image
(img_copy
, features_name
=['exclusive-lock',
658 'fast-diff', 'layering',
660 disk_usage
=img
['disk_usage'])
662 self
.remove_image('rbd', 'coimg')
663 self
.assertStatus(204)
664 self
.remove_image('rbd_iscsi', 'coimg-copy')
665 self
.assertStatus(204)
667 def test_flatten(self
):
668 self
.create_snapshot('rbd', 'img1', 'snapf')
669 self
.update_snapshot('rbd', 'img1', 'snapf', None, True)
670 self
.clone_image('rbd', 'img1', 'snapf', 'rbd_iscsi', 'img1_snapf_clone')
672 img
= self
._get
('/api/block/image/rbd_iscsi/img1_snapf_clone')
673 self
.assertStatus(200)
674 self
.assertIsNotNone(img
['parent'])
676 self
.flatten_image('rbd_iscsi', 'img1_snapf_clone')
677 self
.assertStatus([200, 201])
679 img
= self
._get
('/api/block/image/rbd_iscsi/img1_snapf_clone')
680 self
.assertStatus(200)
681 self
.assertIsNone(img
['parent'])
683 self
.update_snapshot('rbd', 'img1', 'snapf', None, False)
684 self
.remove_snapshot('rbd', 'img1', 'snapf')
685 self
.assertStatus(204)
687 self
.remove_image('rbd_iscsi', 'img1_snapf_clone')
688 self
.assertStatus(204)
690 def test_default_features(self
):
691 default_features
= self
._get
('/api/block/image/default_features')
692 self
.assertEqual(default_features
, ['deep-flatten', 'exclusive-lock',
693 'fast-diff', 'layering',
696 def test_image_with_special_name(self
):
697 rbd_name
= 'test/rbd'
698 rbd_name_encoded
= 'test%2Frbd'
700 self
.create_image('rbd', rbd_name
, 10240)
701 self
.assertStatus(201)
703 img
= self
._get
("/api/block/image/rbd/" + rbd_name_encoded
)
704 self
.assertStatus(200)
706 self
._validate
_image
(img
, name
=rbd_name
, size
=10240,
707 num_objs
=1, obj_size
=4194304,
708 features_name
=['deep-flatten',
710 'fast-diff', 'layering',
713 self
.remove_image('rbd', rbd_name_encoded
)
715 def test_move_image_to_trash(self
):
716 id = self
.create_image_in_trash('rbd', 'test_rbd')
717 self
.assertStatus(200)
719 self
._get
('/api/block/image/rbd/test_rbd')
720 self
.assertStatus(404)
724 image
= self
.get_trash('rbd', id)
725 self
.assertIsNotNone(image
)
727 self
.remove_trash('rbd', id, 'test_rbd')
729 def test_list_trash(self
):
730 id = self
.create_image_in_trash('rbd', 'test_rbd', 0)
731 data
= self
._get
('/api/block/image/trash/?pool_name={}'.format('rbd'))
732 self
.assertStatus(200)
733 self
.assertIsInstance(data
, list)
734 self
.assertIsNotNone(data
)
736 self
.remove_trash('rbd', id, 'test_rbd')
737 self
.assertStatus(204)
739 def test_restore_trash(self
):
740 id = self
.create_image_in_trash('rbd', 'test_rbd')
742 self
._task
_post
('/api/block/image/trash/{}/{}/restore'.format('rbd', id), {'new_image_name': 'test_rbd'})
744 self
._get
('/api/block/image/rbd/test_rbd')
745 self
.assertStatus(200)
747 image
= self
.get_trash('rbd', id)
748 self
.assertIsNone(image
)
750 self
.remove_image('rbd', 'test_rbd')
752 def test_remove_expired_trash(self
):
753 id = self
.create_image_in_trash('rbd', 'test_rbd', 0)
754 self
.remove_trash('rbd', id, 'test_rbd', False)
755 self
.assertStatus(204)
757 image
= self
.get_trash('rbd', id)
758 self
.assertIsNone(image
)
760 def test_remove_not_expired_trash(self
):
761 id = self
.create_image_in_trash('rbd', 'test_rbd', 9999)
762 self
.remove_trash('rbd', id, 'test_rbd', False)
763 self
.assertStatus(400)
767 image
= self
.get_trash('rbd', id)
768 self
.assertIsNotNone(image
)
770 self
.remove_trash('rbd', id, 'test_rbd', True)
772 def test_remove_not_expired_trash_with_force(self
):
773 id = self
.create_image_in_trash('rbd', 'test_rbd', 9999)
774 self
.remove_trash('rbd', id, 'test_rbd', True)
775 self
.assertStatus(204)
777 image
= self
.get_trash('rbd', id)
778 self
.assertIsNone(image
)
780 def test_purge_trash(self
):
781 id_expired
= self
.create_image_in_trash('rbd', 'test_rbd_expired', 0)
782 id_not_expired
= self
.create_image_in_trash('rbd', 'test_rbd', 9999)
786 self
._task
_post
('/api/block/image/trash/purge?pool_name={}'.format('rbd'))
787 self
.assertStatus(200)
791 trash_not_expired
= self
.get_trash('rbd', id_not_expired
)
792 self
.assertIsNotNone(trash_not_expired
)
794 trash_expired
= self
.get_trash('rbd', id_expired
)
795 self
.assertIsNone(trash_expired
)