]>
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_image('pool', None, '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', None, 'name', 0)
36 self
.assertStatus(403)
37 self
.create_snapshot('pool', None, 'image', 'snapshot')
38 self
.assertStatus(403)
39 self
.copy_image('src_pool', None, 'src_image', 'dest_pool', None, 'dest_image')
40 self
.assertStatus(403)
41 self
.clone_image('parent_pool', None, 'parent_image', 'parent_snap', 'pool', None, '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', None, 'image')
47 self
.assertStatus(403)
48 self
.update_snapshot('pool', None, 'image', 'snapshot', None, None)
49 self
.assertStatus(403)
50 self
.rollback_snapshot('rbd', None, 'rollback_img', 'snap1')
51 self
.assertStatus(403)
52 self
.flatten_image('pool', None, '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', None, 'image')
58 self
.assertStatus(403)
59 self
.remove_snapshot('pool', None, 'image', 'snapshot')
60 self
.assertStatus(403)
63 def create_namespace(cls
, pool
, namespace
):
64 data
= {'namespace': namespace
}
65 return cls
._post
('/api/block/pool/{}/namespace'.format(pool
), data
)
68 def remove_namespace(cls
, pool
, namespace
):
69 return cls
._delete
('/api/block/pool/{}/namespace/{}'.format(pool
, namespace
))
72 def create_image(cls
, pool
, namespace
, name
, size
, **kwargs
):
73 data
= {'name': name
, 'pool_name': pool
, 'namespace': namespace
, 'size': size
}
75 return cls
._task
_post
('/api/block/image', data
)
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
))
83 def clone_image(cls
, parent_pool
, parent_namespace
, parent_image
, parent_snap
, pool
, namespace
,
85 # pylint: disable=too-many-arguments
86 data
= {'child_image_name': name
, 'child_namespace': namespace
, 'child_pool_name': pool
}
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
),
94 def copy_image(cls
, src_pool
, src_namespace
, src_image
, dest_pool
, dest_namespace
, dest_image
,
96 # pylint: disable=too-many-arguments
97 data
= {'dest_image_name': dest_image
,
98 'dest_pool_name': dest_pool
,
99 'dest_namespace': dest_namespace
}
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
)
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
))
110 # pylint: disable=too-many-arguments
112 def edit_image(cls
, pool
, namespace
, image
, name
=None, size
=None, features
=None, **kwargs
):
113 kwargs
.update({'name': name
, 'size': size
, 'features': features
})
114 namespace
= '{}%2F'.format(namespace
) if namespace
else ''
115 return cls
._task
_put
('/api/block/image/{}%2F{}{}'.format(pool
, namespace
, image
), kwargs
)
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
))
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
),
126 {'snapshot_name': snapshot
})
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
,
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
,
139 {'new_snap_name': new_name
, 'is_protected': is_protected
})
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
,
151 super(RbdTest
, cls
).setUpClass()
152 cls
.create_pool('rbd', 2**3, 'replicated')
153 cls
.create_pool('rbd_iscsi', 2**3, 'replicated')
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)
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
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'])
177 def create_image_in_trash(cls
, pool
, name
, delay
=0):
178 cls
.create_image(pool
, None, name
, 10240)
179 img
= cls
._get
('/api/block/image/{}%2F{}'.format(pool
, name
))
181 cls
._task
_post
("/api/block/image/{}%2F{}/move_trash".format(pool
, name
),
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
))
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
,
200 def purge_trash(cls
, pool
):
201 return cls
._task
_post
('/api/block/image/trash/purge?pool_name={}'.format(pool
))
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):
208 for image
in pool
['value']:
209 if image
['id'] == image_id
:
214 def _validate_image(self
, img
, **kwargs
):
216 Example of an RBD image json:
223 "block_name_prefix": "rbd_data.10ae2ae8944a",
227 "features_name": ["deep-flatten", "exclusive-lock", "fast-diff", "layering",
231 schema
= JObj(sub_elems
={
233 'obj_size': JLeaf(int),
234 'num_objs': JLeaf(int),
236 'block_name_prefix': JLeaf(str),
239 'pool_name': JLeaf(str),
240 'namespace': JLeaf(str, none
=True),
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),
246 'pool_namespace': JLeaf(str, none
=True),
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
={
256 'source': JLeaf(int),
260 self
.assertSchema(img
, schema
)
262 for k
, v
in kwargs
.items():
263 if isinstance(v
, list):
264 self
.assertSetEqual(set(img
[k
]), set(v
))
266 self
.assertEqual(img
[k
], v
)
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
)
276 for k
, v
in kwargs
.items():
277 if isinstance(v
, list):
278 self
.assertSetEqual(set(snap
[k
]), set(v
))
280 self
.assertEqual(snap
[k
], v
)
282 def _validate_snapshot_list(self
, snap_list
, snap_name
=None, **kwargs
):
284 for snap
in snap_list
:
285 self
.assertIn('name', snap
)
286 if snap_name
and snap
['name'] == snap_name
:
288 self
._validate
_snapshot
(snap
, **kwargs
)
290 if snap_name
and not found
:
291 self
.fail("Snapshot {} not found".format(snap_name
))
294 data
= self
._view
_cache
_get
('/api/block/image')
295 self
.assertStatus(200)
296 self
.assertEqual(len(data
), 2)
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)
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',
318 elif img
['name'] == 'img2':
319 self
._validate
_image
(img
, size
=2147483648,
320 num_objs
=512, obj_size
=4194304,
321 features_name
=['deep-flatten',
327 assert False, "Unexcepted image '{}' in result list".format(img
['name'])
329 def test_create(self
):
330 rbd_name
= 'test_rbd'
331 self
.create_image('rbd', None, rbd_name
, 10240)
332 self
.assertStatus(201)
334 img
= self
.get_image('rbd', None, 'test_rbd')
335 self
.assertStatus(200)
337 self
._validate
_image
(img
, name
=rbd_name
, size
=10240,
338 num_objs
=1, obj_size
=4194304,
339 features_name
=['deep-flatten',
341 'fast-diff', 'layering',
344 self
.remove_image('rbd', None, rbd_name
)
346 def test_create_with_configuration(self
):
348 image_name
= 'image_with_config'
351 'rbd_qos_bps_limit': 10240,
352 'rbd_qos_bps_burst': 10240 * 2,
355 'name': 'rbd_qos_bps_limit',
359 'name': 'rbd_qos_bps_burst',
361 'value': str(10240 * 2),
364 self
.create_image(pool
, None, image_name
, size
, configuration
=configuration
)
365 self
.assertStatus(201)
366 img
= self
.get_image('rbd', None, image_name
)
367 self
.assertStatus(200)
368 for conf
in expected
:
369 self
.assertIn(conf
, img
['configuration'])
371 self
.remove_image(pool
, None, image_name
)
373 def test_create_rbd_in_data_pool(self
):
374 if not self
.bluestore_support
:
375 self
.skipTest('requires bluestore cluster')
377 self
.create_pool('data_pool', 2**4, 'erasure')
379 rbd_name
= 'test_rbd_in_data_pool'
380 self
.create_image('rbd', None, rbd_name
, 10240, data_pool
='data_pool')
381 self
.assertStatus(201)
383 img
= self
.get_image('rbd', None, 'test_rbd_in_data_pool')
384 self
.assertStatus(200)
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',
391 'fast-diff', 'layering',
394 self
.remove_image('rbd', None, rbd_name
)
395 self
.assertStatus(204)
396 self
._ceph
_cmd
(['osd', 'pool', 'delete', 'data_pool', 'data_pool',
397 '--yes-i-really-really-mean-it'])
399 def test_create_rbd_twice(self
):
400 res
= self
.create_image('rbd', None, 'test_rbd_twice', 10240)
402 res
= self
.create_image('rbd', None, 'test_rbd_twice', 10240)
403 self
.assertStatus(400)
404 self
.assertEqual(res
, {"code": '17', 'status': 400, "component": "rbd",
405 "detail": "[errno 17] RBD image already exists (error creating image)",
406 'task': {'name': 'rbd/create',
407 'metadata': {'pool_name': 'rbd', 'namespace': None,
408 'image_name': 'test_rbd_twice'}}})
409 self
.remove_image('rbd', None, 'test_rbd_twice')
410 self
.assertStatus(204)
412 def test_snapshots_and_clone_info(self
):
413 self
.create_snapshot('rbd', None, 'img1', 'snap1')
414 self
.create_snapshot('rbd', None, 'img1', 'snap2')
415 self
._rbd
_cmd
(['snap', 'protect', 'rbd/img1@snap1'])
416 self
._rbd
_cmd
(['clone', 'rbd/img1@snap1', 'rbd_iscsi/img1_clone'])
418 img
= self
.get_image('rbd', None, 'img1')
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',
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)
435 img
= self
.get_image('rbd_iscsi', None, 'img1_clone')
436 self
.assertStatus(200)
437 self
._validate
_image
(img
, name
='img1_clone', size
=1073741824,
438 num_objs
=256, obj_size
=4194304,
439 parent
={'pool_name': 'rbd', 'pool_namespace': '',
440 'image_name': 'img1', 'snap_name': 'snap1'},
441 features_name
=['deep-flatten', 'exclusive-lock',
442 'fast-diff', 'layering',
444 self
.remove_image('rbd_iscsi', None, 'img1_clone')
445 self
.assertStatus(204)
447 def test_disk_usage(self
):
448 self
._rbd
_cmd
(['bench', '--io-type', 'write', '--io-total', '50M', 'rbd/img2'])
449 self
.create_snapshot('rbd', None, 'img2', 'snap1')
450 self
._rbd
_cmd
(['bench', '--io-type', 'write', '--io-total', '20M', 'rbd/img2'])
451 self
.create_snapshot('rbd', None, 'img2', 'snap2')
452 self
._rbd
_cmd
(['bench', '--io-type', 'write', '--io-total', '10M', 'rbd/img2'])
453 self
.create_snapshot('rbd', None, 'img2', 'snap3')
454 self
._rbd
_cmd
(['bench', '--io-type', 'write', '--io-total', '5M', 'rbd/img2'])
455 img
= self
.get_image('rbd', None, 'img2')
456 self
.assertStatus(200)
457 self
._validate
_image
(img
, name
='img2', size
=2147483648,
458 total_disk_usage
=268435456, disk_usage
=67108864)
460 def test_delete_non_existent_image(self
):
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')",
465 'task': {'name': 'rbd/delete',
466 'metadata': {'image_spec': 'rbd/i_dont_exist'}}})
468 def test_image_delete(self
):
469 self
.create_image('rbd', None, 'delete_me', 2**30)
470 self
.assertStatus(201)
471 self
.create_snapshot('rbd', None, 'delete_me', 'snap1')
472 self
.assertStatus(201)
473 self
.create_snapshot('rbd', None, 'delete_me', 'snap2')
474 self
.assertStatus(201)
476 img
= self
.get_image('rbd', None, 'delete_me')
477 self
.assertStatus(200)
478 self
._validate
_image
(img
, name
='delete_me', size
=2**30)
479 self
.assertEqual(len(img
['snapshots']), 2)
481 self
.remove_snapshot('rbd', None, 'delete_me', 'snap1')
482 self
.assertStatus(204)
483 self
.remove_snapshot('rbd', None, 'delete_me', 'snap2')
484 self
.assertStatus(204)
486 img
= self
.get_image('rbd', None, 'delete_me')
487 self
.assertStatus(200)
488 self
._validate
_image
(img
, name
='delete_me', size
=2**30)
489 self
.assertEqual(len(img
['snapshots']), 0)
491 self
.remove_image('rbd', None, 'delete_me')
492 self
.assertStatus(204)
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)
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)
507 self
.remove_image('rbd', None, 'delete_me')
508 self
.assertStatus(204)
510 def test_image_rename(self
):
511 self
.create_image('rbd', None, 'edit_img', 2**30)
512 self
.assertStatus(201)
513 self
.get_image('rbd', None, 'edit_img')
514 self
.assertStatus(200)
515 self
.edit_image('rbd', None, 'edit_img', 'new_edit_img')
516 self
.assertStatus(200)
517 self
.get_image('rbd', None, 'edit_img')
518 self
.assertStatus(404)
519 self
.get_image('rbd', None, 'new_edit_img')
520 self
.assertStatus(200)
521 self
.remove_image('rbd', None, 'new_edit_img')
522 self
.assertStatus(204)
524 def test_image_resize(self
):
525 self
.create_image('rbd', None, 'edit_img', 2**30)
526 self
.assertStatus(201)
527 img
= self
.get_image('rbd', None, 'edit_img')
528 self
.assertStatus(200)
529 self
._validate
_image
(img
, size
=2**30)
530 self
.edit_image('rbd', None, 'edit_img', size
=2*2**30)
531 self
.assertStatus(200)
532 img
= self
.get_image('rbd', None, 'edit_img')
533 self
.assertStatus(200)
534 self
._validate
_image
(img
, size
=2*2**30)
535 self
.remove_image('rbd', None, 'edit_img')
536 self
.assertStatus(204)
538 def test_image_change_features(self
):
539 self
.create_image('rbd', None, 'edit_img', 2**30, features
=["layering"])
540 self
.assertStatus(201)
541 img
= self
.get_image('rbd', None, 'edit_img')
542 self
.assertStatus(200)
543 self
._validate
_image
(img
, features_name
=["layering"])
544 self
.edit_image('rbd', None, 'edit_img',
545 features
=["fast-diff", "object-map", "exclusive-lock"])
546 self
.assertStatus(200)
547 img
= self
.get_image('rbd', None, 'edit_img')
548 self
.assertStatus(200)
549 self
._validate
_image
(img
, features_name
=['exclusive-lock',
550 'fast-diff', 'layering',
552 self
.edit_image('rbd', None, 'edit_img',
553 features
=["journaling", "exclusive-lock"])
554 self
.assertStatus(200)
555 img
= self
.get_image('rbd', None, 'edit_img')
556 self
.assertStatus(200)
557 self
._validate
_image
(img
, features_name
=['exclusive-lock',
558 'journaling', 'layering'])
559 self
.remove_image('rbd', None, 'edit_img')
560 self
.assertStatus(204)
562 def test_image_change_config(self
):
564 image
= 'image_with_config'
566 'rbd_qos_bps_limit': 10240,
567 'rbd_qos_write_iops_limit': None
570 'name': 'rbd_qos_bps_limit',
574 'name': 'rbd_qos_write_iops_limit',
579 'rbd_qos_bps_limit': 0,
580 'rbd_qos_bps_burst': 20480,
581 'rbd_qos_write_iops_limit': None
584 'name': 'rbd_qos_bps_limit',
588 'name': 'rbd_qos_bps_burst',
592 'name': 'rbd_qos_write_iops_limit',
597 self
.create_image(pool
, None, image
, 2**30, configuration
=initial_conf
)
598 self
.assertStatus(201)
599 img
= self
.get_image(pool
, None, image
)
600 self
.assertStatus(200)
601 for conf
in initial_expect
:
602 self
.assertIn(conf
, img
['configuration'])
604 self
.edit_image(pool
, None, image
, configuration
=new_conf
)
605 img
= self
.get_image(pool
, None, image
)
606 self
.assertStatus(200)
607 for conf
in new_expect
:
608 self
.assertIn(conf
, img
['configuration'])
610 self
.remove_image(pool
, None, image
)
611 self
.assertStatus(204)
613 def test_update_snapshot(self
):
614 self
.create_snapshot('rbd', None, 'img1', 'snap5')
615 self
.assertStatus(201)
616 img
= self
.get_image('rbd', None, 'img1')
617 self
._validate
_snapshot
_list
(img
['snapshots'], 'snap5', is_protected
=False)
619 self
.update_snapshot('rbd', None, 'img1', 'snap5', 'snap6', None)
620 self
.assertStatus(200)
621 img
= self
.get_image('rbd', None, 'img1')
622 self
._validate
_snapshot
_list
(img
['snapshots'], 'snap6', is_protected
=False)
624 self
.update_snapshot('rbd', None, 'img1', 'snap6', None, True)
625 self
.assertStatus(200)
626 img
= self
.get_image('rbd', None, 'img1')
627 self
._validate
_snapshot
_list
(img
['snapshots'], 'snap6', is_protected
=True)
629 self
.update_snapshot('rbd', None, 'img1', 'snap6', 'snap5', False)
630 self
.assertStatus(200)
631 img
= self
.get_image('rbd', None, 'img1')
632 self
._validate
_snapshot
_list
(img
['snapshots'], 'snap5', is_protected
=False)
634 self
.remove_snapshot('rbd', None, 'img1', 'snap5')
635 self
.assertStatus(204)
637 def test_snapshot_rollback(self
):
638 self
.create_image('rbd', None, 'rollback_img', 2**30,
639 features
=["layering", "exclusive-lock", "fast-diff",
641 self
.assertStatus(201)
642 self
.create_snapshot('rbd', None, 'rollback_img', 'snap1')
643 self
.assertStatus(201)
645 img
= self
.get_image('rbd', None, 'rollback_img')
646 self
.assertStatus(200)
647 self
.assertEqual(img
['disk_usage'], 0)
649 self
._rbd
_cmd
(['bench', '--io-type', 'write', '--io-total', '5M',
652 img
= self
.get_image('rbd', None, 'rollback_img')
653 self
.assertStatus(200)
654 self
.assertGreater(img
['disk_usage'], 0)
656 self
.rollback_snapshot('rbd', None, 'rollback_img', 'snap1')
657 self
.assertStatus([201, 200])
659 img
= self
.get_image('rbd', None, 'rollback_img')
660 self
.assertStatus(200)
661 self
.assertEqual(img
['disk_usage'], 0)
663 self
.remove_snapshot('rbd', None, 'rollback_img', 'snap1')
664 self
.assertStatus(204)
665 self
.remove_image('rbd', None, 'rollback_img')
666 self
.assertStatus(204)
668 def test_clone(self
):
669 self
.create_image('rbd', None, 'cimg', 2**30, features
=["layering"])
670 self
.assertStatus(201)
671 self
.create_snapshot('rbd', None, 'cimg', 'snap1')
672 self
.assertStatus(201)
673 self
.update_snapshot('rbd', None, 'cimg', 'snap1', None, True)
674 self
.assertStatus(200)
675 self
.clone_image('rbd', None, 'cimg', 'snap1', 'rbd', None, 'cimg-clone',
676 features
=["layering", "exclusive-lock", "fast-diff",
678 self
.assertStatus([200, 201])
680 img
= self
.get_image('rbd', None, 'cimg-clone')
681 self
.assertStatus(200)
682 self
._validate
_image
(img
, features_name
=['exclusive-lock',
683 'fast-diff', 'layering',
685 parent
={'pool_name': 'rbd', 'pool_namespace': '',
686 'image_name': 'cimg', 'snap_name': 'snap1'})
688 res
= self
.remove_image('rbd', None, 'cimg')
689 self
.assertStatus(400)
690 self
.assertIn('code', res
)
691 self
.assertEqual(res
['code'], '16')
693 self
.remove_image('rbd', None, 'cimg-clone')
694 self
.assertStatus(204)
695 self
.remove_image('rbd', None, 'cimg')
696 self
.assertStatus(204)
699 self
.create_image('rbd', None, 'coimg', 2**30,
700 features
=["layering", "exclusive-lock", "fast-diff",
702 self
.assertStatus(201)
704 self
._rbd
_cmd
(['bench', '--io-type', 'write', '--io-total', '5M',
707 self
.copy_image('rbd', None, 'coimg', 'rbd_iscsi', None, 'coimg-copy',
708 features
=["layering", "fast-diff", "exclusive-lock",
710 self
.assertStatus([200, 201])
712 img
= self
.get_image('rbd', None, 'coimg')
713 self
.assertStatus(200)
714 self
._validate
_image
(img
, features_name
=['layering', 'exclusive-lock',
715 'fast-diff', 'object-map'])
717 img_copy
= self
.get_image('rbd_iscsi', None, 'coimg-copy')
718 self
._validate
_image
(img_copy
, features_name
=['exclusive-lock',
719 'fast-diff', 'layering',
721 disk_usage
=img
['disk_usage'])
723 self
.remove_image('rbd', None, 'coimg')
724 self
.assertStatus(204)
725 self
.remove_image('rbd_iscsi', None, 'coimg-copy')
726 self
.assertStatus(204)
728 def test_flatten(self
):
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')
733 img
= self
.get_image('rbd_iscsi', None, 'img1_snapf_clone')
734 self
.assertStatus(200)
735 self
.assertIsNotNone(img
['parent'])
737 self
.flatten_image('rbd_iscsi', None, 'img1_snapf_clone')
738 self
.assertStatus([200, 201])
740 img
= self
.get_image('rbd_iscsi', None, 'img1_snapf_clone')
741 self
.assertStatus(200)
742 self
.assertIsNone(img
['parent'])
744 self
.update_snapshot('rbd', None, 'img1', 'snapf', None, False)
745 self
.remove_snapshot('rbd', None, 'img1', 'snapf')
746 self
.assertStatus(204)
748 self
.remove_image('rbd_iscsi', None, 'img1_snapf_clone')
749 self
.assertStatus(204)
751 def test_default_features(self
):
752 default_features
= self
._get
('/api/block/image/default_features')
753 self
.assertEqual(default_features
, [
754 'deep-flatten', 'exclusive-lock', 'fast-diff', 'layering', 'object-map'])
756 def test_image_with_namespace(self
):
757 self
.create_namespace('rbd', 'ns')
758 self
.create_image('rbd', 'ns', 'test', 10240)
759 self
.assertStatus(201)
761 img
= self
.get_image('rbd', 'ns', 'test')
762 self
.assertStatus(200)
764 self
._validate
_image
(img
, name
='test', size
=10240,
765 pool_name
='rbd', namespace
='ns',
766 num_objs
=1, obj_size
=4194304,
767 features_name
=['deep-flatten',
769 'fast-diff', 'layering',
772 self
.remove_image('rbd', 'ns', 'test')
773 self
.remove_namespace('rbd', 'ns')
775 def test_move_image_to_trash(self
):
776 id = self
.create_image_in_trash('rbd', 'test_rbd')
777 self
.assertStatus(200)
779 self
.get_image('rbd', None, 'test_rbd')
780 self
.assertStatus(404)
784 image
= self
.get_trash('rbd', id)
785 self
.assertIsNotNone(image
)
787 self
.remove_trash('rbd', id)
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
)
796 self
.remove_trash('rbd', id)
797 self
.assertStatus(204)
799 def test_restore_trash(self
):
800 id = self
.create_image_in_trash('rbd', 'test_rbd')
802 self
.restore_trash('rbd', None, id, 'test_rbd')
804 self
.get_image('rbd', None, 'test_rbd')
805 self
.assertStatus(200)
807 image
= self
.get_trash('rbd', id)
808 self
.assertIsNone(image
)
810 self
.remove_image('rbd', None, 'test_rbd')
812 def test_remove_expired_trash(self
):
813 id = self
.create_image_in_trash('rbd', 'test_rbd', 0)
814 self
.remove_trash('rbd', id, False)
815 self
.assertStatus(204)
817 image
= self
.get_trash('rbd', id)
818 self
.assertIsNone(image
)
820 def test_remove_not_expired_trash(self
):
821 id = self
.create_image_in_trash('rbd', 'test_rbd', 9999)
822 self
.remove_trash('rbd', id, False)
823 self
.assertStatus(400)
827 image
= self
.get_trash('rbd', id)
828 self
.assertIsNotNone(image
)
830 self
.remove_trash('rbd', id, True)
832 def test_remove_not_expired_trash_with_force(self
):
833 id = self
.create_image_in_trash('rbd', 'test_rbd', 9999)
834 self
.remove_trash('rbd', id, True)
835 self
.assertStatus(204)
837 image
= self
.get_trash('rbd', id)
838 self
.assertIsNone(image
)
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)
846 self
.purge_trash('rbd')
847 self
.assertStatus(200)
851 trash_not_expired
= self
.get_trash('rbd', id_not_expired
)
852 self
.assertIsNotNone(trash_not_expired
)
854 trash_expired
= self
.get_trash('rbd', id_expired
)
855 self
.assertIsNone(trash_expired
)
857 def test_list_namespaces(self
):
858 self
.create_namespace('rbd', 'ns')
860 namespaces
= self
._get
('/api/block/pool/rbd/namespace')
861 self
.assertStatus(200)
862 self
.assertEqual(len(namespaces
), 1)
864 self
.remove_namespace('rbd', 'ns')