1 # vim: expandtab smarttab shiftwidth=4 softtabstop=4
13 from datetime
import datetime
, timedelta
14 from nose
import with_setup
, SkipTest
15 from nose
.plugins
.attrib
import attr
16 from nose
.tools
import eq_
as eq
, assert_raises
, assert_not_equal
17 from rados
import (Rados
,
18 LIBRADOS_OP_FLAG_FADVISE_DONTNEED
,
19 LIBRADOS_OP_FLAG_FADVISE_NOCACHE
,
20 LIBRADOS_OP_FLAG_FADVISE_RANDOM
)
21 from rbd
import (RBD
, Group
, Image
, ImageNotFound
, InvalidArgument
, ImageExists
,
22 ImageBusy
, ImageHasSnapshots
, ReadOnlyImage
,
23 FunctionNotSupported
, ArgumentOutOfRange
,
24 ECANCELED
, OperationCanceled
,
25 DiskQuotaExceeded
, ConnectionShutdown
, PermissionError
,
26 RBD_FEATURE_LAYERING
, RBD_FEATURE_STRIPINGV2
,
27 RBD_FEATURE_EXCLUSIVE_LOCK
, RBD_FEATURE_JOURNALING
,
28 RBD_FEATURE_DEEP_FLATTEN
, RBD_FEATURE_FAST_DIFF
,
29 RBD_FEATURE_OBJECT_MAP
,
30 RBD_MIRROR_MODE_DISABLED
, RBD_MIRROR_MODE_IMAGE
,
31 RBD_MIRROR_MODE_POOL
, RBD_MIRROR_IMAGE_ENABLED
,
32 RBD_MIRROR_IMAGE_DISABLED
, MIRROR_IMAGE_STATUS_STATE_UNKNOWN
,
33 RBD_MIRROR_IMAGE_MODE_JOURNAL
, RBD_MIRROR_IMAGE_MODE_SNAPSHOT
,
34 RBD_LOCK_MODE_EXCLUSIVE
, RBD_OPERATION_FEATURE_GROUP
,
35 RBD_SNAP_NAMESPACE_TYPE_TRASH
,
36 RBD_SNAP_NAMESPACE_TYPE_MIRROR
,
37 RBD_IMAGE_MIGRATION_STATE_PREPARED
, RBD_CONFIG_SOURCE_CONFIG
,
38 RBD_CONFIG_SOURCE_POOL
, RBD_CONFIG_SOURCE_IMAGE
,
39 RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST
,
40 RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY
,
41 RBD_MIRROR_PEER_DIRECTION_RX
, RBD_MIRROR_PEER_DIRECTION_RX_TX
,
42 RBD_SNAP_REMOVE_UNPROTECT
, RBD_SNAP_MIRROR_STATE_PRIMARY
,
43 RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED
,
44 RBD_SNAP_CREATE_SKIP_QUIESCE
,
45 RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR
,
46 RBD_WRITE_ZEROES_FLAG_THICK_PROVISION
,
47 RBD_ENCRYPTION_FORMAT_LUKS1
, RBD_ENCRYPTION_FORMAT_LUKS2
,
48 RBD_ENCRYPTION_FORMAT_LUKS
)
61 IMG_SIZE
= 8 << 20 # 8 MiB
62 IMG_ORDER
= 22 # 4 MiB objects
64 os
.environ
["RBD_FORCE_ALLOW_V1"] = "1"
68 rados
= Rados(conffile
='')
71 pool_name
= get_temp_pool_name()
72 rados
.create_pool(pool_name
)
74 ioctx
= rados
.open_ioctx(pool_name
)
75 RBD().pool_init(ioctx
, True)
77 features
= os
.getenv("RBD_FEATURES")
78 if features
is not None:
79 features
= int(features
)
81 def teardown_module():
85 rados
.delete_pool(pool_name
)
88 def get_temp_pool_name():
91 return "test-rbd-api-" + socket
.gethostname() + '-' + str(os
.getpid()) + \
94 def get_temp_image_name():
97 return "image" + str(image_idx
)
99 def get_temp_group_name():
102 return "group" + str(group_idx
)
104 def get_temp_snap_name():
107 return "snap" + str(snap_idx
)
111 image_name
= get_temp_image_name()
112 if features
is not None:
113 RBD().create(ioctx
, image_name
, IMG_SIZE
, IMG_ORDER
, old_format
=False,
114 features
=int(features
))
116 RBD().create(ioctx
, image_name
, IMG_SIZE
, IMG_ORDER
, old_format
=True)
120 if image_name
is not None:
121 RBD().remove(ioctx
, image_name
)
125 group_name
= get_temp_group_name()
126 RBD().group_create(ioctx
, group_name
)
129 if group_name
is not None:
130 RBD().group_remove(ioctx
, group_name
)
133 new_group_name
= "new" + group_name
134 RBD().group_rename(ioctx
, group_name
, new_group_name
)
136 def require_new_format():
138 def _require_new_format(*args
, **kwargs
):
142 return fn(*args
, **kwargs
)
143 return functools
.wraps(fn
)(_require_new_format
)
146 def require_features(required_features
):
148 def _require_features(*args
, **kwargs
):
152 for feature
in required_features
:
153 if feature
& features
!= feature
:
155 return fn(*args
, **kwargs
)
156 return functools
.wraps(fn
)(_require_features
)
161 def _require_linux(*args
, **kwargs
):
162 if platform
.system() != "Linux":
164 return fn(*args
, **kwargs
)
165 return functools
.wraps(fn
)(_require_linux
)
168 def blocklist_features(blocklisted_features
):
170 def _blocklist_features(*args
, **kwargs
):
172 for feature
in blocklisted_features
:
173 if features
is not None and feature
& features
== feature
:
175 return fn(*args
, **kwargs
)
176 return functools
.wraps(fn
)(_blocklist_features
)
186 def check_default_params(format
, order
=None, features
=None, stripe_count
=None,
187 stripe_unit
=None, exception
=None):
191 for k
in ['rbd_default_format', 'rbd_default_order', 'rbd_default_features',
192 'rbd_default_stripe_count', 'rbd_default_stripe_unit']:
193 orig_vals
[k
] = rados
.conf_get(k
)
195 rados
.conf_set('rbd_default_format', str(format
))
196 if order
is not None:
197 rados
.conf_set('rbd_default_order', str(order
or 0))
198 if features
is not None:
199 rados
.conf_set('rbd_default_features', str(features
or 0))
200 if stripe_count
is not None:
201 rados
.conf_set('rbd_default_stripe_count', str(stripe_count
or 0))
202 if stripe_unit
is not None:
203 rados
.conf_set('rbd_default_stripe_unit', str(stripe_unit
or 0))
204 feature_data_pool
= 0
205 datapool
= rados
.conf_get('rbd_default_data_pool')
206 if not len(datapool
) == 0:
207 feature_data_pool
= 128
208 image_name
= get_temp_image_name()
209 if exception
is None:
210 RBD().create(ioctx
, image_name
, IMG_SIZE
, old_format
=(format
== 1))
212 with
Image(ioctx
, image_name
) as image
:
213 eq(format
== 1, image
.old_format())
215 expected_order
= int(rados
.conf_get('rbd_default_order'))
216 actual_order
= image
.stat()['order']
217 eq(expected_order
, actual_order
)
219 expected_features
= features
221 expected_features
= 0
222 elif expected_features
is None:
223 expected_features
= 61 | feature_data_pool
225 expected_features |
= feature_data_pool
226 eq(expected_features
, image
.features())
228 expected_stripe_count
= stripe_count
229 if not expected_stripe_count
or format
== 1 or \
230 features
& RBD_FEATURE_STRIPINGV2
== 0:
231 expected_stripe_count
= 1
232 eq(expected_stripe_count
, image
.stripe_count())
234 expected_stripe_unit
= stripe_unit
235 if not expected_stripe_unit
or format
== 1 or \
236 features
& RBD_FEATURE_STRIPINGV2
== 0:
237 expected_stripe_unit
= 1 << actual_order
238 eq(expected_stripe_unit
, image
.stripe_unit())
240 RBD().remove(ioctx
, image_name
)
242 assert_raises(exception
, RBD().create
, ioctx
, image_name
, IMG_SIZE
)
244 for k
, v
in orig_vals
.items():
247 def test_create_defaults():
248 # basic format 1 and 2
249 check_default_params(1)
250 check_default_params(2)
252 check_default_params(1, 0, exception
=ArgumentOutOfRange
)
253 check_default_params(2, 0, exception
=ArgumentOutOfRange
)
254 check_default_params(1, 11, exception
=ArgumentOutOfRange
)
255 check_default_params(2, 11, exception
=ArgumentOutOfRange
)
256 check_default_params(1, 65, exception
=ArgumentOutOfRange
)
257 check_default_params(2, 65, exception
=ArgumentOutOfRange
)
258 # striping and features are ignored for format 1
259 check_default_params(1, 20, 0, 1, 1)
260 check_default_params(1, 20, 3, 1, 1)
261 check_default_params(1, 20, 0, 0, 0)
262 # striping is ignored if stripingv2 is not set
263 check_default_params(2, 20, 0, 1, 1 << 20)
264 check_default_params(2, 20, RBD_FEATURE_LAYERING
, 1, 1 << 20)
265 check_default_params(2, 20, 0, 0, 0)
266 # striping with stripingv2 is fine
267 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 1, 1 << 16)
268 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 10, 1 << 20)
269 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 10, 1 << 16)
270 check_default_params(2, 20, 0, 0, 0)
271 # make sure invalid combinations of stripe unit and order are still invalid
272 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 10, 1 << 50, exception
=InvalidArgument
)
273 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 10, 100, exception
=InvalidArgument
)
274 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 0, 1, exception
=InvalidArgument
)
275 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 1, 0, exception
=InvalidArgument
)
276 # 0 stripe unit and count are still ignored
277 check_default_params(2, 22, 0, 0, 0)
279 def test_context_manager():
280 with
Rados(conffile
='') as cluster
:
281 with cluster
.open_ioctx(pool_name
) as ioctx
:
282 image_name
= get_temp_image_name()
283 RBD().create(ioctx
, image_name
, IMG_SIZE
)
284 with
Image(ioctx
, image_name
) as image
:
285 data
= rand_data(256)
287 read
= image
.read(0, 256)
288 RBD().remove(ioctx
, image_name
)
291 def test_open_read_only():
292 with
Rados(conffile
='') as cluster
:
293 with cluster
.open_ioctx(pool_name
) as ioctx
:
294 image_name
= get_temp_image_name()
295 RBD().create(ioctx
, image_name
, IMG_SIZE
)
296 data
= rand_data(256)
297 with
Image(ioctx
, image_name
) as image
:
299 image
.create_snap('snap')
300 with
Image(ioctx
, image_name
, read_only
=True) as image
:
301 read
= image
.read(0, 256)
303 assert_raises(ReadOnlyImage
, image
.write
, data
, 0)
304 assert_raises(ReadOnlyImage
, image
.create_snap
, 'test')
305 assert_raises(ReadOnlyImage
, image
.remove_snap
, 'snap')
306 assert_raises(ReadOnlyImage
, image
.rollback_to_snap
, 'snap')
307 assert_raises(ReadOnlyImage
, image
.protect_snap
, 'snap')
308 assert_raises(ReadOnlyImage
, image
.unprotect_snap
, 'snap')
309 assert_raises(ReadOnlyImage
, image
.unprotect_snap
, 'snap')
310 assert_raises(ReadOnlyImage
, image
.flatten
)
311 with
Image(ioctx
, image_name
) as image
:
312 image
.remove_snap('snap')
313 RBD().remove(ioctx
, image_name
)
318 image_name
= get_temp_image_name()
319 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
+ 'dne')
320 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
, 'snap')
322 def test_open_readonly_dne():
324 image_name
= get_temp_image_name()
325 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
+ 'dne',
327 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
, 'snap',
330 @require_new_format()
331 def test_open_by_id():
332 with
Rados(conffile
='') as cluster
:
333 with cluster
.open_ioctx(pool_name
) as ioctx
:
334 image_name
= get_temp_image_name()
335 RBD().create(ioctx
, image_name
, IMG_SIZE
)
336 with
Image(ioctx
, image_name
) as image
:
337 image_id
= image
.id()
338 with
Image(ioctx
, image_id
=image_id
) as image
:
339 eq(image
.get_name(), image_name
)
340 RBD().remove(ioctx
, image_name
)
343 with
Rados(conffile
='') as cluster
:
344 with cluster
.open_ioctx(pool_name
) as ioctx
:
345 image_name
= get_temp_image_name()
347 RBD().create(ioctx
, image_name
, IMG_SIZE
, order
)
349 # this is a list so that the open_cb() can modify it
351 def open_cb(_
, image_
):
354 comp
= RBD().aio_open_image(open_cb
, ioctx
, image_name
)
355 comp
.wait_for_complete_and_cb()
356 eq(comp
.get_return_value(), 0)
357 eq(sys
.getrefcount(comp
), 2)
358 assert_not_equal(image
[0], None)
361 eq(image
.get_name(), image_name
)
362 check_stat(image
.stat(), IMG_SIZE
, order
)
368 comp
= image
.aio_close(close_cb
)
369 comp
.wait_for_complete_and_cb()
370 eq(comp
.get_return_value(), 0)
371 eq(sys
.getrefcount(comp
), 2)
374 RBD().remove(ioctx
, image_name
)
376 def test_remove_dne():
377 assert_raises(ImageNotFound
, remove_image
)
379 def test_list_empty():
380 eq([], RBD().list(ioctx
))
382 @with_setup(create_image
, remove_image
)
384 eq([image_name
], RBD().list(ioctx
))
386 with
Image(ioctx
, image_name
) as image
:
387 image_id
= image
.id()
388 eq([{'id': image_id
, 'name': image_name
}], list(RBD().list2(ioctx
)))
390 @with_setup(create_image
)
391 def test_remove_with_progress():
392 d
= {'received_callback': False}
393 def progress_cb(current
, total
):
394 d
['received_callback'] = True
397 RBD().remove(ioctx
, image_name
, on_progress
=progress_cb
)
398 eq(True, d
['received_callback'])
400 @with_setup(create_image
)
401 def test_remove_canceled():
402 def progress_cb(current
, total
):
405 assert_raises(OperationCanceled
, RBD().remove
, ioctx
, image_name
,
406 on_progress
=progress_cb
)
408 @with_setup(create_image
, remove_image
)
411 image_name2
= get_temp_image_name()
412 rbd
.rename(ioctx
, image_name
, image_name2
)
413 eq([image_name2
], rbd
.list(ioctx
))
414 rbd
.rename(ioctx
, image_name2
, image_name
)
415 eq([image_name
], rbd
.list(ioctx
))
417 def test_pool_metadata():
419 metadata
= list(rbd
.pool_metadata_list(ioctx
))
421 assert_raises(KeyError, rbd
.pool_metadata_get
, ioctx
, "key1")
422 rbd
.pool_metadata_set(ioctx
, "key1", "value1")
423 rbd
.pool_metadata_set(ioctx
, "key2", "value2")
424 value
= rbd
.pool_metadata_get(ioctx
, "key1")
426 value
= rbd
.pool_metadata_get(ioctx
, "key2")
428 metadata
= list(rbd
.pool_metadata_list(ioctx
))
430 rbd
.pool_metadata_remove(ioctx
, "key1")
431 metadata
= list(rbd
.pool_metadata_list(ioctx
))
433 eq(metadata
[0], ("key2", "value2"))
434 rbd
.pool_metadata_remove(ioctx
, "key2")
435 assert_raises(KeyError, rbd
.pool_metadata_remove
, ioctx
, "key2")
436 metadata
= list(rbd
.pool_metadata_list(ioctx
))
441 rbd
.pool_metadata_set(ioctx
, "key" + str(i
), "X" * 1025)
442 metadata
= list(rbd
.pool_metadata_list(ioctx
))
445 rbd
.pool_metadata_remove(ioctx
, "key" + str(i
))
446 metadata
= list(rbd
.pool_metadata_list(ioctx
))
447 eq(len(metadata
), N
- i
- 1)
449 def test_config_list():
452 for option
in rbd
.config_list(ioctx
):
453 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
455 rbd
.pool_metadata_set(ioctx
, "conf_rbd_cache", "true")
457 for option
in rbd
.config_list(ioctx
):
458 if option
['name'] == "rbd_cache":
459 eq(option
['source'], RBD_CONFIG_SOURCE_POOL
)
461 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
463 rbd
.pool_metadata_remove(ioctx
, "conf_rbd_cache")
465 for option
in rbd
.config_list(ioctx
):
466 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
468 def test_pool_config_set_and_get_and_remove():
471 for option
in rbd
.config_list(ioctx
):
472 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
474 rbd
.config_set(ioctx
, "rbd_request_timed_out_seconds", "100")
475 new_value
= rbd
.config_get(ioctx
, "rbd_request_timed_out_seconds")
477 rbd
.config_remove(ioctx
, "rbd_request_timed_out_seconds")
479 for option
in rbd
.config_list(ioctx
):
480 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
482 def test_namespaces():
485 eq(False, rbd
.namespace_exists(ioctx
, 'ns1'))
486 eq([], rbd
.namespace_list(ioctx
))
487 assert_raises(ImageNotFound
, rbd
.namespace_remove
, ioctx
, 'ns1')
489 rbd
.namespace_create(ioctx
, 'ns1')
490 eq(True, rbd
.namespace_exists(ioctx
, 'ns1'))
492 assert_raises(ImageExists
, rbd
.namespace_create
, ioctx
, 'ns1')
493 eq(['ns1'], rbd
.namespace_list(ioctx
))
494 rbd
.namespace_remove(ioctx
, 'ns1')
495 eq([], rbd
.namespace_list(ioctx
))
497 @require_new_format()
498 def test_pool_stats():
502 image1
= create_image()
503 image2
= create_image()
504 image3
= create_image()
505 image4
= create_image()
506 with
Image(ioctx
, image4
) as image
:
507 image
.create_snap('snap')
510 stats
= rbd
.pool_stats_get(ioctx
)
511 eq(stats
['image_count'], 4)
512 eq(stats
['image_provisioned_bytes'], 3 * IMG_SIZE
)
513 eq(stats
['image_max_provisioned_bytes'], 4 * IMG_SIZE
)
514 eq(stats
['image_snap_count'], 1)
515 eq(stats
['trash_count'], 0)
516 eq(stats
['trash_provisioned_bytes'], 0)
517 eq(stats
['trash_max_provisioned_bytes'], 0)
518 eq(stats
['trash_snap_count'], 0)
520 rbd
.remove(ioctx
, image1
)
521 rbd
.remove(ioctx
, image2
)
522 rbd
.remove(ioctx
, image3
)
523 with
Image(ioctx
, image4
) as image
:
524 image
.remove_snap('snap')
525 rbd
.remove(ioctx
, image4
)
528 return os
.urandom(size
)
530 def check_stat(info
, size
, order
):
531 assert 'block_name_prefix' in info
532 eq(info
['size'], size
)
533 eq(info
['order'], order
)
534 eq(info
['num_objs'], size
// (1 << order
))
535 eq(info
['obj_size'], 1 << order
)
537 @require_new_format()
538 def test_features_to_string():
540 features
= RBD_FEATURE_DEEP_FLATTEN | RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_FAST_DIFF \
541 | RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP
542 expected_features_string
= "deep-flatten,exclusive-lock,fast-diff,layering,object-map"
543 features_string
= rbd
.features_to_string(features
)
544 eq(expected_features_string
, features_string
)
546 features
= RBD_FEATURE_LAYERING
547 features_string
= rbd
.features_to_string(features
)
548 eq(features_string
, "layering")
551 assert_raises(InvalidArgument
, rbd
.features_to_string
, features
)
553 @require_new_format()
554 def test_features_from_string():
556 features_string
= "deep-flatten,exclusive-lock,fast-diff,layering,object-map"
557 expected_features_bitmask
= RBD_FEATURE_DEEP_FLATTEN | RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_FAST_DIFF \
558 | RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP
559 features
= rbd
.features_from_string(features_string
)
560 eq(expected_features_bitmask
, features
)
562 features_string
= "layering"
563 features
= rbd
.features_from_string(features_string
)
564 eq(features
, RBD_FEATURE_LAYERING
)
566 class TestImage(object):
571 self
.image
= Image(ioctx
, image_name
)
578 @require_new_format()
579 @blocklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
580 def test_update_features(self
):
581 features
= self
.image
.features()
582 self
.image
.update_features(RBD_FEATURE_EXCLUSIVE_LOCK
, True)
583 eq(features | RBD_FEATURE_EXCLUSIVE_LOCK
, self
.image
.features())
585 @require_features([RBD_FEATURE_STRIPINGV2
])
586 def test_create_with_params(self
):
588 image_name
= get_temp_image_name()
590 stripe_unit
= 1 << 20
592 self
.rbd
.create(ioctx
, image_name
, IMG_SIZE
, order
,
593 False, features
, stripe_unit
, stripe_count
)
594 image
= Image(ioctx
, image_name
)
596 check_stat(info
, IMG_SIZE
, order
)
597 eq(image
.features(), features
)
598 eq(image
.stripe_unit(), stripe_unit
)
599 eq(image
.stripe_count(), stripe_count
)
601 RBD().remove(ioctx
, image_name
)
603 @require_new_format()
605 assert_not_equal(b
'', self
.image
.id())
607 def test_block_name_prefix(self
):
608 assert_not_equal(b
'', self
.image
.block_name_prefix())
610 def test_create_timestamp(self
):
611 timestamp
= self
.image
.create_timestamp()
612 assert_not_equal(0, timestamp
.year
)
613 assert_not_equal(1970, timestamp
.year
)
615 def test_access_timestamp(self
):
616 timestamp
= self
.image
.access_timestamp()
617 assert_not_equal(0, timestamp
.year
)
618 assert_not_equal(1970, timestamp
.year
)
620 def test_modify_timestamp(self
):
621 timestamp
= self
.image
.modify_timestamp()
622 assert_not_equal(0, timestamp
.year
)
623 assert_not_equal(1970, timestamp
.year
)
625 def test_invalidate_cache(self
):
626 self
.image
.write(b
'abc', 0)
627 eq(b
'abc', self
.image
.read(0, 3))
628 self
.image
.invalidate_cache()
629 eq(b
'abc', self
.image
.read(0, 3))
632 info
= self
.image
.stat()
633 check_stat(info
, IMG_SIZE
, IMG_ORDER
)
635 def test_flags(self
):
636 flags
= self
.image
.flags()
639 def test_image_auto_close(self
):
640 image
= Image(ioctx
, image_name
)
642 def test_use_after_close(self
):
644 assert_raises(InvalidArgument
, self
.image
.stat
)
646 def test_write(self
):
647 data
= rand_data(256)
648 self
.image
.write(data
, 0)
650 def test_write_with_fadvise_flags(self
):
651 data
= rand_data(256)
652 self
.image
.write(data
, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED
)
653 self
.image
.write(data
, 0, LIBRADOS_OP_FLAG_FADVISE_NOCACHE
)
655 def test_write_zeroes(self
):
656 data
= rand_data(256)
657 self
.image
.write(data
, 0)
658 self
.image
.write_zeroes(0, 256)
659 eq(self
.image
.read(256, 256), b
'\0' * 256)
660 check_diff(self
.image
, 0, IMG_SIZE
, None, [])
662 def test_write_zeroes_thick_provision(self
):
663 data
= rand_data(256)
664 self
.image
.write(data
, 0)
665 self
.image
.write_zeroes(0, 256, RBD_WRITE_ZEROES_FLAG_THICK_PROVISION
)
666 eq(self
.image
.read(256, 256), b
'\0' * 256)
667 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 256, True)])
670 data
= self
.image
.read(0, 20)
673 def test_read_with_fadvise_flags(self
):
674 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED
)
676 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM
)
679 def test_large_write(self
):
680 data
= rand_data(IMG_SIZE
)
681 self
.image
.write(data
, 0)
683 def test_large_read(self
):
684 data
= self
.image
.read(0, IMG_SIZE
)
685 eq(data
, b
'\0' * IMG_SIZE
)
687 def test_write_read(self
):
688 data
= rand_data(256)
690 self
.image
.write(data
, offset
)
691 read
= self
.image
.read(offset
, 256)
694 def test_read_bad_offset(self
):
695 assert_raises(InvalidArgument
, self
.image
.read
, IMG_SIZE
+ 1, IMG_SIZE
)
697 def test_resize(self
):
698 new_size
= IMG_SIZE
* 2
699 self
.image
.resize(new_size
)
700 info
= self
.image
.stat()
701 check_stat(info
, new_size
, IMG_ORDER
)
703 def test_resize_allow_shrink_False(self
):
704 new_size
= IMG_SIZE
* 2
705 self
.image
.resize(new_size
)
706 info
= self
.image
.stat()
707 check_stat(info
, new_size
, IMG_ORDER
)
708 assert_raises(InvalidArgument
, self
.image
.resize
, IMG_SIZE
, False)
711 eq(IMG_SIZE
, self
.image
.size())
712 self
.image
.create_snap('snap1')
713 new_size
= IMG_SIZE
* 2
714 self
.image
.resize(new_size
)
715 eq(new_size
, self
.image
.size())
716 self
.image
.create_snap('snap2')
717 self
.image
.set_snap('snap2')
718 eq(new_size
, self
.image
.size())
719 self
.image
.set_snap('snap1')
720 eq(IMG_SIZE
, self
.image
.size())
721 self
.image
.set_snap(None)
722 eq(new_size
, self
.image
.size())
723 self
.image
.remove_snap('snap1')
724 self
.image
.remove_snap('snap2')
726 def test_resize_down(self
):
727 new_size
= IMG_SIZE
// 2
728 data
= rand_data(256)
729 self
.image
.write(data
, IMG_SIZE
// 2);
730 self
.image
.resize(new_size
)
731 self
.image
.resize(IMG_SIZE
)
732 read
= self
.image
.read(IMG_SIZE
// 2, 256)
733 eq(b
'\0' * 256, read
)
735 def test_resize_bytes(self
):
736 new_size
= IMG_SIZE
// 2 - 5
737 data
= rand_data(256)
738 self
.image
.write(data
, IMG_SIZE
// 2 - 10);
739 self
.image
.resize(new_size
)
740 self
.image
.resize(IMG_SIZE
)
741 read
= self
.image
.read(IMG_SIZE
// 2 - 10, 5)
743 read
= self
.image
.read(IMG_SIZE
// 2 - 5, 251)
744 eq(b
'\0' * 251, read
)
746 def _test_copy(self
, features
=None, order
=None, stripe_unit
=None,
749 data
= rand_data(256)
750 self
.image
.write(data
, 256)
751 image_name
= get_temp_image_name()
753 self
.image
.copy(ioctx
, image_name
)
755 self
.image
.copy(ioctx
, image_name
, features
)
756 elif stripe_unit
is None:
757 self
.image
.copy(ioctx
, image_name
, features
, order
)
758 elif stripe_count
is None:
759 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
)
761 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
,
763 assert_raises(ImageExists
, self
.image
.copy
, ioctx
, image_name
)
764 copy
= Image(ioctx
, image_name
)
765 copy_data
= copy
.read(256, 256)
767 self
.rbd
.remove(ioctx
, image_name
)
773 def test_copy2(self
):
774 self
._test
_copy
(self
.image
.features(), self
.image
.stat()['order'])
776 @require_features([RBD_FEATURE_STRIPINGV2
])
777 def test_copy3(self
):
779 self
._test
_copy
(features
, self
.image
.stat()['order'],
780 self
.image
.stripe_unit(), self
.image
.stripe_count())
782 @attr('SKIP_IF_CRIMSON')
783 def test_deep_copy(self
):
786 self
.image
.write(b
'a' * 256, 0)
787 self
.image
.create_snap('snap1')
788 self
.image
.write(b
'b' * 256, 0)
789 dst_name
= get_temp_image_name()
790 self
.image
.deep_copy(ioctx
, dst_name
, features
=features
,
791 order
=self
.image
.stat()['order'],
792 stripe_unit
=self
.image
.stripe_unit(),
793 stripe_count
=self
.image
.stripe_count(),
795 self
.image
.remove_snap('snap1')
796 with
Image(ioctx
, dst_name
, 'snap1') as copy
:
797 copy_data
= copy
.read(0, 256)
798 eq(b
'a' * 256, copy_data
)
799 with
Image(ioctx
, dst_name
) as copy
:
800 copy_data
= copy
.read(0, 256)
801 eq(b
'b' * 256, copy_data
)
802 copy
.remove_snap('snap1')
803 self
.rbd
.remove(ioctx
, dst_name
)
805 @require_features([RBD_FEATURE_LAYERING
])
806 def test_deep_copy_clone(self
):
809 self
.image
.write(b
'a' * 256, 0)
810 self
.image
.create_snap('snap1')
811 self
.image
.write(b
'b' * 256, 0)
812 self
.image
.protect_snap('snap1')
813 clone_name
= get_temp_image_name()
814 dst_name
= get_temp_image_name()
815 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name
)
816 with
Image(ioctx
, clone_name
) as child
:
817 child
.create_snap('snap1')
818 child
.deep_copy(ioctx
, dst_name
, features
=features
,
819 order
=self
.image
.stat()['order'],
820 stripe_unit
=self
.image
.stripe_unit(),
821 stripe_count
=self
.image
.stripe_count(),
823 child
.remove_snap('snap1')
825 with
Image(ioctx
, dst_name
) as copy
:
826 copy_data
= copy
.read(0, 256)
827 eq(b
'a' * 256, copy_data
)
828 copy
.remove_snap('snap1')
829 self
.rbd
.remove(ioctx
, dst_name
)
830 self
.rbd
.remove(ioctx
, clone_name
)
831 self
.image
.unprotect_snap('snap1')
832 self
.image
.remove_snap('snap1')
834 def test_create_snap(self
):
836 self
.image
.create_snap('snap1')
837 read
= self
.image
.read(0, 256)
838 eq(read
, b
'\0' * 256)
839 data
= rand_data(256)
840 self
.image
.write(data
, 0)
841 read
= self
.image
.read(0, 256)
843 at_snapshot
= Image(ioctx
, image_name
, 'snap1')
844 snap_data
= at_snapshot
.read(0, 256)
846 eq(snap_data
, b
'\0' * 256)
847 self
.image
.remove_snap('snap1')
849 def test_create_snap_exists(self
):
850 self
.image
.create_snap('snap1')
851 assert_raises(ImageExists
, self
.image
.create_snap
, 'snap1')
852 self
.image
.remove_snap('snap1')
854 def test_create_snap_flags(self
):
855 self
.image
.create_snap('snap1', 0)
856 self
.image
.remove_snap('snap1')
857 self
.image
.create_snap('snap1', RBD_SNAP_CREATE_SKIP_QUIESCE
)
858 self
.image
.remove_snap('snap1')
859 self
.image
.create_snap('snap1', RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR
)
860 self
.image
.remove_snap('snap1')
862 def test_list_snaps(self
):
863 eq([], list(self
.image
.list_snaps()))
864 self
.image
.create_snap('snap1')
865 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
866 self
.image
.create_snap('snap2')
867 eq(['snap1', 'snap2'], [snap
['name'] for snap
in self
.image
.list_snaps()])
868 self
.image
.remove_snap('snap1')
869 self
.image
.remove_snap('snap2')
871 def test_list_snaps_iterator_auto_close(self
):
872 self
.image
.create_snap('snap1')
873 self
.image
.list_snaps()
874 self
.image
.remove_snap('snap1')
876 def test_remove_snap(self
):
877 eq([], list(self
.image
.list_snaps()))
878 self
.image
.create_snap('snap1')
879 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
880 self
.image
.remove_snap('snap1')
881 eq([], list(self
.image
.list_snaps()))
883 def test_remove_snap_not_found(self
):
884 assert_raises(ImageNotFound
, self
.image
.remove_snap
, 'snap1')
886 @require_features([RBD_FEATURE_LAYERING
])
887 def test_remove_snap2(self
):
888 self
.image
.create_snap('snap1')
889 self
.image
.protect_snap('snap1')
890 assert(self
.image
.is_protected_snap('snap1'))
891 self
.image
.remove_snap2('snap1', RBD_SNAP_REMOVE_UNPROTECT
)
892 eq([], list(self
.image
.list_snaps()))
894 def test_remove_snap_by_id(self
):
895 eq([], list(self
.image
.list_snaps()))
896 self
.image
.create_snap('snap1')
897 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
898 for snap
in self
.image
.list_snaps():
900 self
.image
.remove_snap_by_id(snap_id
)
901 eq([], list(self
.image
.list_snaps()))
903 def test_rename_snap(self
):
904 eq([], list(self
.image
.list_snaps()))
905 self
.image
.create_snap('snap1')
906 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
907 self
.image
.rename_snap("snap1", "snap1-rename")
908 eq(['snap1-rename'], [snap
['name'] for snap
in self
.image
.list_snaps()])
909 self
.image
.remove_snap('snap1-rename')
910 eq([], list(self
.image
.list_snaps()))
912 @require_features([RBD_FEATURE_LAYERING
])
913 def test_protect_snap(self
):
914 self
.image
.create_snap('snap1')
915 assert(not self
.image
.is_protected_snap('snap1'))
916 self
.image
.protect_snap('snap1')
917 assert(self
.image
.is_protected_snap('snap1'))
918 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
919 self
.image
.unprotect_snap('snap1')
920 assert(not self
.image
.is_protected_snap('snap1'))
921 self
.image
.remove_snap('snap1')
922 assert_raises(ImageNotFound
, self
.image
.unprotect_snap
, 'snap1')
923 assert_raises(ImageNotFound
, self
.image
.is_protected_snap
, 'snap1')
925 def test_snap_exists(self
):
926 self
.image
.create_snap('snap1')
927 eq(self
.image
.snap_exists('snap1'), True)
928 self
.image
.remove_snap('snap1')
929 eq(self
.image
.snap_exists('snap1'), False)
931 def test_snap_timestamp(self
):
932 self
.image
.create_snap('snap1')
933 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
934 for snap
in self
.image
.list_snaps():
936 time
= self
.image
.get_snap_timestamp(snap_id
)
937 assert_not_equal(b
'', time
.year
)
938 assert_not_equal(0, time
.year
)
939 assert_not_equal(time
.year
, '1970')
940 self
.image
.remove_snap('snap1')
942 def test_limit_snaps(self
):
943 self
.image
.set_snap_limit(2)
944 eq(2, self
.image
.get_snap_limit())
945 self
.image
.create_snap('snap1')
946 self
.image
.create_snap('snap2')
947 assert_raises(DiskQuotaExceeded
, self
.image
.create_snap
, 'snap3')
948 self
.image
.remove_snap_limit()
949 self
.image
.create_snap('snap3')
951 self
.image
.remove_snap('snap1')
952 self
.image
.remove_snap('snap2')
953 self
.image
.remove_snap('snap3')
955 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
956 def test_remove_with_exclusive_lock(self
):
957 assert_raises(ImageBusy
, remove_image
)
959 @blocklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
960 def test_remove_with_snap(self
):
961 self
.image
.create_snap('snap1')
962 assert_raises(ImageHasSnapshots
, remove_image
)
963 self
.image
.remove_snap('snap1')
965 @blocklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
966 def test_remove_with_watcher(self
):
967 data
= rand_data(256)
968 self
.image
.write(data
, 0)
969 assert_raises(ImageBusy
, remove_image
)
970 read
= self
.image
.read(0, 256)
973 def test_rollback_to_snap(self
):
974 self
.image
.write(b
'\0' * 256, 0)
975 self
.image
.create_snap('snap1')
976 read
= self
.image
.read(0, 256)
977 eq(read
, b
'\0' * 256)
978 data
= rand_data(256)
979 self
.image
.write(data
, 0)
980 read
= self
.image
.read(0, 256)
982 self
.image
.rollback_to_snap('snap1')
983 read
= self
.image
.read(0, 256)
984 eq(read
, b
'\0' * 256)
985 self
.image
.remove_snap('snap1')
987 def test_rollback_to_snap_sparse(self
):
988 self
.image
.create_snap('snap1')
989 read
= self
.image
.read(0, 256)
990 eq(read
, b
'\0' * 256)
991 data
= rand_data(256)
992 self
.image
.write(data
, 0)
993 read
= self
.image
.read(0, 256)
995 self
.image
.rollback_to_snap('snap1')
996 read
= self
.image
.read(0, 256)
997 eq(read
, b
'\0' * 256)
998 self
.image
.remove_snap('snap1')
1000 def test_rollback_with_resize(self
):
1001 read
= self
.image
.read(0, 256)
1002 eq(read
, b
'\0' * 256)
1003 data
= rand_data(256)
1004 self
.image
.write(data
, 0)
1005 self
.image
.create_snap('snap1')
1006 read
= self
.image
.read(0, 256)
1008 new_size
= IMG_SIZE
* 2
1009 self
.image
.resize(new_size
)
1010 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
1011 self
.image
.write(data
, new_size
- 256)
1012 self
.image
.create_snap('snap2')
1013 read
= self
.image
.read(new_size
- 256, 256)
1015 self
.image
.rollback_to_snap('snap1')
1016 check_stat(self
.image
.stat(), IMG_SIZE
, IMG_ORDER
)
1017 assert_raises(InvalidArgument
, self
.image
.read
, new_size
- 256, 256)
1018 self
.image
.rollback_to_snap('snap2')
1019 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
1020 read
= self
.image
.read(new_size
- 256, 256)
1022 self
.image
.remove_snap('snap1')
1023 self
.image
.remove_snap('snap2')
1025 def test_set_snap(self
):
1026 self
.image
.write(b
'\0' * 256, 0)
1027 self
.image
.create_snap('snap1')
1028 read
= self
.image
.read(0, 256)
1029 eq(read
, b
'\0' * 256)
1030 data
= rand_data(256)
1031 self
.image
.write(data
, 0)
1032 read
= self
.image
.read(0, 256)
1034 self
.image
.set_snap('snap1')
1035 read
= self
.image
.read(0, 256)
1036 eq(read
, b
'\0' * 256)
1037 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
1038 self
.image
.remove_snap('snap1')
1040 def test_set_no_snap(self
):
1041 self
.image
.write(b
'\0' * 256, 0)
1042 self
.image
.create_snap('snap1')
1043 read
= self
.image
.read(0, 256)
1044 eq(read
, b
'\0' * 256)
1045 data
= rand_data(256)
1046 self
.image
.write(data
, 0)
1047 read
= self
.image
.read(0, 256)
1049 self
.image
.set_snap('snap1')
1050 read
= self
.image
.read(0, 256)
1051 eq(read
, b
'\0' * 256)
1052 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
1053 self
.image
.set_snap(None)
1054 read
= self
.image
.read(0, 256)
1056 self
.image
.remove_snap('snap1')
1058 def test_set_snap_by_id(self
):
1059 self
.image
.write(b
'\0' * 256, 0)
1060 self
.image
.create_snap('snap1')
1061 read
= self
.image
.read(0, 256)
1062 eq(read
, b
'\0' * 256)
1063 data
= rand_data(256)
1064 self
.image
.write(data
, 0)
1065 read
= self
.image
.read(0, 256)
1067 snaps
= list(self
.image
.list_snaps())
1068 self
.image
.set_snap_by_id(snaps
[0]['id'])
1069 read
= self
.image
.read(0, 256)
1070 eq(read
, b
'\0' * 256)
1071 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
1072 self
.image
.set_snap_by_id(None)
1073 read
= self
.image
.read(0, 256)
1075 self
.image
.remove_snap('snap1')
1077 def test_snap_get_name(self
):
1078 eq([], list(self
.image
.list_snaps()))
1079 self
.image
.create_snap('snap1')
1080 self
.image
.create_snap('snap2')
1081 self
.image
.create_snap('snap3')
1083 for snap
in self
.image
.list_snaps():
1084 expected_snap_name
= self
.image
.snap_get_name(snap
['id'])
1085 eq(expected_snap_name
, snap
['name'])
1086 self
.image
.remove_snap('snap1')
1087 self
.image
.remove_snap('snap2')
1088 self
.image
.remove_snap('snap3')
1089 eq([], list(self
.image
.list_snaps()))
1091 assert_raises(ImageNotFound
, self
.image
.snap_get_name
, 1)
1093 def test_snap_get_id(self
):
1094 eq([], list(self
.image
.list_snaps()))
1095 self
.image
.create_snap('snap1')
1096 self
.image
.create_snap('snap2')
1097 self
.image
.create_snap('snap3')
1099 for snap
in self
.image
.list_snaps():
1100 expected_snap_id
= self
.image
.snap_get_id(snap
['name'])
1101 eq(expected_snap_id
, snap
['id'])
1102 self
.image
.remove_snap('snap1')
1103 self
.image
.remove_snap('snap2')
1104 self
.image
.remove_snap('snap3')
1105 eq([], list(self
.image
.list_snaps()))
1107 assert_raises(ImageNotFound
, self
.image
.snap_get_id
, 'snap1')
1109 def test_set_snap_sparse(self
):
1110 self
.image
.create_snap('snap1')
1111 read
= self
.image
.read(0, 256)
1112 eq(read
, b
'\0' * 256)
1113 data
= rand_data(256)
1114 self
.image
.write(data
, 0)
1115 read
= self
.image
.read(0, 256)
1117 self
.image
.set_snap('snap1')
1118 read
= self
.image
.read(0, 256)
1119 eq(read
, b
'\0' * 256)
1120 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
1121 self
.image
.remove_snap('snap1')
1123 def test_many_snaps(self
):
1125 for i
in range(num_snaps
):
1126 self
.image
.create_snap(str(i
))
1127 snaps
= sorted(self
.image
.list_snaps(),
1128 key
=lambda snap
: int(snap
['name']))
1129 eq(len(snaps
), num_snaps
)
1130 for i
, snap
in enumerate(snaps
):
1131 eq(snap
['size'], IMG_SIZE
)
1132 eq(snap
['name'], str(i
))
1133 for i
in range(num_snaps
):
1134 self
.image
.remove_snap(str(i
))
1136 def test_set_snap_deleted(self
):
1137 self
.image
.write(b
'\0' * 256, 0)
1138 self
.image
.create_snap('snap1')
1139 read
= self
.image
.read(0, 256)
1140 eq(read
, b
'\0' * 256)
1141 data
= rand_data(256)
1142 self
.image
.write(data
, 0)
1143 read
= self
.image
.read(0, 256)
1145 self
.image
.set_snap('snap1')
1146 self
.image
.remove_snap('snap1')
1147 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
1148 self
.image
.set_snap(None)
1149 read
= self
.image
.read(0, 256)
1152 def test_set_snap_recreated(self
):
1153 self
.image
.write(b
'\0' * 256, 0)
1154 self
.image
.create_snap('snap1')
1155 read
= self
.image
.read(0, 256)
1156 eq(read
, b
'\0' * 256)
1157 data
= rand_data(256)
1158 self
.image
.write(data
, 0)
1159 read
= self
.image
.read(0, 256)
1161 self
.image
.set_snap('snap1')
1162 self
.image
.remove_snap('snap1')
1163 self
.image
.create_snap('snap1')
1164 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
1165 self
.image
.set_snap(None)
1166 read
= self
.image
.read(0, 256)
1168 self
.image
.remove_snap('snap1')
1170 def test_lock_unlock(self
):
1171 assert_raises(ImageNotFound
, self
.image
.unlock
, '')
1172 self
.image
.lock_exclusive('')
1173 assert_raises(ImageExists
, self
.image
.lock_exclusive
, '')
1174 assert_raises(ImageBusy
, self
.image
.lock_exclusive
, 'test')
1175 assert_raises(ImageExists
, self
.image
.lock_shared
, '', '')
1176 assert_raises(ImageBusy
, self
.image
.lock_shared
, 'foo', '')
1177 self
.image
.unlock('')
1179 def test_list_lockers(self
):
1180 eq([], self
.image
.list_lockers())
1181 self
.image
.lock_exclusive('test')
1182 lockers
= self
.image
.list_lockers()
1183 eq(1, len(lockers
['lockers']))
1184 _
, cookie
, _
= lockers
['lockers'][0]
1186 eq('', lockers
['tag'])
1187 assert lockers
['exclusive']
1188 self
.image
.unlock('test')
1189 eq([], self
.image
.list_lockers())
1192 for i
in range(num_shared
):
1193 self
.image
.lock_shared(str(i
), 'tag')
1194 lockers
= self
.image
.list_lockers()
1195 eq('tag', lockers
['tag'])
1196 assert not lockers
['exclusive']
1197 eq(num_shared
, len(lockers
['lockers']))
1198 cookies
= sorted(map(lambda x
: x
[1], lockers
['lockers']))
1199 for i
in range(num_shared
):
1200 eq(str(i
), cookies
[i
])
1201 self
.image
.unlock(str(i
))
1202 eq([], self
.image
.list_lockers())
1204 def test_diff_iterate(self
):
1205 check_diff(self
.image
, 0, IMG_SIZE
, None, [])
1206 self
.image
.write(b
'a' * 256, 0)
1207 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 256, True)])
1208 self
.image
.write(b
'b' * 256, 256)
1209 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
1210 self
.image
.discard(128, 256)
1211 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
1213 self
.image
.create_snap('snap1')
1214 self
.image
.discard(0, 1 << IMG_ORDER
)
1215 self
.image
.create_snap('snap2')
1216 self
.image
.set_snap('snap2')
1217 check_diff(self
.image
, 0, IMG_SIZE
, 'snap1', [(0, 512, False)])
1218 self
.image
.remove_snap('snap1')
1219 self
.image
.remove_snap('snap2')
1221 def test_aio_read(self
):
1222 # this is a list so that the local cb() can modify it
1227 # test1: success case
1228 comp
= self
.image
.aio_read(0, 20, cb
)
1229 comp
.wait_for_complete_and_cb()
1230 eq(retval
[0], b
'\0' * 20)
1231 eq(comp
.get_return_value(), 20)
1232 eq(sys
.getrefcount(comp
), 2)
1236 comp
= self
.image
.aio_read(IMG_SIZE
, 20, cb
)
1237 comp
.wait_for_complete_and_cb()
1239 assert(comp
.get_return_value() < 0)
1240 eq(sys
.getrefcount(comp
), 2)
1242 def test_aio_write(self
):
1245 retval
[0] = comp
.get_return_value()
1247 data
= rand_data(256)
1248 comp
= self
.image
.aio_write(data
, 256, cb
)
1249 comp
.wait_for_complete_and_cb()
1251 eq(comp
.get_return_value(), 0)
1252 eq(sys
.getrefcount(comp
), 2)
1253 eq(self
.image
.read(256, 256), data
)
1255 def test_aio_discard(self
):
1258 retval
[0] = comp
.get_return_value()
1260 data
= rand_data(256)
1261 self
.image
.write(data
, 0)
1262 comp
= self
.image
.aio_discard(0, 256, cb
)
1263 comp
.wait_for_complete_and_cb()
1265 eq(comp
.get_return_value(), 0)
1266 eq(sys
.getrefcount(comp
), 2)
1267 eq(self
.image
.read(256, 256), b
'\0' * 256)
1269 def test_aio_write_zeroes(self
):
1272 retval
[0] = comp
.get_return_value()
1274 data
= rand_data(256)
1275 self
.image
.write(data
, 0)
1276 comp
= self
.image
.aio_write_zeroes(0, 256, cb
)
1277 comp
.wait_for_complete_and_cb()
1279 eq(comp
.get_return_value(), 0)
1280 eq(sys
.getrefcount(comp
), 2)
1281 eq(self
.image
.read(256, 256), b
'\0' * 256)
1283 def test_aio_flush(self
):
1286 retval
[0] = comp
.get_return_value()
1288 comp
= self
.image
.aio_flush(cb
)
1289 comp
.wait_for_complete_and_cb()
1291 eq(sys
.getrefcount(comp
), 2)
1293 def test_metadata(self
):
1294 metadata
= list(self
.image
.metadata_list())
1295 eq(len(metadata
), 0)
1296 assert_raises(KeyError, self
.image
.metadata_get
, "key1")
1297 self
.image
.metadata_set("key1", "value1")
1298 self
.image
.metadata_set("key2", "value2")
1299 value
= self
.image
.metadata_get("key1")
1301 value
= self
.image
.metadata_get("key2")
1303 metadata
= list(self
.image
.metadata_list())
1304 eq(len(metadata
), 2)
1305 self
.image
.metadata_remove("key1")
1306 metadata
= list(self
.image
.metadata_list())
1307 eq(len(metadata
), 1)
1308 eq(metadata
[0], ("key2", "value2"))
1309 self
.image
.metadata_remove("key2")
1310 assert_raises(KeyError, self
.image
.metadata_remove
, "key2")
1311 metadata
= list(self
.image
.metadata_list())
1312 eq(len(metadata
), 0)
1316 self
.image
.metadata_set("key" + str(i
), "X" * 1025)
1317 metadata
= list(self
.image
.metadata_list())
1318 eq(len(metadata
), N
)
1320 self
.image
.metadata_remove("key" + str(i
))
1321 metadata
= list(self
.image
.metadata_list())
1322 eq(len(metadata
), N
- i
- 1)
1324 def test_watchers_list(self
):
1325 watchers
= list(self
.image
.watchers_list())
1326 # The image is open (in r/w mode) from setup, so expect there to be one
1328 eq(len(watchers
), 1)
1330 def test_config_list(self
):
1331 with
Image(ioctx
, image_name
) as image
:
1332 for option
in image
.config_list():
1333 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1335 image
.metadata_set("conf_rbd_cache", "true")
1337 for option
in image
.config_list():
1338 if option
['name'] == "rbd_cache":
1339 eq(option
['source'], RBD_CONFIG_SOURCE_IMAGE
)
1341 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1343 image
.metadata_remove("conf_rbd_cache")
1345 for option
in image
.config_list():
1346 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1348 def test_image_config_set_and_get_and_remove(self
):
1349 with
Image(ioctx
, image_name
) as image
:
1350 for option
in image
.config_list():
1351 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1353 image
.config_set("rbd_request_timed_out_seconds", "100")
1354 modify_value
= image
.config_get("rbd_request_timed_out_seconds")
1355 eq(modify_value
, '100')
1357 image
.config_remove("rbd_request_timed_out_seconds")
1359 for option
in image
.config_list():
1360 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1362 def test_sparsify(self
):
1363 assert_raises(InvalidArgument
, self
.image
.sparsify
, 16)
1364 self
.image
.sparsify(4096)
1367 @blocklist_features([RBD_FEATURE_JOURNALING
])
1368 def test_encryption_luks1(self
):
1369 data
= b
'hello world'
1373 with
Image(ioctx
, image_name
) as image
:
1374 image
.resize(image_size
)
1375 image
.write(data
, offset
)
1376 image
.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1
, "password")
1377 assert_not_equal(data
, image
.read(offset
, len(data
)))
1378 with
Image(ioctx
, image_name
) as image
:
1379 image
.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS1
, "password")
1380 assert_not_equal(data
, image
.read(offset
, len(data
)))
1381 image
.write(data
, offset
)
1382 with
Image(ioctx
, image_name
) as image
:
1383 image
.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS
, "password")
1384 eq(data
, image
.read(offset
, len(data
)))
1387 @blocklist_features([RBD_FEATURE_JOURNALING
])
1388 def test_encryption_luks2(self
):
1389 data
= b
'hello world'
1391 image_size
= 256<<20
1393 with
Image(ioctx
, image_name
) as image
:
1394 image
.resize(image_size
)
1395 image
.write(data
, offset
)
1396 image
.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2
, "password")
1397 assert_not_equal(data
, image
.read(offset
, len(data
)))
1398 with
Image(ioctx
, image_name
) as image
:
1399 image
.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS2
, "password")
1400 assert_not_equal(data
, image
.read(offset
, len(data
)))
1401 image
.write(data
, offset
)
1402 with
Image(ioctx
, image_name
) as image
:
1403 image
.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS
, "password")
1404 eq(data
, image
.read(offset
, len(data
)))
1407 class TestImageId(object):
1412 self
.image
= Image(ioctx
, image_name
)
1413 self
.image2
= Image(ioctx
, None, None, False, self
.image
.id())
1422 def test_read(self
):
1423 data
= self
.image2
.read(0, 20)
1424 eq(data
, b
'\0' * 20)
1426 def test_write(self
):
1427 data
= rand_data(256)
1428 self
.image2
.write(data
, 0)
1430 def test_resize(self
):
1431 new_size
= IMG_SIZE
* 2
1432 self
.image2
.resize(new_size
)
1433 info
= self
.image2
.stat()
1434 check_stat(info
, new_size
, IMG_ORDER
)
1436 def check_diff(image
, offset
, length
, from_snapshot
, expected
):
1438 def cb(offset
, length
, exists
):
1439 extents
.append((offset
, length
, exists
))
1440 image
.diff_iterate(0, IMG_SIZE
, None, cb
)
1441 eq(extents
, expected
)
1443 class TestClone(object):
1445 @require_features([RBD_FEATURE_LAYERING
])
1451 self
.image
= Image(ioctx
, image_name
)
1452 data
= rand_data(256)
1453 self
.image
.write(data
, IMG_SIZE
// 2)
1454 self
.image
.create_snap('snap1')
1456 self
.image
.protect_snap('snap1')
1457 self
.clone_name
= get_temp_image_name()
1458 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
1460 self
.clone
= Image(ioctx
, self
.clone_name
)
1465 self
.rbd
.remove(ioctx
, self
.clone_name
)
1466 self
.image
.unprotect_snap('snap1')
1467 self
.image
.remove_snap('snap1')
1471 def _test_with_params(self
, features
=None, order
=None, stripe_unit
=None,
1473 self
.image
.create_snap('snap2')
1474 self
.image
.protect_snap('snap2')
1475 clone_name2
= get_temp_image_name()
1476 if features
is None:
1477 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
)
1479 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1481 elif stripe_unit
is None:
1482 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1484 elif stripe_count
is None:
1485 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1486 features
, order
, stripe_unit
)
1488 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1489 features
, order
, stripe_unit
, stripe_count
)
1490 self
.rbd
.remove(ioctx
, clone_name2
)
1491 self
.image
.unprotect_snap('snap2')
1492 self
.image
.remove_snap('snap2')
1494 def test_with_params(self
):
1495 self
._test
_with
_params
()
1497 def test_with_params2(self
):
1499 self
._test
_with
_params
(features
, self
.image
.stat()['order'])
1501 @require_features([RBD_FEATURE_STRIPINGV2
])
1502 def test_with_params3(self
):
1504 self
._test
_with
_params
(features
, self
.image
.stat()['order'],
1505 self
.image
.stripe_unit(),
1506 self
.image
.stripe_count())
1508 def test_stripe_unit_and_count(self
):
1511 image_name
= get_temp_image_name()
1512 RBD().create(ioctx
, image_name
, IMG_SIZE
, IMG_ORDER
, old_format
=False,
1513 features
=int(features
), stripe_unit
=1048576, stripe_count
=8)
1514 image
= Image(ioctx
, image_name
)
1515 image
.create_snap('snap1')
1516 image
.protect_snap('snap1')
1517 clone_name
= get_temp_image_name()
1518 RBD().clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name
)
1519 clone
= Image(ioctx
, clone_name
)
1521 eq(1048576, clone
.stripe_unit())
1522 eq(8, clone
.stripe_count())
1525 RBD().remove(ioctx
, clone_name
)
1526 image
.unprotect_snap('snap1')
1527 image
.remove_snap('snap1')
1529 RBD().remove(ioctx
, image_name
)
1532 def test_unprotected(self
):
1533 self
.image
.create_snap('snap2')
1535 clone_name2
= get_temp_image_name()
1536 rados
.conf_set("rbd_default_clone_format", "1")
1537 assert_raises(InvalidArgument
, self
.rbd
.clone
, ioctx
, image_name
,
1538 'snap2', ioctx
, clone_name2
, features
)
1539 rados
.conf_set("rbd_default_clone_format", "auto")
1540 self
.image
.remove_snap('snap2')
1542 def test_unprotect_with_children(self
):
1544 # can't remove a snapshot that has dependent clones
1545 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
1547 # validate parent info of clone created by TestClone.setUp
1548 (pool
, image
, snap
) = self
.clone
.parent_info()
1550 eq(image
, image_name
)
1552 eq(self
.image
.id(), self
.clone
.parent_id())
1554 # create a new pool...
1555 pool_name2
= get_temp_pool_name()
1556 rados
.create_pool(pool_name2
)
1557 other_ioctx
= rados
.open_ioctx(pool_name2
)
1558 other_ioctx
.application_enable('rbd')
1560 # ...with a clone of the same parent
1561 other_clone_name
= get_temp_image_name()
1562 rados
.conf_set("rbd_default_clone_format", "1")
1563 self
.rbd
.clone(ioctx
, image_name
, 'snap1', other_ioctx
,
1564 other_clone_name
, features
)
1565 rados
.conf_set("rbd_default_clone_format", "auto")
1566 self
.other_clone
= Image(other_ioctx
, other_clone_name
)
1567 # validate its parent info
1568 (pool
, image
, snap
) = self
.other_clone
.parent_info()
1570 eq(image
, image_name
)
1572 eq(self
.image
.id(), self
.other_clone
.parent_id())
1574 # can't unprotect snap with children
1575 assert_raises(ImageBusy
, self
.image
.unprotect_snap
, 'snap1')
1577 # 2 children, check that cannot remove the parent snap
1578 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
1580 # close and remove other pool's clone
1581 self
.other_clone
.close()
1582 self
.rbd
.remove(other_ioctx
, other_clone_name
)
1584 # check that we cannot yet remove the parent snap
1585 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
1588 rados
.delete_pool(pool_name2
)
1590 # unprotect, remove parent snap happen in cleanup, and should succeed
1592 def test_stat(self
):
1593 image_info
= self
.image
.stat()
1594 clone_info
= self
.clone
.stat()
1595 eq(clone_info
['size'], image_info
['size'])
1596 eq(clone_info
['size'], self
.clone
.overlap())
1598 def test_resize_stat(self
):
1599 self
.clone
.resize(IMG_SIZE
// 2)
1600 image_info
= self
.image
.stat()
1601 clone_info
= self
.clone
.stat()
1602 eq(clone_info
['size'], IMG_SIZE
// 2)
1603 eq(image_info
['size'], IMG_SIZE
)
1604 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
1606 self
.clone
.resize(IMG_SIZE
* 2)
1607 image_info
= self
.image
.stat()
1608 clone_info
= self
.clone
.stat()
1609 eq(clone_info
['size'], IMG_SIZE
* 2)
1610 eq(image_info
['size'], IMG_SIZE
)
1611 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
1613 def test_resize_io(self
):
1614 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1615 self
.image
.resize(0)
1616 self
.clone
.resize(IMG_SIZE
// 2 + 128)
1617 child_data
= self
.clone
.read(IMG_SIZE
// 2, 128)
1618 eq(child_data
, parent_data
[:128])
1619 self
.clone
.resize(IMG_SIZE
)
1620 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1621 eq(child_data
, parent_data
[:128] + (b
'\0' * 128))
1622 self
.clone
.resize(IMG_SIZE
// 2 + 1)
1623 child_data
= self
.clone
.read(IMG_SIZE
// 2, 1)
1624 eq(child_data
, parent_data
[0:1])
1625 self
.clone
.resize(0)
1626 self
.clone
.resize(IMG_SIZE
)
1627 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1628 eq(child_data
, b
'\0' * 256)
1630 def test_read(self
):
1631 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1632 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1633 eq(child_data
, parent_data
)
1635 def test_write(self
):
1636 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1637 new_data
= rand_data(256)
1638 self
.clone
.write(new_data
, IMG_SIZE
// 2 + 256)
1639 child_data
= self
.clone
.read(IMG_SIZE
// 2 + 256, 256)
1640 eq(child_data
, new_data
)
1641 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1642 eq(child_data
, parent_data
)
1643 parent_data
= self
.image
.read(IMG_SIZE
// 2 + 256, 256)
1644 eq(parent_data
, b
'\0' * 256)
1646 def check_children(self
, expected
):
1647 actual
= self
.image
.list_children()
1648 # dedup for cache pools until
1649 # http://tracker.ceph.com/issues/8187 is fixed
1650 deduped
= set([(pool_name
, image
[1]) for image
in actual
])
1651 eq(deduped
, set(expected
))
1653 def check_children2(self
, expected
):
1654 actual
= [{k
:v
for k
,v
in x
.items() if k
in expected
[0]} \
1655 for x
in self
.image
.list_children2()]
1656 eq(actual
, expected
)
1658 def check_descendants(self
, expected
):
1659 eq(list(self
.image
.list_descendants()), expected
)
1661 def get_image_id(self
, ioctx
, name
):
1662 with
Image(ioctx
, name
) as image
:
1665 def test_list_children(self
):
1668 self
.image
.set_snap('snap1')
1669 self
.check_children([(pool_name
, self
.clone_name
)])
1670 self
.check_children2(
1671 [{'pool': pool_name
, 'pool_namespace': '',
1672 'image': self
.clone_name
, 'trash': False,
1673 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1674 self
.check_descendants(
1675 [{'pool': pool_name
, 'pool_namespace': '',
1676 'image': self
.clone_name
, 'trash': False,
1677 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1679 self
.rbd
.remove(ioctx
, self
.clone_name
)
1680 eq(self
.image
.list_children(), [])
1681 eq(list(self
.image
.list_children2()), [])
1682 eq(list(self
.image
.list_descendants()), [])
1684 clone_name
= get_temp_image_name() + '_'
1685 expected_children
= []
1686 expected_children2
= []
1688 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
,
1689 clone_name
+ str(i
), features
)
1690 expected_children
.append((pool_name
, clone_name
+ str(i
)))
1691 expected_children2
.append(
1692 {'pool': pool_name
, 'pool_namespace': '',
1693 'image': clone_name
+ str(i
), 'trash': False,
1694 'id': self
.get_image_id(ioctx
, clone_name
+ str(i
))})
1695 self
.check_children(expected_children
)
1696 self
.check_children2(expected_children2
)
1697 self
.check_descendants(expected_children2
)
1699 image6_id
= self
.get_image_id(ioctx
, clone_name
+ str(5))
1700 RBD().trash_move(ioctx
, clone_name
+ str(5), 0)
1701 expected_children
.remove((pool_name
, clone_name
+ str(5)))
1702 for item
in expected_children2
:
1703 for k
, v
in item
.items():
1705 item
["trash"] = True
1706 self
.check_children(expected_children
)
1707 self
.check_children2(expected_children2
)
1708 self
.check_descendants(expected_children2
)
1710 RBD().trash_restore(ioctx
, image6_id
, clone_name
+ str(5))
1711 expected_children
.append((pool_name
, clone_name
+ str(5)))
1712 for item
in expected_children2
:
1713 for k
, v
in item
.items():
1715 item
["trash"] = False
1716 self
.check_children(expected_children
)
1717 self
.check_children2(expected_children2
)
1718 self
.check_descendants(expected_children2
)
1721 self
.rbd
.remove(ioctx
, clone_name
+ str(i
))
1722 expected_children
.remove((pool_name
, clone_name
+ str(i
)))
1723 expected_children2
.pop(0)
1724 self
.check_children(expected_children
)
1725 self
.check_children2(expected_children2
)
1726 self
.check_descendants(expected_children2
)
1728 eq(self
.image
.list_children(), [])
1729 eq(list(self
.image
.list_children2()), [])
1730 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
1732 self
.check_children([(pool_name
, self
.clone_name
)])
1733 self
.check_children2(
1734 [{'pool': pool_name
, 'pool_namespace': '',
1735 'image': self
.clone_name
, 'trash': False,
1736 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1737 self
.check_descendants(
1738 [{'pool': pool_name
, 'pool_namespace': '',
1739 'image': self
.clone_name
, 'trash': False,
1740 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1741 self
.clone
= Image(ioctx
, self
.clone_name
)
1743 def test_flatten_errors(self
):
1744 # test that we can't flatten a non-clone
1745 assert_raises(InvalidArgument
, self
.image
.flatten
)
1747 # test that we can't flatten a snapshot
1748 self
.clone
.create_snap('snap2')
1749 self
.clone
.set_snap('snap2')
1750 assert_raises(ReadOnlyImage
, self
.clone
.flatten
)
1751 self
.clone
.remove_snap('snap2')
1753 def check_flatten_with_order(self
, new_order
, stripe_unit
=None,
1757 clone_name2
= get_temp_image_name()
1758 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1759 features
, new_order
, stripe_unit
, stripe_count
)
1760 #with Image(ioctx, 'clone2') as clone:
1761 clone2
= Image(ioctx
, clone_name2
)
1763 eq(clone2
.overlap(), 0)
1765 self
.rbd
.remove(ioctx
, clone_name2
)
1767 # flatten after resizing to non-block size
1768 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1769 features
, new_order
, stripe_unit
, stripe_count
)
1770 with
Image(ioctx
, clone_name2
) as clone
:
1771 clone
.resize(IMG_SIZE
// 2 - 1)
1773 eq(0, clone
.overlap())
1774 self
.rbd
.remove(ioctx
, clone_name2
)
1776 # flatten after resizing to non-block size
1777 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1778 features
, new_order
, stripe_unit
, stripe_count
)
1779 with
Image(ioctx
, clone_name2
) as clone
:
1780 clone
.resize(IMG_SIZE
// 2 + 1)
1782 eq(clone
.overlap(), 0)
1783 self
.rbd
.remove(ioctx
, clone_name2
)
1785 def test_flatten_basic(self
):
1786 self
.check_flatten_with_order(IMG_ORDER
)
1788 def test_flatten_smaller_order(self
):
1789 self
.check_flatten_with_order(IMG_ORDER
- 2, 1048576, 1)
1791 def test_flatten_larger_order(self
):
1792 self
.check_flatten_with_order(IMG_ORDER
+ 2)
1794 def test_flatten_drops_cache(self
):
1797 clone_name2
= get_temp_image_name()
1798 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1799 features
, IMG_ORDER
)
1800 with
Image(ioctx
, clone_name2
) as clone
:
1801 with
Image(ioctx
, clone_name2
) as clone2
:
1802 # cache object non-existence
1803 data
= clone
.read(IMG_SIZE
// 2, 256)
1804 clone2_data
= clone2
.read(IMG_SIZE
// 2, 256)
1805 eq(data
, clone2_data
)
1807 assert_raises(ImageNotFound
, clone
.parent_info
)
1808 assert_raises(ImageNotFound
, clone2
.parent_info
)
1809 assert_raises(ImageNotFound
, clone
.parent_id
)
1810 assert_raises(ImageNotFound
, clone2
.parent_id
)
1811 after_flatten
= clone
.read(IMG_SIZE
// 2, 256)
1812 eq(data
, after_flatten
)
1813 after_flatten
= clone2
.read(IMG_SIZE
// 2, 256)
1814 eq(data
, after_flatten
)
1815 self
.rbd
.remove(ioctx
, clone_name2
)
1817 def test_flatten_multi_level(self
):
1818 self
.clone
.create_snap('snap2')
1819 self
.clone
.protect_snap('snap2')
1820 clone_name3
= get_temp_image_name()
1821 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1823 self
.clone
.flatten()
1824 with
Image(ioctx
, clone_name3
) as clone3
:
1826 self
.clone
.unprotect_snap('snap2')
1827 self
.clone
.remove_snap('snap2')
1828 self
.rbd
.remove(ioctx
, clone_name3
)
1830 def test_flatten_with_progress(self
):
1831 d
= {'received_callback': False}
1832 def progress_cb(current
, total
):
1833 d
['received_callback'] = True
1838 clone_name
= get_temp_image_name()
1839 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name
,
1841 with
Image(ioctx
, clone_name
) as clone
:
1842 clone
.flatten(on_progress
=progress_cb
)
1843 self
.rbd
.remove(ioctx
, clone_name
)
1844 eq(True, d
['received_callback'])
1846 def test_resize_flatten_multi_level(self
):
1847 self
.clone
.create_snap('snap2')
1848 self
.clone
.protect_snap('snap2')
1849 clone_name3
= get_temp_image_name()
1850 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1852 self
.clone
.resize(1)
1853 orig_data
= self
.image
.read(0, 256)
1854 with
Image(ioctx
, clone_name3
) as clone3
:
1855 clone3_data
= clone3
.read(0, 256)
1856 eq(orig_data
, clone3_data
)
1857 self
.clone
.flatten()
1858 with
Image(ioctx
, clone_name3
) as clone3
:
1859 clone3_data
= clone3
.read(0, 256)
1860 eq(orig_data
, clone3_data
)
1861 self
.rbd
.remove(ioctx
, clone_name3
)
1862 self
.clone
.unprotect_snap('snap2')
1863 self
.clone
.remove_snap('snap2')
1865 def test_trash_snapshot(self
):
1866 self
.image
.create_snap('snap2')
1868 clone_name
= get_temp_image_name()
1869 rados
.conf_set("rbd_default_clone_format", "2")
1870 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name
, features
)
1871 rados
.conf_set("rbd_default_clone_format", "auto")
1873 self
.image
.remove_snap('snap2')
1875 snaps
= [s
for s
in self
.image
.list_snaps() if s
['name'] != 'snap1']
1876 eq([RBD_SNAP_NAMESPACE_TYPE_TRASH
], [s
['namespace'] for s
in snaps
])
1877 eq([{'original_name' : 'snap2'}], [s
['trash'] for s
in snaps
])
1879 self
.rbd
.remove(ioctx
, clone_name
)
1880 eq([], [s
for s
in self
.image
.list_snaps() if s
['name'] != 'snap1'])
1883 @blocklist_features([RBD_FEATURE_JOURNALING
])
1884 def test_encryption_luks1(self
):
1885 data
= b
'hello world'
1889 self
.clone
.resize(image_size
)
1890 self
.clone
.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1
, "password")
1891 self
.clone
.encryption_load2(
1892 ((RBD_ENCRYPTION_FORMAT_LUKS1
, "password"),))
1893 self
.clone
.write(data
, offset
)
1894 eq(self
.clone
.read(0, 16), self
.image
.read(0, 16))
1897 @blocklist_features([RBD_FEATURE_JOURNALING
])
1898 def test_encryption_luks2(self
):
1899 data
= b
'hello world'
1903 self
.clone
.resize(image_size
)
1904 self
.clone
.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2
, "password")
1905 self
.clone
.encryption_load2(
1906 ((RBD_ENCRYPTION_FORMAT_LUKS2
, "password"),))
1907 self
.clone
.write(data
, offset
)
1908 eq(self
.clone
.read(0, 16), self
.image
.read(0, 16))
1910 class TestExclusiveLock(object):
1912 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
1915 rados2
= Rados(conffile
='')
1918 ioctx2
= rados2
.open_ioctx(pool_name
)
1928 def test_ownership(self
):
1929 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1930 image1
.write(b
'0'*256, 0)
1931 eq(image1
.is_exclusive_lock_owner(), True)
1932 eq(image2
.is_exclusive_lock_owner(), False)
1934 def test_snapshot_leadership(self
):
1935 with
Image(ioctx
, image_name
) as image
:
1936 image
.create_snap('snap')
1937 eq(image
.is_exclusive_lock_owner(), True)
1939 with
Image(ioctx
, image_name
) as image
:
1940 image
.write(b
'0'*256, 0)
1941 eq(image
.is_exclusive_lock_owner(), True)
1942 image
.set_snap('snap')
1943 eq(image
.is_exclusive_lock_owner(), False)
1944 with
Image(ioctx
, image_name
, snapshot
='snap') as image
:
1945 eq(image
.is_exclusive_lock_owner(), False)
1947 with
Image(ioctx
, image_name
) as image
:
1948 image
.remove_snap('snap')
1950 def test_read_only_leadership(self
):
1951 with
Image(ioctx
, image_name
, read_only
=True) as image
:
1952 eq(image
.is_exclusive_lock_owner(), False)
1954 def test_follower_flatten(self
):
1955 with
Image(ioctx
, image_name
) as image
:
1956 image
.create_snap('snap')
1957 image
.protect_snap('snap')
1959 RBD().clone(ioctx
, image_name
, 'snap', ioctx
, 'clone', features
)
1960 with
Image(ioctx
, 'clone') as image1
, Image(ioctx2
, 'clone') as image2
:
1961 data
= rand_data(256)
1962 image1
.write(data
, 0)
1964 assert_raises(ImageNotFound
, image1
.parent_info
)
1965 assert_raises(ImageNotFound
, image1
.parent_id
)
1969 image2
.parent_info()
1970 except ImageNotFound
:
1975 RBD().remove(ioctx
, 'clone')
1976 with
Image(ioctx
, image_name
) as image
:
1977 image
.unprotect_snap('snap')
1978 image
.remove_snap('snap')
1980 def test_follower_resize(self
):
1981 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1982 image1
.write(b
'0'*256, 0)
1983 for new_size
in [IMG_SIZE
* 2, IMG_SIZE
// 2]:
1984 image2
.resize(new_size
);
1985 eq(new_size
, image1
.size())
1987 if new_size
== image2
.size():
1990 eq(new_size
, image2
.size())
1992 def test_follower_snap_create(self
):
1993 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1994 image2
.create_snap('snap1')
1995 image1
.remove_snap('snap1')
1997 def test_follower_snap_rollback(self
):
1998 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1999 image1
.create_snap('snap')
2001 assert_raises(ReadOnlyImage
, image2
.rollback_to_snap
, 'snap')
2002 image1
.rollback_to_snap('snap')
2004 image1
.remove_snap('snap')
2006 def test_follower_discard(self
):
2008 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
2009 data
= rand_data(256)
2010 image1
.write(data
, 0)
2011 image2
.discard(0, 256)
2012 eq(image1
.is_exclusive_lock_owner(), False)
2013 eq(image2
.is_exclusive_lock_owner(), True)
2014 read
= image2
.read(0, 256)
2015 if rados
.conf_get('rbd_skip_partial_discard') == 'false':
2016 eq(256 * b
'\0', read
)
2020 def test_follower_write(self
):
2021 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
2022 data
= rand_data(256)
2023 image1
.write(data
, 0)
2024 image2
.write(data
, IMG_SIZE
// 2)
2025 eq(image1
.is_exclusive_lock_owner(), False)
2026 eq(image2
.is_exclusive_lock_owner(), True)
2027 for offset
in [0, IMG_SIZE
// 2]:
2028 read
= image2
.read(offset
, 256)
2030 def test_acquire_release_lock(self
):
2031 with
Image(ioctx
, image_name
) as image
:
2032 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
2033 image
.lock_release()
2035 @attr('SKIP_IF_CRIMSON')
2036 def test_break_lock(self
):
2037 blocklist_rados
= Rados(conffile
='')
2038 blocklist_rados
.connect()
2040 blocklist_ioctx
= blocklist_rados
.open_ioctx(pool_name
)
2042 rados2
.conf_set('rbd_blocklist_on_break_lock', 'true')
2043 with
Image(ioctx2
, image_name
) as image
, \
2044 Image(blocklist_ioctx
, image_name
) as blocklist_image
:
2046 lock_owners
= list(image
.lock_get_owners())
2047 eq(0, len(lock_owners
))
2049 blocklist_image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
2050 assert_raises(ReadOnlyImage
, image
.lock_acquire
,
2051 RBD_LOCK_MODE_EXCLUSIVE
)
2052 lock_owners
= list(image
.lock_get_owners())
2053 eq(1, len(lock_owners
))
2054 eq(RBD_LOCK_MODE_EXCLUSIVE
, lock_owners
[0]['mode'])
2055 image
.lock_break(RBD_LOCK_MODE_EXCLUSIVE
,
2056 lock_owners
[0]['owner'])
2058 assert_raises(ConnectionShutdown
,
2059 blocklist_image
.is_exclusive_lock_owner
)
2061 blocklist_rados
.wait_for_latest_osdmap()
2062 data
= rand_data(256)
2063 assert_raises(ConnectionShutdown
,
2064 blocklist_image
.write
, data
, 0)
2066 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
2069 blocklist_image
.close()
2070 except ConnectionShutdown
:
2073 blocklist_ioctx
.close()
2075 blocklist_rados
.shutdown()
2077 class TestMirroring(object):
2080 def check_info(info
, global_id
, state
, primary
=None):
2081 eq(global_id
, info
['global_id'])
2082 eq(state
, info
['state'])
2083 if primary
is not None:
2084 eq(primary
, info
['primary'])
2088 self
.initial_mirror_mode
= self
.rbd
.mirror_mode_get(ioctx
)
2089 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
2091 self
.image
= Image(ioctx
, image_name
)
2096 self
.rbd
.mirror_mode_set(ioctx
, self
.initial_mirror_mode
)
2098 def test_uuid(self
):
2099 mirror_uuid
= self
.rbd
.mirror_uuid_get(ioctx
)
2102 def test_site_name(self
):
2103 site_name
= "us-west-1"
2104 self
.rbd
.mirror_site_name_set(rados
, site_name
)
2105 eq(site_name
, self
.rbd
.mirror_site_name_get(rados
))
2106 self
.rbd
.mirror_site_name_set(rados
, "")
2107 eq(rados
.get_fsid(), self
.rbd
.mirror_site_name_get(rados
))
2109 def test_mirror_peer_bootstrap(self
):
2110 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
2112 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_DISABLED
)
2113 assert_raises(InvalidArgument
, self
.rbd
.mirror_peer_bootstrap_create
,
2116 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
2117 token_b64
= self
.rbd
.mirror_peer_bootstrap_create(ioctx
)
2118 token
= base64
.b64decode(token_b64
)
2119 token_dict
= json
.loads(token
)
2120 eq(sorted(['fsid', 'client_id', 'key', 'mon_host']),
2121 sorted(list(token_dict
.keys())))
2123 # requires different cluster
2124 assert_raises(InvalidArgument
, self
.rbd
.mirror_peer_bootstrap_import
,
2125 ioctx
, RBD_MIRROR_PEER_DIRECTION_RX
, token_b64
)
2127 def test_mirror_peer(self
):
2128 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
2129 site_name
= "test_site"
2130 client_name
= "test_client"
2131 uuid
= self
.rbd
.mirror_peer_add(ioctx
, site_name
, client_name
,
2132 direction
=RBD_MIRROR_PEER_DIRECTION_RX_TX
)
2136 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX
,
2137 'site_name' : site_name
,
2138 'cluster_name' : site_name
,
2140 'client_name' : client_name
,
2142 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
2143 cluster_name
= "test_cluster1"
2144 self
.rbd
.mirror_peer_set_cluster(ioctx
, uuid
, cluster_name
)
2145 client_name
= "test_client1"
2146 self
.rbd
.mirror_peer_set_client(ioctx
, uuid
, client_name
)
2149 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX
,
2150 'site_name' : cluster_name
,
2151 'cluster_name' : cluster_name
,
2153 'client_name' : client_name
,
2155 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
2158 RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST
: 'host1',
2159 RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY
: 'abc'
2161 self
.rbd
.mirror_peer_set_attributes(ioctx
, uuid
, attribs
)
2162 eq(attribs
, self
.rbd
.mirror_peer_get_attributes(ioctx
, uuid
))
2164 self
.rbd
.mirror_peer_remove(ioctx
, uuid
)
2165 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
2167 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
2168 RBD_FEATURE_JOURNALING
])
2169 def test_mirror_image(self
):
2171 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
2172 self
.image
.mirror_image_disable(True)
2173 info
= self
.image
.mirror_image_get_info()
2174 self
.check_info(info
, '', RBD_MIRROR_IMAGE_DISABLED
, False)
2176 self
.image
.mirror_image_enable()
2177 info
= self
.image
.mirror_image_get_info()
2178 global_id
= info
['global_id']
2179 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2181 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
2184 self
.image
.mirror_image_disable(True)
2185 except InvalidArgument
:
2187 eq(True, fail
) # Fails because of mirror mode pool
2189 self
.image
.mirror_image_demote()
2190 info
= self
.image
.mirror_image_get_info()
2191 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, False)
2193 entries
= dict(self
.rbd
.mirror_image_info_list(ioctx
))
2194 info
['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL
;
2195 eq(info
, entries
[self
.image
.id()])
2197 self
.image
.mirror_image_resync()
2199 self
.image
.mirror_image_promote(True)
2200 info
= self
.image
.mirror_image_get_info()
2201 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2203 entries
= dict(self
.rbd
.mirror_image_info_list(ioctx
))
2204 info
['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL
;
2205 eq(info
, entries
[self
.image
.id()])
2209 self
.image
.mirror_image_resync()
2210 except InvalidArgument
:
2212 eq(True, fail
) # Fails because it is primary
2214 status
= self
.image
.mirror_image_get_status()
2215 eq(image_name
, status
['name'])
2216 eq(False, status
['up'])
2217 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
2218 info
= status
['info']
2219 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2221 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
2222 RBD_FEATURE_JOURNALING
])
2223 def test_mirror_image_status(self
):
2224 info
= self
.image
.mirror_image_get_info()
2225 global_id
= info
['global_id']
2226 state
= info
['state']
2227 primary
= info
['primary']
2229 status
= self
.image
.mirror_image_get_status()
2230 eq(image_name
, status
['name'])
2231 eq(False, status
['up'])
2232 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
2233 eq([], status
['remote_statuses'])
2234 info
= status
['info']
2235 self
.check_info(info
, global_id
, state
, primary
)
2237 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
2240 eq(image_name
, status
['name'])
2241 eq(False, status
['up'])
2242 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
2243 info
= status
['info']
2244 self
.check_info(info
, global_id
, state
)
2246 states
= self
.rbd
.mirror_image_status_summary(ioctx
)
2247 eq([(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, 1)], states
)
2249 assert_raises(ImageNotFound
, self
.image
.mirror_image_get_instance_id
)
2250 instance_ids
= list(self
.rbd
.mirror_image_instance_id_list(ioctx
))
2251 eq(0, len(instance_ids
))
2255 self
.rbd
.create(ioctx
, image_name
+ str(i
), IMG_SIZE
, IMG_ORDER
,
2256 old_format
=False, features
=int(features
))
2257 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
2258 eq(N
+ 1, len(images
))
2260 self
.rbd
.remove(ioctx
, image_name
+ str(i
))
2262 def test_mirror_image_create_snapshot(self
):
2263 assert_raises(InvalidArgument
, self
.image
.mirror_image_create_snapshot
)
2265 peer1_uuid
= self
.rbd
.mirror_peer_add(ioctx
, "cluster1", "client")
2266 peer2_uuid
= self
.rbd
.mirror_peer_add(ioctx
, "cluster2", "client")
2267 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
2268 self
.image
.mirror_image_disable(False)
2269 self
.image
.mirror_image_enable(RBD_MIRROR_IMAGE_MODE_SNAPSHOT
)
2270 mode
= self
.image
.mirror_image_get_mode()
2271 eq(RBD_MIRROR_IMAGE_MODE_SNAPSHOT
, mode
)
2273 snaps
= list(self
.image
.list_snaps())
2276 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2277 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2279 info
= self
.image
.mirror_image_get_info()
2280 eq(True, info
['primary'])
2282 self
.rbd
.mirror_image_info_list(ioctx
,
2283 RBD_MIRROR_IMAGE_MODE_SNAPSHOT
))
2284 info
['mode'] = RBD_MIRROR_IMAGE_MODE_SNAPSHOT
;
2285 eq(info
, entries
[self
.image
.id()])
2287 snap_id
= self
.image
.mirror_image_create_snapshot(
2288 RBD_SNAP_CREATE_SKIP_QUIESCE
)
2290 snaps
= list(self
.image
.list_snaps())
2293 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2294 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2296 eq(snap
['id'], snap_id
)
2297 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2298 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2299 eq(sorted([peer1_uuid
, peer2_uuid
]),
2300 sorted(snap
['mirror']['mirror_peer_uuids']))
2302 eq(RBD_SNAP_NAMESPACE_TYPE_MIRROR
,
2303 self
.image
.snap_get_namespace_type(snap_id
))
2304 mirror_snap
= self
.image
.snap_get_mirror_namespace(snap_id
)
2305 eq(mirror_snap
, snap
['mirror'])
2307 self
.image
.mirror_image_demote()
2309 assert_raises(InvalidArgument
, self
.image
.mirror_image_create_snapshot
)
2311 snaps
= list(self
.image
.list_snaps())
2314 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2316 eq(snap
['id'], snap_id
)
2317 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2319 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2320 eq(RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED
, snap
['mirror']['state'])
2321 eq(sorted([peer1_uuid
, peer2_uuid
]),
2322 sorted(snap
['mirror']['mirror_peer_uuids']))
2324 self
.rbd
.mirror_peer_remove(ioctx
, peer1_uuid
)
2325 self
.rbd
.mirror_peer_remove(ioctx
, peer2_uuid
)
2326 self
.image
.mirror_image_promote(False)
2328 def test_aio_mirror_image_create_snapshot(self
):
2329 peer_uuid
= self
.rbd
.mirror_peer_add(ioctx
, "cluster", "client")
2330 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
2331 self
.image
.mirror_image_disable(False)
2332 self
.image
.mirror_image_enable(RBD_MIRROR_IMAGE_MODE_SNAPSHOT
)
2334 snaps
= list(self
.image
.list_snaps())
2337 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2338 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2340 # this is a list so that the local cb() can modify it
2345 comp
= self
.image
.aio_mirror_image_get_info(cb
)
2346 comp
.wait_for_complete_and_cb()
2347 assert_not_equal(info
[0], None)
2348 eq(comp
.get_return_value(), 0)
2349 eq(sys
.getrefcount(comp
), 2)
2351 global_id
= info
['global_id']
2352 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2358 comp
= self
.image
.aio_mirror_image_get_mode(cb
)
2359 comp
.wait_for_complete_and_cb()
2360 eq(comp
.get_return_value(), 0)
2361 eq(sys
.getrefcount(comp
), 2)
2362 eq(mode
[0], RBD_MIRROR_IMAGE_MODE_SNAPSHOT
)
2365 def cb(_
, _snap_id
):
2366 snap_id
[0] = _snap_id
2368 comp
= self
.image
.aio_mirror_image_create_snapshot(0, cb
)
2369 comp
.wait_for_complete_and_cb()
2370 assert_not_equal(snap_id
[0], None)
2371 eq(comp
.get_return_value(), 0)
2372 eq(sys
.getrefcount(comp
), 2)
2374 snaps
= list(self
.image
.list_snaps())
2377 eq(snap
['id'], snap_id
[0])
2378 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2379 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2380 eq([peer_uuid
], snap
['mirror']['mirror_peer_uuids'])
2382 self
.rbd
.mirror_peer_remove(ioctx
, peer_uuid
)
2384 class TestTrash(object):
2388 rados2
= Rados(conffile
='')
2391 ioctx2
= rados2
.open_ioctx(pool_name
)
2399 def test_move(self
):
2401 with
Image(ioctx
, image_name
) as image
:
2402 image_id
= image
.id()
2404 RBD().trash_move(ioctx
, image_name
, 1000)
2405 RBD().trash_remove(ioctx
, image_id
, True)
2407 def test_purge(self
):
2409 with
Image(ioctx
, image_name
) as image
:
2410 image_name1
= image_name
2411 image_id1
= image
.id()
2414 with
Image(ioctx
, image_name
) as image
:
2415 image_name2
= image_name
2416 image_id2
= image
.id()
2418 RBD().trash_move(ioctx
, image_name1
, 0)
2419 RBD().trash_move(ioctx
, image_name2
, 1000)
2420 RBD().trash_purge(ioctx
, datetime
.now())
2422 entries
= list(RBD().trash_list(ioctx
))
2423 eq([image_id2
], [x
['id'] for x
in entries
])
2424 RBD().trash_remove(ioctx
, image_id2
, True)
2426 def test_remove_denied(self
):
2428 with
Image(ioctx
, image_name
) as image
:
2429 image_id
= image
.id()
2431 RBD().trash_move(ioctx
, image_name
, 1000)
2432 assert_raises(PermissionError
, RBD().trash_remove
, ioctx
, image_id
)
2433 RBD().trash_remove(ioctx
, image_id
, True)
2435 def test_remove(self
):
2437 with
Image(ioctx
, image_name
) as image
:
2438 image_id
= image
.id()
2440 RBD().trash_move(ioctx
, image_name
, 0)
2441 RBD().trash_remove(ioctx
, image_id
)
2443 def test_remove_with_progress(self
):
2444 d
= {'received_callback': False}
2445 def progress_cb(current
, total
):
2446 d
['received_callback'] = True
2450 with
Image(ioctx
, image_name
) as image
:
2451 image_id
= image
.id()
2453 RBD().trash_move(ioctx
, image_name
, 0)
2454 RBD().trash_remove(ioctx
, image_id
, on_progress
=progress_cb
)
2455 eq(True, d
['received_callback'])
2459 with
Image(ioctx
, image_name
) as image
:
2460 image_id
= image
.id()
2462 RBD().trash_move(ioctx
, image_name
, 1000)
2464 info
= RBD().trash_get(ioctx
, image_id
)
2465 eq(image_id
, info
['id'])
2466 eq(image_name
, info
['name'])
2467 eq('USER', info
['source'])
2468 assert(info
['deferment_end_time'] > info
['deletion_time'])
2470 RBD().trash_remove(ioctx
, image_id
, True)
2472 def test_list(self
):
2474 with
Image(ioctx
, image_name
) as image
:
2475 image_id1
= image
.id()
2476 image_name1
= image_name
2477 RBD().trash_move(ioctx
, image_name
, 1000)
2480 with
Image(ioctx
, image_name
) as image
:
2481 image_id2
= image
.id()
2482 image_name2
= image_name
2483 RBD().trash_move(ioctx
, image_name
, 1000)
2485 entries
= list(RBD().trash_list(ioctx
))
2487 if e
['id'] == image_id1
:
2488 eq(e
['name'], image_name1
)
2489 elif e
['id'] == image_id2
:
2490 eq(e
['name'], image_name2
)
2493 eq(e
['source'], 'USER')
2494 assert e
['deferment_end_time'] > e
['deletion_time']
2496 RBD().trash_remove(ioctx
, image_id1
, True)
2497 RBD().trash_remove(ioctx
, image_id2
, True)
2499 def test_restore(self
):
2501 with
Image(ioctx
, image_name
) as image
:
2502 image_id
= image
.id()
2503 RBD().trash_move(ioctx
, image_name
, 1000)
2504 RBD().trash_restore(ioctx
, image_id
, image_name
)
2507 def test_create_group():
2511 def test_rename_group():
2513 if group_name
is not None:
2515 eq(["new" + group_name
], RBD().group_list(ioctx
))
2516 RBD().group_remove(ioctx
, "new" + group_name
)
2520 def test_list_groups_empty():
2521 eq([], RBD().group_list(ioctx
))
2523 @with_setup(create_group
, remove_group
)
2524 def test_list_groups():
2525 eq([group_name
], RBD().group_list(ioctx
))
2527 @with_setup(create_group
)
2528 def test_list_groups_after_removed():
2530 eq([], RBD().group_list(ioctx
))
2532 class TestGroups(object):
2538 self
.image_names
= [image_name
]
2539 self
.image
= Image(ioctx
, image_name
)
2542 snap_name
= get_temp_snap_name()
2543 self
.group
= Group(ioctx
, group_name
)
2548 for name
in self
.image_names
:
2549 RBD().remove(ioctx
, name
)
2551 def test_group_image_add(self
):
2552 self
.group
.add_image(ioctx
, image_name
)
2554 def test_group_image_list_empty(self
):
2555 eq([], list(self
.group
.list_images()))
2557 def test_group_image_list(self
):
2558 eq([], list(self
.group
.list_images()))
2559 self
.group
.add_image(ioctx
, image_name
)
2560 eq([image_name
], [img
['name'] for img
in self
.group
.list_images()])
2562 def test_group_image_list_move_to_trash(self
):
2563 eq([], list(self
.group
.list_images()))
2564 with
Image(ioctx
, image_name
) as image
:
2565 image_id
= image
.id()
2566 self
.group
.add_image(ioctx
, image_name
)
2567 eq([image_name
], [img
['name'] for img
in self
.group
.list_images()])
2568 RBD().trash_move(ioctx
, image_name
, 0)
2569 eq([], list(self
.group
.list_images()))
2570 RBD().trash_restore(ioctx
, image_id
, image_name
)
2572 def test_group_image_many_images(self
):
2573 eq([], list(self
.group
.list_images()))
2574 self
.group
.add_image(ioctx
, image_name
)
2576 for x
in range(0, 20):
2578 self
.image_names
.append(image_name
)
2579 self
.group
.add_image(ioctx
, image_name
)
2581 self
.image_names
.sort()
2582 answer
= [img
['name'] for img
in self
.group
.list_images()]
2584 eq(self
.image_names
, answer
)
2586 def test_group_image_remove(self
):
2587 eq([], list(self
.group
.list_images()))
2588 self
.group
.add_image(ioctx
, image_name
)
2589 with
Image(ioctx
, image_name
) as image
:
2590 eq(RBD_OPERATION_FEATURE_GROUP
,
2591 image
.op_features() & RBD_OPERATION_FEATURE_GROUP
)
2592 group
= image
.group()
2593 eq(group_name
, group
['name'])
2595 eq([image_name
], [img
['name'] for img
in self
.group
.list_images()])
2596 self
.group
.remove_image(ioctx
, image_name
)
2597 eq([], list(self
.group
.list_images()))
2598 with
Image(ioctx
, image_name
) as image
:
2599 eq(0, image
.op_features() & RBD_OPERATION_FEATURE_GROUP
)
2601 def test_group_snap(self
):
2603 eq([], list(self
.group
.list_snaps()))
2604 self
.group
.create_snap(snap_name
)
2605 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2607 for snap
in self
.image
.list_snaps():
2608 eq(rbd
.RBD_SNAP_NAMESPACE_TYPE_GROUP
, snap
['namespace'])
2609 info
= snap
['group']
2610 eq(group_name
, info
['group_name'])
2611 eq(snap_name
, info
['group_snap_name'])
2613 self
.group
.remove_snap(snap_name
)
2614 eq([], list(self
.group
.list_snaps()))
2616 def test_group_snap_flags(self
):
2618 eq([], list(self
.group
.list_snaps()))
2620 self
.group
.create_snap(snap_name
, 0)
2621 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2622 self
.group
.remove_snap(snap_name
)
2624 self
.group
.create_snap(snap_name
, RBD_SNAP_CREATE_SKIP_QUIESCE
)
2625 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2626 self
.group
.remove_snap(snap_name
)
2628 self
.group
.create_snap(snap_name
, RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR
)
2629 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2630 self
.group
.remove_snap(snap_name
)
2632 assert_raises(InvalidArgument
, self
.group
.create_snap
, snap_name
,
2633 RBD_SNAP_CREATE_SKIP_QUIESCE |
2634 RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR
)
2635 eq([], list(self
.group
.list_snaps()))
2637 def test_group_snap_list_many(self
):
2639 eq([], list(self
.group
.list_snaps()))
2641 for x
in range(0, 20):
2642 snap_names
.append(snap_name
)
2643 self
.group
.create_snap(snap_name
)
2644 snap_name
= get_temp_snap_name()
2647 answer
= [snap
['name'] for snap
in self
.group
.list_snaps()]
2649 eq(snap_names
, answer
)
2651 def test_group_snap_namespace(self
):
2653 eq([], list(self
.group
.list_snaps()))
2654 self
.group
.add_image(ioctx
, image_name
)
2655 self
.group
.create_snap(snap_name
)
2656 eq(1, len([snap
['name'] for snap
in self
.image
.list_snaps()]))
2657 self
.group
.remove_image(ioctx
, image_name
)
2658 self
.group
.remove_snap(snap_name
)
2659 eq([], list(self
.group
.list_snaps()))
2661 def test_group_snap_rename(self
):
2663 new_snap_name
= "new" + snap_name
2665 eq([], list(self
.group
.list_snaps()))
2666 self
.group
.create_snap(snap_name
)
2667 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2668 self
.group
.rename_snap(snap_name
, new_snap_name
)
2669 eq([new_snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2670 self
.group
.remove_snap(new_snap_name
)
2671 eq([], list(self
.group
.list_snaps()))
2673 def test_group_snap_rollback(self
):
2674 eq([], list(self
.group
.list_images()))
2675 self
.group
.add_image(ioctx
, image_name
)
2676 with
Image(ioctx
, image_name
) as image
:
2677 image
.write(b
'\0' * 256, 0)
2678 read
= image
.read(0, 256)
2679 eq(read
, b
'\0' * 256)
2682 eq([], list(self
.group
.list_snaps()))
2683 self
.group
.create_snap(snap_name
)
2684 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2686 with
Image(ioctx
, image_name
) as image
:
2687 data
= rand_data(256)
2688 image
.write(data
, 0)
2689 read
= image
.read(0, 256)
2692 self
.group
.rollback_to_snap(snap_name
)
2693 with
Image(ioctx
, image_name
) as image
:
2694 read
= image
.read(0, 256)
2695 eq(read
, b
'\0' * 256)
2697 self
.group
.remove_image(ioctx
, image_name
)
2698 eq([], list(self
.group
.list_images()))
2699 self
.group
.remove_snap(snap_name
)
2700 eq([], list(self
.group
.list_snaps()))
2702 @with_setup(create_image
, remove_image
)
2705 image_name2
= get_temp_image_name()
2707 class TestMigration(object):
2709 def test_migration(self
):
2711 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2712 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2715 status
= RBD().migration_status(ioctx
, image_name
)
2716 eq(image_name
, status
['source_image_name'])
2717 eq(image_name
, status
['dest_image_name'])
2718 eq(RBD_IMAGE_MIGRATION_STATE_PREPARED
, status
['state'])
2720 with
Image(ioctx
, image_name
) as image
:
2721 source_spec
= image
.migration_source_spec()
2722 eq("native", source_spec
["type"])
2724 RBD().migration_execute(ioctx
, image_name
)
2725 RBD().migration_commit(ioctx
, image_name
)
2728 def test_migration_import(self
):
2730 with
Image(ioctx
, image_name
) as image
:
2731 image_id
= image
.id()
2732 image
.create_snap('snap')
2734 source_spec
= json
.dumps(
2736 'pool_id': ioctx
.get_pool_id(),
2737 'pool_namespace': '',
2738 'image_name': image_name
,
2739 'image_id': image_id
,
2740 'snap_name': 'snap'})
2741 dst_image_name
= get_temp_image_name()
2742 RBD().migration_prepare_import(source_spec
, ioctx
, dst_image_name
,
2743 features
=63, order
=23, stripe_unit
=1<<23,
2744 stripe_count
=1, data_pool
=None)
2746 status
= RBD().migration_status(ioctx
, dst_image_name
)
2747 eq('', status
['source_image_name'])
2748 eq(dst_image_name
, status
['dest_image_name'])
2749 eq(RBD_IMAGE_MIGRATION_STATE_PREPARED
, status
['state'])
2751 with
Image(ioctx
, dst_image_name
) as image
:
2752 source_spec
= image
.migration_source_spec()
2753 eq("native", source_spec
["type"])
2755 RBD().migration_execute(ioctx
, dst_image_name
)
2756 RBD().migration_commit(ioctx
, dst_image_name
)
2758 with
Image(ioctx
, image_name
) as image
:
2759 image
.remove_snap('snap')
2760 with
Image(ioctx
, dst_image_name
) as image
:
2761 image
.remove_snap('snap')
2763 RBD().remove(ioctx
, dst_image_name
)
2764 RBD().remove(ioctx
, image_name
)
2766 def test_migration_with_progress(self
):
2767 d
= {'received_callback': False}
2768 def progress_cb(current
, total
):
2769 d
['received_callback'] = True
2773 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2774 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2776 RBD().migration_execute(ioctx
, image_name
, on_progress
=progress_cb
)
2777 eq(True, d
['received_callback'])
2778 d
['received_callback'] = False
2780 RBD().migration_commit(ioctx
, image_name
, on_progress
=progress_cb
)
2781 eq(True, d
['received_callback'])
2784 def test_migrate_abort(self
):
2786 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2787 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2789 RBD().migration_abort(ioctx
, image_name
)
2792 def test_migrate_abort_with_progress(self
):
2793 d
= {'received_callback': False}
2794 def progress_cb(current
, total
):
2795 d
['received_callback'] = True
2799 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2800 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2802 RBD().migration_abort(ioctx
, image_name
, on_progress
=progress_cb
)
2803 eq(True, d
['received_callback'])