1 # vim: expandtab smarttab shiftwidth=4 softtabstop=4
14 from assertions
import (assert_equal
as eq
, assert_raises
, assert_not_equal
,
16 from datetime
import datetime
, timedelta
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
)
131 group_name
= get_temp_group_name()
132 RBD().group_create(ioctx
, group_name
)
135 if group_name
is not None:
136 RBD().group_remove(ioctx
, group_name
)
145 new_group_name
= "new" + group_name
146 RBD().group_rename(ioctx
, group_name
, new_group_name
)
148 def require_new_format():
150 def _require_new_format(*args
, **kwargs
):
153 pytest
.skip('requires new format')
154 return fn(*args
, **kwargs
)
155 return functools
.wraps(fn
)(_require_new_format
)
158 def require_features(required_features
):
160 def _require_features(*args
, **kwargs
):
163 pytest
.skip('requires new format')
164 for feature
in required_features
:
165 if feature
& features
!= feature
:
166 pytest
.skip('missing required feature')
167 return fn(*args
, **kwargs
)
168 return functools
.wraps(fn
)(_require_features
)
173 def _require_linux(*args
, **kwargs
):
174 if platform
.system() != "Linux":
175 pytest
.skip('requires linux')
176 return fn(*args
, **kwargs
)
177 return functools
.wraps(fn
)(_require_linux
)
180 def blocklist_features(blocklisted_features
):
182 def _blocklist_features(*args
, **kwargs
):
184 for feature
in blocklisted_features
:
185 if features
is not None and feature
& features
== feature
:
186 pytest
.skip('blocklisted feature enabled')
187 return fn(*args
, **kwargs
)
188 return functools
.wraps(fn
)(_blocklist_features
)
198 def check_default_params(format
, order
=None, features
=None, stripe_count
=None,
199 stripe_unit
=None, exception
=None):
203 for k
in ['rbd_default_format', 'rbd_default_order', 'rbd_default_features',
204 'rbd_default_stripe_count', 'rbd_default_stripe_unit']:
205 orig_vals
[k
] = rados
.conf_get(k
)
207 rados
.conf_set('rbd_default_format', str(format
))
208 if order
is not None:
209 rados
.conf_set('rbd_default_order', str(order
or 0))
210 if features
is not None:
211 rados
.conf_set('rbd_default_features', str(features
or 0))
212 if stripe_count
is not None:
213 rados
.conf_set('rbd_default_stripe_count', str(stripe_count
or 0))
214 if stripe_unit
is not None:
215 rados
.conf_set('rbd_default_stripe_unit', str(stripe_unit
or 0))
216 feature_data_pool
= 0
217 datapool
= rados
.conf_get('rbd_default_data_pool')
218 if not len(datapool
) == 0:
219 feature_data_pool
= 128
220 image_name
= get_temp_image_name()
221 if exception
is None:
222 RBD().create(ioctx
, image_name
, IMG_SIZE
, old_format
=(format
== 1))
224 with
Image(ioctx
, image_name
) as image
:
225 eq(format
== 1, image
.old_format())
227 expected_order
= int(rados
.conf_get('rbd_default_order'))
228 actual_order
= image
.stat()['order']
229 eq(expected_order
, actual_order
)
231 expected_features
= features
233 expected_features
= 0
234 elif expected_features
is None:
235 expected_features
= 61 | feature_data_pool
237 expected_features |
= feature_data_pool
238 eq(expected_features
, image
.features())
240 expected_stripe_count
= stripe_count
241 if not expected_stripe_count
or format
== 1 or \
242 features
& RBD_FEATURE_STRIPINGV2
== 0:
243 expected_stripe_count
= 1
244 eq(expected_stripe_count
, image
.stripe_count())
246 expected_stripe_unit
= stripe_unit
247 if not expected_stripe_unit
or format
== 1 or \
248 features
& RBD_FEATURE_STRIPINGV2
== 0:
249 expected_stripe_unit
= 1 << actual_order
250 eq(expected_stripe_unit
, image
.stripe_unit())
252 RBD().remove(ioctx
, image_name
)
254 assert_raises(exception
, RBD().create
, ioctx
, image_name
, IMG_SIZE
)
256 for k
, v
in orig_vals
.items():
259 def test_create_defaults():
260 # basic format 1 and 2
261 check_default_params(1)
262 check_default_params(2)
264 check_default_params(1, 0, exception
=ArgumentOutOfRange
)
265 check_default_params(2, 0, exception
=ArgumentOutOfRange
)
266 check_default_params(1, 11, exception
=ArgumentOutOfRange
)
267 check_default_params(2, 11, exception
=ArgumentOutOfRange
)
268 check_default_params(1, 65, exception
=ArgumentOutOfRange
)
269 check_default_params(2, 65, exception
=ArgumentOutOfRange
)
270 # striping and features are ignored for format 1
271 check_default_params(1, 20, 0, 1, 1)
272 check_default_params(1, 20, 3, 1, 1)
273 check_default_params(1, 20, 0, 0, 0)
274 # striping is ignored if stripingv2 is not set
275 check_default_params(2, 20, 0, 1, 1 << 20)
276 check_default_params(2, 20, RBD_FEATURE_LAYERING
, 1, 1 << 20)
277 check_default_params(2, 20, 0, 0, 0)
278 # striping with stripingv2 is fine
279 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 1, 1 << 16)
280 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 10, 1 << 20)
281 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 10, 1 << 16)
282 check_default_params(2, 20, 0, 0, 0)
283 # make sure invalid combinations of stripe unit and order are still invalid
284 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 10, 1 << 50, exception
=InvalidArgument
)
285 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 10, 100, exception
=InvalidArgument
)
286 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 0, 1, exception
=InvalidArgument
)
287 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 1, 0, exception
=InvalidArgument
)
288 # 0 stripe unit and count are still ignored
289 check_default_params(2, 22, 0, 0, 0)
291 def test_context_manager():
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 with
Image(ioctx
, image_name
) as image
:
297 data
= rand_data(256)
299 read
= image
.read(0, 256)
300 RBD().remove(ioctx
, image_name
)
303 def test_open_read_only():
304 with
Rados(conffile
='') as cluster
:
305 with cluster
.open_ioctx(pool_name
) as ioctx
:
306 image_name
= get_temp_image_name()
307 RBD().create(ioctx
, image_name
, IMG_SIZE
)
308 data
= rand_data(256)
309 with
Image(ioctx
, image_name
) as image
:
311 image
.create_snap('snap')
312 with
Image(ioctx
, image_name
, read_only
=True) as image
:
313 read
= image
.read(0, 256)
315 assert_raises(ReadOnlyImage
, image
.write
, data
, 0)
316 assert_raises(ReadOnlyImage
, image
.create_snap
, 'test')
317 assert_raises(ReadOnlyImage
, image
.remove_snap
, 'snap')
318 assert_raises(ReadOnlyImage
, image
.rollback_to_snap
, 'snap')
319 assert_raises(ReadOnlyImage
, image
.protect_snap
, 'snap')
320 assert_raises(ReadOnlyImage
, image
.unprotect_snap
, 'snap')
321 assert_raises(ReadOnlyImage
, image
.unprotect_snap
, 'snap')
322 assert_raises(ReadOnlyImage
, image
.flatten
)
323 with
Image(ioctx
, image_name
) as image
:
324 image
.remove_snap('snap')
325 RBD().remove(ioctx
, image_name
)
330 image_name
= get_temp_image_name()
331 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
+ 'dne')
332 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
, 'snap')
334 def test_open_readonly_dne():
336 image_name
= get_temp_image_name()
337 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
+ 'dne',
339 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
, 'snap',
342 @require_new_format()
343 def test_open_by_id():
344 with
Rados(conffile
='') as cluster
:
345 with cluster
.open_ioctx(pool_name
) as ioctx
:
346 image_name
= get_temp_image_name()
347 RBD().create(ioctx
, image_name
, IMG_SIZE
)
348 with
Image(ioctx
, image_name
) as image
:
349 image_id
= image
.id()
350 with
Image(ioctx
, image_id
=image_id
) as image
:
351 eq(image
.get_name(), image_name
)
352 RBD().remove(ioctx
, image_name
)
355 with
Rados(conffile
='') as cluster
:
356 with cluster
.open_ioctx(pool_name
) as ioctx
:
357 image_name
= get_temp_image_name()
359 RBD().create(ioctx
, image_name
, IMG_SIZE
, order
)
361 # this is a list so that the open_cb() can modify it
363 def open_cb(_
, image_
):
366 comp
= RBD().aio_open_image(open_cb
, ioctx
, image_name
)
367 comp
.wait_for_complete_and_cb()
368 eq(comp
.get_return_value(), 0)
369 eq(sys
.getrefcount(comp
), 2)
370 assert_not_equal(image
[0], None)
373 eq(image
.get_name(), image_name
)
374 check_stat(image
.stat(), IMG_SIZE
, order
)
380 comp
= image
.aio_close(close_cb
)
381 comp
.wait_for_complete_and_cb()
382 eq(comp
.get_return_value(), 0)
383 eq(sys
.getrefcount(comp
), 2)
386 RBD().remove(ioctx
, image_name
)
388 def test_remove_dne():
389 assert_raises(ImageNotFound
, remove_image
)
391 def test_list_empty():
392 eq([], RBD().list(ioctx
))
394 def test_list(tmp_image
):
395 eq([image_name
], RBD().list(ioctx
))
397 with
Image(ioctx
, image_name
) as image
:
398 image_id
= image
.id()
399 eq([{'id': image_id
, 'name': image_name
}], list(RBD().list2(ioctx
)))
401 def test_remove_with_progress():
403 d
= {'received_callback': False}
404 def progress_cb(current
, total
):
405 d
['received_callback'] = True
408 RBD().remove(ioctx
, image_name
, on_progress
=progress_cb
)
409 eq(True, d
['received_callback'])
411 def test_remove_canceled(tmp_image
):
412 def progress_cb(current
, total
):
415 assert_raises(OperationCanceled
, RBD().remove
, ioctx
, image_name
,
416 on_progress
=progress_cb
)
418 def test_rename(tmp_image
):
420 image_name2
= get_temp_image_name()
421 rbd
.rename(ioctx
, image_name
, image_name2
)
422 eq([image_name2
], rbd
.list(ioctx
))
423 rbd
.rename(ioctx
, image_name2
, image_name
)
424 eq([image_name
], rbd
.list(ioctx
))
426 def test_pool_metadata():
428 metadata
= list(rbd
.pool_metadata_list(ioctx
))
430 assert_raises(KeyError, rbd
.pool_metadata_get
, ioctx
, "key1")
431 rbd
.pool_metadata_set(ioctx
, "key1", "value1")
432 rbd
.pool_metadata_set(ioctx
, "key2", "value2")
433 value
= rbd
.pool_metadata_get(ioctx
, "key1")
435 value
= rbd
.pool_metadata_get(ioctx
, "key2")
437 metadata
= list(rbd
.pool_metadata_list(ioctx
))
439 rbd
.pool_metadata_remove(ioctx
, "key1")
440 metadata
= list(rbd
.pool_metadata_list(ioctx
))
442 eq(metadata
[0], ("key2", "value2"))
443 rbd
.pool_metadata_remove(ioctx
, "key2")
444 assert_raises(KeyError, rbd
.pool_metadata_remove
, ioctx
, "key2")
445 metadata
= list(rbd
.pool_metadata_list(ioctx
))
450 rbd
.pool_metadata_set(ioctx
, "key" + str(i
), "X" * 1025)
451 metadata
= list(rbd
.pool_metadata_list(ioctx
))
454 rbd
.pool_metadata_remove(ioctx
, "key" + str(i
))
455 metadata
= list(rbd
.pool_metadata_list(ioctx
))
456 eq(len(metadata
), N
- i
- 1)
458 def test_config_list():
461 for option
in rbd
.config_list(ioctx
):
462 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
464 rbd
.pool_metadata_set(ioctx
, "conf_rbd_cache", "true")
466 for option
in rbd
.config_list(ioctx
):
467 if option
['name'] == "rbd_cache":
468 eq(option
['source'], RBD_CONFIG_SOURCE_POOL
)
470 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
472 rbd
.pool_metadata_remove(ioctx
, "conf_rbd_cache")
474 for option
in rbd
.config_list(ioctx
):
475 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
477 def test_pool_config_set_and_get_and_remove():
480 for option
in rbd
.config_list(ioctx
):
481 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
483 rbd
.config_set(ioctx
, "rbd_request_timed_out_seconds", "100")
484 new_value
= rbd
.config_get(ioctx
, "rbd_request_timed_out_seconds")
486 rbd
.config_remove(ioctx
, "rbd_request_timed_out_seconds")
488 for option
in rbd
.config_list(ioctx
):
489 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
491 def test_namespaces():
494 eq(False, rbd
.namespace_exists(ioctx
, 'ns1'))
495 eq([], rbd
.namespace_list(ioctx
))
496 assert_raises(ImageNotFound
, rbd
.namespace_remove
, ioctx
, 'ns1')
498 rbd
.namespace_create(ioctx
, 'ns1')
499 eq(True, rbd
.namespace_exists(ioctx
, 'ns1'))
501 assert_raises(ImageExists
, rbd
.namespace_create
, ioctx
, 'ns1')
502 eq(['ns1'], rbd
.namespace_list(ioctx
))
503 rbd
.namespace_remove(ioctx
, 'ns1')
504 eq([], rbd
.namespace_list(ioctx
))
506 @require_new_format()
507 def test_pool_stats():
511 image1
= create_image()
512 image2
= create_image()
513 image3
= create_image()
514 image4
= create_image()
515 with
Image(ioctx
, image4
) as image
:
516 image
.create_snap('snap')
519 stats
= rbd
.pool_stats_get(ioctx
)
520 eq(stats
['image_count'], 4)
521 eq(stats
['image_provisioned_bytes'], 3 * IMG_SIZE
)
522 eq(stats
['image_max_provisioned_bytes'], 4 * IMG_SIZE
)
523 eq(stats
['image_snap_count'], 1)
524 eq(stats
['trash_count'], 0)
525 eq(stats
['trash_provisioned_bytes'], 0)
526 eq(stats
['trash_max_provisioned_bytes'], 0)
527 eq(stats
['trash_snap_count'], 0)
529 rbd
.remove(ioctx
, image1
)
530 rbd
.remove(ioctx
, image2
)
531 rbd
.remove(ioctx
, image3
)
532 with
Image(ioctx
, image4
) as image
:
533 image
.remove_snap('snap')
534 rbd
.remove(ioctx
, image4
)
537 return os
.urandom(size
)
539 def check_stat(info
, size
, order
):
540 assert 'block_name_prefix' in info
541 eq(info
['size'], size
)
542 eq(info
['order'], order
)
543 eq(info
['num_objs'], size
// (1 << order
))
544 eq(info
['obj_size'], 1 << order
)
546 @require_new_format()
547 def test_features_to_string():
549 features
= RBD_FEATURE_DEEP_FLATTEN | RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_FAST_DIFF \
550 | RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP
551 expected_features_string
= "deep-flatten,exclusive-lock,fast-diff,layering,object-map"
552 features_string
= rbd
.features_to_string(features
)
553 eq(expected_features_string
, features_string
)
555 features
= RBD_FEATURE_LAYERING
556 features_string
= rbd
.features_to_string(features
)
557 eq(features_string
, "layering")
560 assert_raises(InvalidArgument
, rbd
.features_to_string
, features
)
562 @require_new_format()
563 def test_features_from_string():
565 features_string
= "deep-flatten,exclusive-lock,fast-diff,layering,object-map"
566 expected_features_bitmask
= RBD_FEATURE_DEEP_FLATTEN | RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_FAST_DIFF \
567 | RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP
568 features
= rbd
.features_from_string(features_string
)
569 eq(expected_features_bitmask
, features
)
571 features_string
= "layering"
572 features
= rbd
.features_from_string(features_string
)
573 eq(features
, RBD_FEATURE_LAYERING
)
575 class TestImage(object):
577 def setup_method(self
, method
):
580 self
.image
= Image(ioctx
, image_name
)
582 def teardown_method(self
, method
):
587 @require_new_format()
588 @blocklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
589 def test_update_features(self
):
590 features
= self
.image
.features()
591 self
.image
.update_features(RBD_FEATURE_EXCLUSIVE_LOCK
, True)
592 eq(features | RBD_FEATURE_EXCLUSIVE_LOCK
, self
.image
.features())
594 @require_features([RBD_FEATURE_STRIPINGV2
])
595 def test_create_with_params(self
):
597 image_name
= get_temp_image_name()
599 stripe_unit
= 1 << 20
601 self
.rbd
.create(ioctx
, image_name
, IMG_SIZE
, order
,
602 False, features
, stripe_unit
, stripe_count
)
603 image
= Image(ioctx
, image_name
)
605 check_stat(info
, IMG_SIZE
, order
)
606 eq(image
.features(), features
)
607 eq(image
.stripe_unit(), stripe_unit
)
608 eq(image
.stripe_count(), stripe_count
)
610 RBD().remove(ioctx
, image_name
)
612 @require_new_format()
614 assert_not_equal(b
'', self
.image
.id())
616 def test_block_name_prefix(self
):
617 assert_not_equal(b
'', self
.image
.block_name_prefix())
619 def test_data_pool_id(self
):
620 assert_greater_equal(self
.image
.data_pool_id(), 0)
622 def test_create_timestamp(self
):
623 timestamp
= self
.image
.create_timestamp()
624 assert_not_equal(0, timestamp
.year
)
625 assert_not_equal(1970, timestamp
.year
)
627 def test_access_timestamp(self
):
628 timestamp
= self
.image
.access_timestamp()
629 assert_not_equal(0, timestamp
.year
)
630 assert_not_equal(1970, timestamp
.year
)
632 def test_modify_timestamp(self
):
633 timestamp
= self
.image
.modify_timestamp()
634 assert_not_equal(0, timestamp
.year
)
635 assert_not_equal(1970, timestamp
.year
)
637 def test_invalidate_cache(self
):
638 self
.image
.write(b
'abc', 0)
639 eq(b
'abc', self
.image
.read(0, 3))
640 self
.image
.invalidate_cache()
641 eq(b
'abc', self
.image
.read(0, 3))
644 info
= self
.image
.stat()
645 check_stat(info
, IMG_SIZE
, IMG_ORDER
)
647 def test_flags(self
):
648 flags
= self
.image
.flags()
651 def test_image_auto_close(self
):
652 image
= Image(ioctx
, image_name
)
654 def test_use_after_close(self
):
656 assert_raises(InvalidArgument
, self
.image
.stat
)
658 def test_write(self
):
659 data
= rand_data(256)
660 self
.image
.write(data
, 0)
662 def test_write_with_fadvise_flags(self
):
663 data
= rand_data(256)
664 self
.image
.write(data
, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED
)
665 self
.image
.write(data
, 0, LIBRADOS_OP_FLAG_FADVISE_NOCACHE
)
667 def test_write_zeroes(self
):
668 data
= rand_data(256)
669 self
.image
.write(data
, 0)
670 self
.image
.write_zeroes(0, 256)
671 eq(self
.image
.read(256, 256), b
'\0' * 256)
672 check_diff(self
.image
, 0, IMG_SIZE
, None, [])
674 def test_write_zeroes_thick_provision(self
):
675 data
= rand_data(256)
676 self
.image
.write(data
, 0)
677 self
.image
.write_zeroes(0, 256, RBD_WRITE_ZEROES_FLAG_THICK_PROVISION
)
678 eq(self
.image
.read(256, 256), b
'\0' * 256)
679 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 256, True)])
682 data
= self
.image
.read(0, 20)
685 def test_read_with_fadvise_flags(self
):
686 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED
)
688 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM
)
691 def test_large_write(self
):
692 data
= rand_data(IMG_SIZE
)
693 self
.image
.write(data
, 0)
695 def test_large_read(self
):
696 data
= self
.image
.read(0, IMG_SIZE
)
697 eq(data
, b
'\0' * IMG_SIZE
)
699 def test_write_read(self
):
700 data
= rand_data(256)
702 self
.image
.write(data
, offset
)
703 read
= self
.image
.read(offset
, 256)
706 def test_read_bad_offset(self
):
707 assert_raises(InvalidArgument
, self
.image
.read
, IMG_SIZE
+ 1, IMG_SIZE
)
709 def test_resize(self
):
710 new_size
= IMG_SIZE
* 2
711 self
.image
.resize(new_size
)
712 info
= self
.image
.stat()
713 check_stat(info
, new_size
, IMG_ORDER
)
715 def test_resize_allow_shrink_False(self
):
716 new_size
= IMG_SIZE
* 2
717 self
.image
.resize(new_size
)
718 info
= self
.image
.stat()
719 check_stat(info
, new_size
, IMG_ORDER
)
720 assert_raises(InvalidArgument
, self
.image
.resize
, IMG_SIZE
, False)
723 eq(IMG_SIZE
, self
.image
.size())
724 self
.image
.create_snap('snap1')
725 new_size
= IMG_SIZE
* 2
726 self
.image
.resize(new_size
)
727 eq(new_size
, self
.image
.size())
728 self
.image
.create_snap('snap2')
729 self
.image
.set_snap('snap2')
730 eq(new_size
, self
.image
.size())
731 self
.image
.set_snap('snap1')
732 eq(IMG_SIZE
, self
.image
.size())
733 self
.image
.set_snap(None)
734 eq(new_size
, self
.image
.size())
735 self
.image
.remove_snap('snap1')
736 self
.image
.remove_snap('snap2')
738 def test_resize_down(self
):
739 new_size
= IMG_SIZE
// 2
740 data
= rand_data(256)
741 self
.image
.write(data
, IMG_SIZE
// 2);
742 self
.image
.resize(new_size
)
743 self
.image
.resize(IMG_SIZE
)
744 read
= self
.image
.read(IMG_SIZE
// 2, 256)
745 eq(b
'\0' * 256, read
)
747 def test_resize_bytes(self
):
748 new_size
= IMG_SIZE
// 2 - 5
749 data
= rand_data(256)
750 self
.image
.write(data
, IMG_SIZE
// 2 - 10);
751 self
.image
.resize(new_size
)
752 self
.image
.resize(IMG_SIZE
)
753 read
= self
.image
.read(IMG_SIZE
// 2 - 10, 5)
755 read
= self
.image
.read(IMG_SIZE
// 2 - 5, 251)
756 eq(b
'\0' * 251, read
)
758 def _test_copy(self
, features
=None, order
=None, stripe_unit
=None,
761 data
= rand_data(256)
762 self
.image
.write(data
, 256)
763 image_name
= get_temp_image_name()
765 self
.image
.copy(ioctx
, image_name
)
767 self
.image
.copy(ioctx
, image_name
, features
)
768 elif stripe_unit
is None:
769 self
.image
.copy(ioctx
, image_name
, features
, order
)
770 elif stripe_count
is None:
771 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
)
773 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
,
775 assert_raises(ImageExists
, self
.image
.copy
, ioctx
, image_name
)
776 copy
= Image(ioctx
, image_name
)
777 copy_data
= copy
.read(256, 256)
779 self
.rbd
.remove(ioctx
, image_name
)
785 def test_copy2(self
):
786 self
._test
_copy
(self
.image
.features(), self
.image
.stat()['order'])
788 @require_features([RBD_FEATURE_STRIPINGV2
])
789 def test_copy3(self
):
791 self
._test
_copy
(features
, self
.image
.stat()['order'],
792 self
.image
.stripe_unit(), self
.image
.stripe_count())
794 @pytest.mark
.skip_if_crimson
795 def test_deep_copy(self
):
798 self
.image
.write(b
'a' * 256, 0)
799 self
.image
.create_snap('snap1')
800 self
.image
.write(b
'b' * 256, 0)
801 dst_name
= get_temp_image_name()
802 self
.image
.deep_copy(ioctx
, dst_name
, features
=features
,
803 order
=self
.image
.stat()['order'],
804 stripe_unit
=self
.image
.stripe_unit(),
805 stripe_count
=self
.image
.stripe_count(),
807 self
.image
.remove_snap('snap1')
808 with
Image(ioctx
, dst_name
, 'snap1') as copy
:
809 copy_data
= copy
.read(0, 256)
810 eq(b
'a' * 256, copy_data
)
811 with
Image(ioctx
, dst_name
) as copy
:
812 copy_data
= copy
.read(0, 256)
813 eq(b
'b' * 256, copy_data
)
814 copy
.remove_snap('snap1')
815 self
.rbd
.remove(ioctx
, dst_name
)
817 @require_features([RBD_FEATURE_LAYERING
])
818 def test_deep_copy_clone(self
):
821 self
.image
.write(b
'a' * 256, 0)
822 self
.image
.create_snap('snap1')
823 self
.image
.write(b
'b' * 256, 0)
824 self
.image
.protect_snap('snap1')
825 clone_name
= get_temp_image_name()
826 dst_name
= get_temp_image_name()
827 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name
)
828 with
Image(ioctx
, clone_name
) as child
:
829 child
.create_snap('snap1')
830 child
.deep_copy(ioctx
, dst_name
, features
=features
,
831 order
=self
.image
.stat()['order'],
832 stripe_unit
=self
.image
.stripe_unit(),
833 stripe_count
=self
.image
.stripe_count(),
835 child
.remove_snap('snap1')
837 with
Image(ioctx
, dst_name
) as copy
:
838 copy_data
= copy
.read(0, 256)
839 eq(b
'a' * 256, copy_data
)
840 copy
.remove_snap('snap1')
841 self
.rbd
.remove(ioctx
, dst_name
)
842 self
.rbd
.remove(ioctx
, clone_name
)
843 self
.image
.unprotect_snap('snap1')
844 self
.image
.remove_snap('snap1')
846 def test_create_snap(self
):
848 self
.image
.create_snap('snap1')
849 read
= self
.image
.read(0, 256)
850 eq(read
, b
'\0' * 256)
851 data
= rand_data(256)
852 self
.image
.write(data
, 0)
853 read
= self
.image
.read(0, 256)
855 at_snapshot
= Image(ioctx
, image_name
, 'snap1')
856 snap_data
= at_snapshot
.read(0, 256)
858 eq(snap_data
, b
'\0' * 256)
859 self
.image
.remove_snap('snap1')
861 def test_create_snap_exists(self
):
862 self
.image
.create_snap('snap1')
863 assert_raises(ImageExists
, self
.image
.create_snap
, 'snap1')
864 self
.image
.remove_snap('snap1')
866 def test_create_snap_flags(self
):
867 self
.image
.create_snap('snap1', 0)
868 self
.image
.remove_snap('snap1')
869 self
.image
.create_snap('snap1', RBD_SNAP_CREATE_SKIP_QUIESCE
)
870 self
.image
.remove_snap('snap1')
871 self
.image
.create_snap('snap1', RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR
)
872 self
.image
.remove_snap('snap1')
874 def test_list_snaps(self
):
875 eq([], list(self
.image
.list_snaps()))
876 self
.image
.create_snap('snap1')
877 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
878 self
.image
.create_snap('snap2')
879 eq(['snap1', 'snap2'], [snap
['name'] for snap
in self
.image
.list_snaps()])
880 self
.image
.remove_snap('snap1')
881 self
.image
.remove_snap('snap2')
883 def test_list_snaps_iterator_auto_close(self
):
884 self
.image
.create_snap('snap1')
885 self
.image
.list_snaps()
886 self
.image
.remove_snap('snap1')
888 def test_remove_snap(self
):
889 eq([], list(self
.image
.list_snaps()))
890 self
.image
.create_snap('snap1')
891 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
892 self
.image
.remove_snap('snap1')
893 eq([], list(self
.image
.list_snaps()))
895 def test_remove_snap_not_found(self
):
896 assert_raises(ImageNotFound
, self
.image
.remove_snap
, 'snap1')
898 @require_features([RBD_FEATURE_LAYERING
])
899 def test_remove_snap2(self
):
900 self
.image
.create_snap('snap1')
901 self
.image
.protect_snap('snap1')
902 assert(self
.image
.is_protected_snap('snap1'))
903 self
.image
.remove_snap2('snap1', RBD_SNAP_REMOVE_UNPROTECT
)
904 eq([], list(self
.image
.list_snaps()))
906 def test_remove_snap_by_id(self
):
907 eq([], list(self
.image
.list_snaps()))
908 self
.image
.create_snap('snap1')
909 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
910 for snap
in self
.image
.list_snaps():
912 self
.image
.remove_snap_by_id(snap_id
)
913 eq([], list(self
.image
.list_snaps()))
915 def test_rename_snap(self
):
916 eq([], list(self
.image
.list_snaps()))
917 self
.image
.create_snap('snap1')
918 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
919 self
.image
.rename_snap("snap1", "snap1-rename")
920 eq(['snap1-rename'], [snap
['name'] for snap
in self
.image
.list_snaps()])
921 self
.image
.remove_snap('snap1-rename')
922 eq([], list(self
.image
.list_snaps()))
924 @require_features([RBD_FEATURE_LAYERING
])
925 def test_protect_snap(self
):
926 self
.image
.create_snap('snap1')
927 assert(not self
.image
.is_protected_snap('snap1'))
928 self
.image
.protect_snap('snap1')
929 assert(self
.image
.is_protected_snap('snap1'))
930 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
931 self
.image
.unprotect_snap('snap1')
932 assert(not self
.image
.is_protected_snap('snap1'))
933 self
.image
.remove_snap('snap1')
934 assert_raises(ImageNotFound
, self
.image
.unprotect_snap
, 'snap1')
935 assert_raises(ImageNotFound
, self
.image
.is_protected_snap
, 'snap1')
937 def test_snap_exists(self
):
938 self
.image
.create_snap('snap1')
939 eq(self
.image
.snap_exists('snap1'), True)
940 self
.image
.remove_snap('snap1')
941 eq(self
.image
.snap_exists('snap1'), False)
943 def test_snap_timestamp(self
):
944 self
.image
.create_snap('snap1')
945 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
946 for snap
in self
.image
.list_snaps():
948 time
= self
.image
.get_snap_timestamp(snap_id
)
949 assert_not_equal(b
'', time
.year
)
950 assert_not_equal(0, time
.year
)
951 assert_not_equal(time
.year
, '1970')
952 self
.image
.remove_snap('snap1')
954 def test_limit_snaps(self
):
955 self
.image
.set_snap_limit(2)
956 eq(2, self
.image
.get_snap_limit())
957 self
.image
.create_snap('snap1')
958 self
.image
.create_snap('snap2')
959 assert_raises(DiskQuotaExceeded
, self
.image
.create_snap
, 'snap3')
960 self
.image
.remove_snap_limit()
961 self
.image
.create_snap('snap3')
963 self
.image
.remove_snap('snap1')
964 self
.image
.remove_snap('snap2')
965 self
.image
.remove_snap('snap3')
967 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
968 def test_remove_with_exclusive_lock(self
):
969 assert_raises(ImageBusy
, remove_image
)
971 @blocklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
972 def test_remove_with_snap(self
):
973 self
.image
.create_snap('snap1')
974 assert_raises(ImageHasSnapshots
, remove_image
)
975 self
.image
.remove_snap('snap1')
977 @blocklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
978 def test_remove_with_watcher(self
):
979 data
= rand_data(256)
980 self
.image
.write(data
, 0)
981 assert_raises(ImageBusy
, remove_image
)
982 read
= self
.image
.read(0, 256)
985 def test_rollback_to_snap(self
):
986 self
.image
.write(b
'\0' * 256, 0)
987 self
.image
.create_snap('snap1')
988 read
= self
.image
.read(0, 256)
989 eq(read
, b
'\0' * 256)
990 data
= rand_data(256)
991 self
.image
.write(data
, 0)
992 read
= self
.image
.read(0, 256)
994 self
.image
.rollback_to_snap('snap1')
995 read
= self
.image
.read(0, 256)
996 eq(read
, b
'\0' * 256)
997 self
.image
.remove_snap('snap1')
999 def test_rollback_to_snap_sparse(self
):
1000 self
.image
.create_snap('snap1')
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 read
= self
.image
.read(0, 256)
1007 self
.image
.rollback_to_snap('snap1')
1008 read
= self
.image
.read(0, 256)
1009 eq(read
, b
'\0' * 256)
1010 self
.image
.remove_snap('snap1')
1012 def test_rollback_with_resize(self
):
1013 read
= self
.image
.read(0, 256)
1014 eq(read
, b
'\0' * 256)
1015 data
= rand_data(256)
1016 self
.image
.write(data
, 0)
1017 self
.image
.create_snap('snap1')
1018 read
= self
.image
.read(0, 256)
1020 new_size
= IMG_SIZE
* 2
1021 self
.image
.resize(new_size
)
1022 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
1023 self
.image
.write(data
, new_size
- 256)
1024 self
.image
.create_snap('snap2')
1025 read
= self
.image
.read(new_size
- 256, 256)
1027 self
.image
.rollback_to_snap('snap1')
1028 check_stat(self
.image
.stat(), IMG_SIZE
, IMG_ORDER
)
1029 assert_raises(InvalidArgument
, self
.image
.read
, new_size
- 256, 256)
1030 self
.image
.rollback_to_snap('snap2')
1031 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
1032 read
= self
.image
.read(new_size
- 256, 256)
1034 self
.image
.remove_snap('snap1')
1035 self
.image
.remove_snap('snap2')
1037 def test_set_snap(self
):
1038 self
.image
.write(b
'\0' * 256, 0)
1039 self
.image
.create_snap('snap1')
1040 read
= self
.image
.read(0, 256)
1041 eq(read
, b
'\0' * 256)
1042 data
= rand_data(256)
1043 self
.image
.write(data
, 0)
1044 read
= self
.image
.read(0, 256)
1046 self
.image
.set_snap('snap1')
1047 read
= self
.image
.read(0, 256)
1048 eq(read
, b
'\0' * 256)
1049 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
1050 self
.image
.remove_snap('snap1')
1052 def test_set_no_snap(self
):
1053 self
.image
.write(b
'\0' * 256, 0)
1054 self
.image
.create_snap('snap1')
1055 read
= self
.image
.read(0, 256)
1056 eq(read
, b
'\0' * 256)
1057 data
= rand_data(256)
1058 self
.image
.write(data
, 0)
1059 read
= self
.image
.read(0, 256)
1061 self
.image
.set_snap('snap1')
1062 read
= self
.image
.read(0, 256)
1063 eq(read
, b
'\0' * 256)
1064 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
1065 self
.image
.set_snap(None)
1066 read
= self
.image
.read(0, 256)
1068 self
.image
.remove_snap('snap1')
1070 def test_set_snap_by_id(self
):
1071 self
.image
.write(b
'\0' * 256, 0)
1072 self
.image
.create_snap('snap1')
1073 read
= self
.image
.read(0, 256)
1074 eq(read
, b
'\0' * 256)
1075 data
= rand_data(256)
1076 self
.image
.write(data
, 0)
1077 read
= self
.image
.read(0, 256)
1079 snaps
= list(self
.image
.list_snaps())
1080 self
.image
.set_snap_by_id(snaps
[0]['id'])
1081 read
= self
.image
.read(0, 256)
1082 eq(read
, b
'\0' * 256)
1083 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
1084 self
.image
.set_snap_by_id(None)
1085 read
= self
.image
.read(0, 256)
1087 self
.image
.remove_snap('snap1')
1089 def test_snap_get_name(self
):
1090 eq([], list(self
.image
.list_snaps()))
1091 self
.image
.create_snap('snap1')
1092 self
.image
.create_snap('snap2')
1093 self
.image
.create_snap('snap3')
1095 for snap
in self
.image
.list_snaps():
1096 expected_snap_name
= self
.image
.snap_get_name(snap
['id'])
1097 eq(expected_snap_name
, snap
['name'])
1098 self
.image
.remove_snap('snap1')
1099 self
.image
.remove_snap('snap2')
1100 self
.image
.remove_snap('snap3')
1101 eq([], list(self
.image
.list_snaps()))
1103 assert_raises(ImageNotFound
, self
.image
.snap_get_name
, 1)
1105 def test_snap_get_id(self
):
1106 eq([], list(self
.image
.list_snaps()))
1107 self
.image
.create_snap('snap1')
1108 self
.image
.create_snap('snap2')
1109 self
.image
.create_snap('snap3')
1111 for snap
in self
.image
.list_snaps():
1112 expected_snap_id
= self
.image
.snap_get_id(snap
['name'])
1113 eq(expected_snap_id
, snap
['id'])
1114 self
.image
.remove_snap('snap1')
1115 self
.image
.remove_snap('snap2')
1116 self
.image
.remove_snap('snap3')
1117 eq([], list(self
.image
.list_snaps()))
1119 assert_raises(ImageNotFound
, self
.image
.snap_get_id
, 'snap1')
1121 def test_set_snap_sparse(self
):
1122 self
.image
.create_snap('snap1')
1123 read
= self
.image
.read(0, 256)
1124 eq(read
, b
'\0' * 256)
1125 data
= rand_data(256)
1126 self
.image
.write(data
, 0)
1127 read
= self
.image
.read(0, 256)
1129 self
.image
.set_snap('snap1')
1130 read
= self
.image
.read(0, 256)
1131 eq(read
, b
'\0' * 256)
1132 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
1133 self
.image
.remove_snap('snap1')
1135 def test_many_snaps(self
):
1137 for i
in range(num_snaps
):
1138 self
.image
.create_snap(str(i
))
1139 snaps
= sorted(self
.image
.list_snaps(),
1140 key
=lambda snap
: int(snap
['name']))
1141 eq(len(snaps
), num_snaps
)
1142 for i
, snap
in enumerate(snaps
):
1143 eq(snap
['size'], IMG_SIZE
)
1144 eq(snap
['name'], str(i
))
1145 for i
in range(num_snaps
):
1146 self
.image
.remove_snap(str(i
))
1148 def test_set_snap_deleted(self
):
1149 self
.image
.write(b
'\0' * 256, 0)
1150 self
.image
.create_snap('snap1')
1151 read
= self
.image
.read(0, 256)
1152 eq(read
, b
'\0' * 256)
1153 data
= rand_data(256)
1154 self
.image
.write(data
, 0)
1155 read
= self
.image
.read(0, 256)
1157 self
.image
.set_snap('snap1')
1158 self
.image
.remove_snap('snap1')
1159 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
1160 self
.image
.set_snap(None)
1161 read
= self
.image
.read(0, 256)
1164 def test_set_snap_recreated(self
):
1165 self
.image
.write(b
'\0' * 256, 0)
1166 self
.image
.create_snap('snap1')
1167 read
= self
.image
.read(0, 256)
1168 eq(read
, b
'\0' * 256)
1169 data
= rand_data(256)
1170 self
.image
.write(data
, 0)
1171 read
= self
.image
.read(0, 256)
1173 self
.image
.set_snap('snap1')
1174 self
.image
.remove_snap('snap1')
1175 self
.image
.create_snap('snap1')
1176 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
1177 self
.image
.set_snap(None)
1178 read
= self
.image
.read(0, 256)
1180 self
.image
.remove_snap('snap1')
1182 def test_lock_unlock(self
):
1183 assert_raises(ImageNotFound
, self
.image
.unlock
, '')
1184 self
.image
.lock_exclusive('')
1185 assert_raises(ImageExists
, self
.image
.lock_exclusive
, '')
1186 assert_raises(ImageBusy
, self
.image
.lock_exclusive
, 'test')
1187 assert_raises(ImageExists
, self
.image
.lock_shared
, '', '')
1188 assert_raises(ImageBusy
, self
.image
.lock_shared
, 'foo', '')
1189 self
.image
.unlock('')
1191 def test_list_lockers(self
):
1192 eq([], self
.image
.list_lockers())
1193 self
.image
.lock_exclusive('test')
1194 lockers
= self
.image
.list_lockers()
1195 eq(1, len(lockers
['lockers']))
1196 _
, cookie
, _
= lockers
['lockers'][0]
1198 eq('', lockers
['tag'])
1199 assert lockers
['exclusive']
1200 self
.image
.unlock('test')
1201 eq([], self
.image
.list_lockers())
1204 for i
in range(num_shared
):
1205 self
.image
.lock_shared(str(i
), 'tag')
1206 lockers
= self
.image
.list_lockers()
1207 eq('tag', lockers
['tag'])
1208 assert not lockers
['exclusive']
1209 eq(num_shared
, len(lockers
['lockers']))
1210 cookies
= sorted(map(lambda x
: x
[1], lockers
['lockers']))
1211 for i
in range(num_shared
):
1212 eq(str(i
), cookies
[i
])
1213 self
.image
.unlock(str(i
))
1214 eq([], self
.image
.list_lockers())
1216 def test_diff_iterate(self
):
1217 check_diff(self
.image
, 0, IMG_SIZE
, None, [])
1218 self
.image
.write(b
'a' * 256, 0)
1219 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 256, True)])
1220 self
.image
.write(b
'b' * 256, 256)
1221 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
1222 self
.image
.discard(128, 256)
1223 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
1225 self
.image
.create_snap('snap1')
1226 self
.image
.discard(0, 1 << IMG_ORDER
)
1227 self
.image
.create_snap('snap2')
1228 self
.image
.set_snap('snap2')
1229 check_diff(self
.image
, 0, IMG_SIZE
, 'snap1', [(0, 512, False)])
1230 self
.image
.remove_snap('snap1')
1231 self
.image
.remove_snap('snap2')
1233 def test_aio_read(self
):
1234 # this is a list so that the local cb() can modify it
1239 # test1: success case
1240 comp
= self
.image
.aio_read(0, 20, cb
)
1241 comp
.wait_for_complete_and_cb()
1242 eq(retval
[0], b
'\0' * 20)
1243 eq(comp
.get_return_value(), 20)
1244 eq(sys
.getrefcount(comp
), 2)
1248 comp
= self
.image
.aio_read(IMG_SIZE
, 20, cb
)
1249 comp
.wait_for_complete_and_cb()
1251 assert(comp
.get_return_value() < 0)
1252 eq(sys
.getrefcount(comp
), 2)
1254 def test_aio_write(self
):
1257 retval
[0] = comp
.get_return_value()
1259 data
= rand_data(256)
1260 comp
= self
.image
.aio_write(data
, 256, cb
)
1261 comp
.wait_for_complete_and_cb()
1263 eq(comp
.get_return_value(), 0)
1264 eq(sys
.getrefcount(comp
), 2)
1265 eq(self
.image
.read(256, 256), data
)
1267 def test_aio_discard(self
):
1270 retval
[0] = comp
.get_return_value()
1272 data
= rand_data(256)
1273 self
.image
.write(data
, 0)
1274 comp
= self
.image
.aio_discard(0, 256, cb
)
1275 comp
.wait_for_complete_and_cb()
1277 eq(comp
.get_return_value(), 0)
1278 eq(sys
.getrefcount(comp
), 2)
1279 eq(self
.image
.read(256, 256), b
'\0' * 256)
1281 def test_aio_write_zeroes(self
):
1284 retval
[0] = comp
.get_return_value()
1286 data
= rand_data(256)
1287 self
.image
.write(data
, 0)
1288 comp
= self
.image
.aio_write_zeroes(0, 256, cb
)
1289 comp
.wait_for_complete_and_cb()
1291 eq(comp
.get_return_value(), 0)
1292 eq(sys
.getrefcount(comp
), 2)
1293 eq(self
.image
.read(256, 256), b
'\0' * 256)
1295 def test_aio_flush(self
):
1298 retval
[0] = comp
.get_return_value()
1300 comp
= self
.image
.aio_flush(cb
)
1301 comp
.wait_for_complete_and_cb()
1303 eq(sys
.getrefcount(comp
), 2)
1305 def test_metadata(self
):
1306 metadata
= list(self
.image
.metadata_list())
1307 eq(len(metadata
), 0)
1308 assert_raises(KeyError, self
.image
.metadata_get
, "key1")
1309 self
.image
.metadata_set("key1", "value1")
1310 self
.image
.metadata_set("key2", "value2")
1311 value
= self
.image
.metadata_get("key1")
1313 value
= self
.image
.metadata_get("key2")
1315 metadata
= list(self
.image
.metadata_list())
1316 eq(len(metadata
), 2)
1317 self
.image
.metadata_remove("key1")
1318 metadata
= list(self
.image
.metadata_list())
1319 eq(len(metadata
), 1)
1320 eq(metadata
[0], ("key2", "value2"))
1321 self
.image
.metadata_remove("key2")
1322 assert_raises(KeyError, self
.image
.metadata_remove
, "key2")
1323 metadata
= list(self
.image
.metadata_list())
1324 eq(len(metadata
), 0)
1328 self
.image
.metadata_set("key" + str(i
), "X" * 1025)
1329 metadata
= list(self
.image
.metadata_list())
1330 eq(len(metadata
), N
)
1332 self
.image
.metadata_remove("key" + str(i
))
1333 metadata
= list(self
.image
.metadata_list())
1334 eq(len(metadata
), N
- i
- 1)
1336 def test_watchers_list(self
):
1337 watchers
= list(self
.image
.watchers_list())
1338 # The image is open (in r/w mode) from setup, so expect there to be one
1340 eq(len(watchers
), 1)
1342 def test_config_list(self
):
1343 with
Image(ioctx
, image_name
) as image
:
1344 for option
in image
.config_list():
1345 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1347 image
.metadata_set("conf_rbd_cache", "true")
1349 for option
in image
.config_list():
1350 if option
['name'] == "rbd_cache":
1351 eq(option
['source'], RBD_CONFIG_SOURCE_IMAGE
)
1353 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1355 image
.metadata_remove("conf_rbd_cache")
1357 for option
in image
.config_list():
1358 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1360 def test_image_config_set_and_get_and_remove(self
):
1361 with
Image(ioctx
, image_name
) as image
:
1362 for option
in image
.config_list():
1363 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1365 image
.config_set("rbd_request_timed_out_seconds", "100")
1366 modify_value
= image
.config_get("rbd_request_timed_out_seconds")
1367 eq(modify_value
, '100')
1369 image
.config_remove("rbd_request_timed_out_seconds")
1371 for option
in image
.config_list():
1372 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1374 def test_sparsify(self
):
1375 assert_raises(InvalidArgument
, self
.image
.sparsify
, 16)
1376 self
.image
.sparsify(4096)
1379 @blocklist_features([RBD_FEATURE_JOURNALING
])
1380 def test_encryption_luks1(self
):
1381 data
= b
'hello world'
1385 with
Image(ioctx
, image_name
) as image
:
1386 image
.resize(image_size
)
1387 image
.write(data
, offset
)
1388 image
.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1
, "password")
1389 assert_not_equal(data
, image
.read(offset
, len(data
)))
1390 with
Image(ioctx
, image_name
) as image
:
1391 image
.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS1
, "password")
1392 assert_not_equal(data
, image
.read(offset
, len(data
)))
1393 image
.write(data
, offset
)
1394 with
Image(ioctx
, image_name
) as image
:
1395 image
.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS
, "password")
1396 eq(data
, image
.read(offset
, len(data
)))
1399 @blocklist_features([RBD_FEATURE_JOURNALING
])
1400 def test_encryption_luks2(self
):
1401 data
= b
'hello world'
1403 image_size
= 256<<20
1405 with
Image(ioctx
, image_name
) as image
:
1406 image
.resize(image_size
)
1407 image
.write(data
, offset
)
1408 image
.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2
, "password")
1409 assert_not_equal(data
, image
.read(offset
, len(data
)))
1410 with
Image(ioctx
, image_name
) as image
:
1411 image
.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS2
, "password")
1412 assert_not_equal(data
, image
.read(offset
, len(data
)))
1413 image
.write(data
, offset
)
1414 with
Image(ioctx
, image_name
) as image
:
1415 image
.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS
, "password")
1416 eq(data
, image
.read(offset
, len(data
)))
1419 class TestImageId(object):
1421 def setup_method(self
, method
):
1424 self
.image
= Image(ioctx
, image_name
)
1425 self
.image2
= Image(ioctx
, None, None, False, self
.image
.id())
1427 def teardown_method(self
, method
):
1434 def test_read(self
):
1435 data
= self
.image2
.read(0, 20)
1436 eq(data
, b
'\0' * 20)
1438 def test_write(self
):
1439 data
= rand_data(256)
1440 self
.image2
.write(data
, 0)
1442 def test_resize(self
):
1443 new_size
= IMG_SIZE
* 2
1444 self
.image2
.resize(new_size
)
1445 info
= self
.image2
.stat()
1446 check_stat(info
, new_size
, IMG_ORDER
)
1448 def check_diff(image
, offset
, length
, from_snapshot
, expected
):
1450 def cb(offset
, length
, exists
):
1451 extents
.append((offset
, length
, exists
))
1452 image
.diff_iterate(0, IMG_SIZE
, None, cb
)
1453 eq(extents
, expected
)
1455 class TestClone(object):
1457 @require_features([RBD_FEATURE_LAYERING
])
1458 def setup_method(self
, method
):
1463 self
.image
= Image(ioctx
, image_name
)
1464 data
= rand_data(256)
1465 self
.image
.write(data
, IMG_SIZE
// 2)
1466 self
.image
.create_snap('snap1')
1468 self
.image
.protect_snap('snap1')
1469 self
.clone_name
= get_temp_image_name()
1470 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
1472 self
.clone
= Image(ioctx
, self
.clone_name
)
1474 def teardown_method(self
, method
):
1477 self
.rbd
.remove(ioctx
, self
.clone_name
)
1478 self
.image
.unprotect_snap('snap1')
1479 self
.image
.remove_snap('snap1')
1483 def _test_with_params(self
, features
=None, order
=None, stripe_unit
=None,
1485 self
.image
.create_snap('snap2')
1486 self
.image
.protect_snap('snap2')
1487 clone_name2
= get_temp_image_name()
1488 if features
is None:
1489 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
)
1491 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1493 elif stripe_unit
is None:
1494 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1496 elif stripe_count
is None:
1497 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1498 features
, order
, stripe_unit
)
1500 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1501 features
, order
, stripe_unit
, stripe_count
)
1502 self
.rbd
.remove(ioctx
, clone_name2
)
1503 self
.image
.unprotect_snap('snap2')
1504 self
.image
.remove_snap('snap2')
1506 def test_with_params(self
):
1507 self
._test
_with
_params
()
1509 def test_with_params2(self
):
1511 self
._test
_with
_params
(features
, self
.image
.stat()['order'])
1513 @require_features([RBD_FEATURE_STRIPINGV2
])
1514 def test_with_params3(self
):
1516 self
._test
_with
_params
(features
, self
.image
.stat()['order'],
1517 self
.image
.stripe_unit(),
1518 self
.image
.stripe_count())
1520 def test_stripe_unit_and_count(self
):
1523 image_name
= get_temp_image_name()
1524 RBD().create(ioctx
, image_name
, IMG_SIZE
, IMG_ORDER
, old_format
=False,
1525 features
=int(features
), stripe_unit
=1048576, stripe_count
=8)
1526 image
= Image(ioctx
, image_name
)
1527 image
.create_snap('snap1')
1528 image
.protect_snap('snap1')
1529 clone_name
= get_temp_image_name()
1530 RBD().clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name
)
1531 clone
= Image(ioctx
, clone_name
)
1533 eq(1048576, clone
.stripe_unit())
1534 eq(8, clone
.stripe_count())
1537 RBD().remove(ioctx
, clone_name
)
1538 image
.unprotect_snap('snap1')
1539 image
.remove_snap('snap1')
1541 RBD().remove(ioctx
, image_name
)
1544 def test_unprotected(self
):
1545 self
.image
.create_snap('snap2')
1547 clone_name2
= get_temp_image_name()
1548 rados
.conf_set("rbd_default_clone_format", "1")
1549 assert_raises(InvalidArgument
, self
.rbd
.clone
, ioctx
, image_name
,
1550 'snap2', ioctx
, clone_name2
, features
)
1551 rados
.conf_set("rbd_default_clone_format", "auto")
1552 self
.image
.remove_snap('snap2')
1554 def test_unprotect_with_children(self
):
1556 # can't remove a snapshot that has dependent clones
1557 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
1559 # validate parent info of clone created by TestClone.setup_method
1560 (pool
, image
, snap
) = self
.clone
.parent_info()
1562 eq(image
, image_name
)
1564 eq(self
.image
.id(), self
.clone
.parent_id())
1566 # create a new pool...
1567 pool_name2
= get_temp_pool_name()
1568 rados
.create_pool(pool_name2
)
1569 other_ioctx
= rados
.open_ioctx(pool_name2
)
1570 other_ioctx
.application_enable('rbd')
1572 # ...with a clone of the same parent
1573 other_clone_name
= get_temp_image_name()
1574 rados
.conf_set("rbd_default_clone_format", "1")
1575 self
.rbd
.clone(ioctx
, image_name
, 'snap1', other_ioctx
,
1576 other_clone_name
, features
)
1577 rados
.conf_set("rbd_default_clone_format", "auto")
1578 self
.other_clone
= Image(other_ioctx
, other_clone_name
)
1579 # validate its parent info
1580 (pool
, image
, snap
) = self
.other_clone
.parent_info()
1582 eq(image
, image_name
)
1584 eq(self
.image
.id(), self
.other_clone
.parent_id())
1586 # can't unprotect snap with children
1587 assert_raises(ImageBusy
, self
.image
.unprotect_snap
, 'snap1')
1589 # 2 children, check that cannot remove the parent snap
1590 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
1592 # close and remove other pool's clone
1593 self
.other_clone
.close()
1594 self
.rbd
.remove(other_ioctx
, other_clone_name
)
1596 # check that we cannot yet remove the parent snap
1597 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
1600 rados
.delete_pool(pool_name2
)
1602 # unprotect, remove parent snap happen in cleanup, and should succeed
1604 def test_stat(self
):
1605 image_info
= self
.image
.stat()
1606 clone_info
= self
.clone
.stat()
1607 eq(clone_info
['size'], image_info
['size'])
1608 eq(clone_info
['size'], self
.clone
.overlap())
1610 def test_resize_stat(self
):
1611 self
.clone
.resize(IMG_SIZE
// 2)
1612 image_info
= self
.image
.stat()
1613 clone_info
= self
.clone
.stat()
1614 eq(clone_info
['size'], IMG_SIZE
// 2)
1615 eq(image_info
['size'], IMG_SIZE
)
1616 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
1618 self
.clone
.resize(IMG_SIZE
* 2)
1619 image_info
= self
.image
.stat()
1620 clone_info
= self
.clone
.stat()
1621 eq(clone_info
['size'], IMG_SIZE
* 2)
1622 eq(image_info
['size'], IMG_SIZE
)
1623 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
1625 def test_resize_io(self
):
1626 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1627 self
.image
.resize(0)
1628 self
.clone
.resize(IMG_SIZE
// 2 + 128)
1629 child_data
= self
.clone
.read(IMG_SIZE
// 2, 128)
1630 eq(child_data
, parent_data
[:128])
1631 self
.clone
.resize(IMG_SIZE
)
1632 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1633 eq(child_data
, parent_data
[:128] + (b
'\0' * 128))
1634 self
.clone
.resize(IMG_SIZE
// 2 + 1)
1635 child_data
= self
.clone
.read(IMG_SIZE
// 2, 1)
1636 eq(child_data
, parent_data
[0:1])
1637 self
.clone
.resize(0)
1638 self
.clone
.resize(IMG_SIZE
)
1639 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1640 eq(child_data
, b
'\0' * 256)
1642 def test_read(self
):
1643 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1644 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1645 eq(child_data
, parent_data
)
1647 def test_write(self
):
1648 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1649 new_data
= rand_data(256)
1650 self
.clone
.write(new_data
, IMG_SIZE
// 2 + 256)
1651 child_data
= self
.clone
.read(IMG_SIZE
// 2 + 256, 256)
1652 eq(child_data
, new_data
)
1653 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1654 eq(child_data
, parent_data
)
1655 parent_data
= self
.image
.read(IMG_SIZE
// 2 + 256, 256)
1656 eq(parent_data
, b
'\0' * 256)
1658 def check_children(self
, expected
):
1659 actual
= self
.image
.list_children()
1660 # dedup for cache pools until
1661 # http://tracker.ceph.com/issues/8187 is fixed
1662 deduped
= set([(pool_name
, image
[1]) for image
in actual
])
1663 eq(deduped
, set(expected
))
1665 def check_children2(self
, expected
):
1666 actual
= [{k
:v
for k
,v
in x
.items() if k
in expected
[0]} \
1667 for x
in self
.image
.list_children2()]
1668 eq(actual
, expected
)
1670 def check_descendants(self
, expected
):
1671 eq(list(self
.image
.list_descendants()), expected
)
1673 def get_image_id(self
, ioctx
, name
):
1674 with
Image(ioctx
, name
) as image
:
1677 def test_list_children(self
):
1680 self
.image
.set_snap('snap1')
1681 self
.check_children([(pool_name
, self
.clone_name
)])
1682 self
.check_children2(
1683 [{'pool': pool_name
, 'pool_namespace': '',
1684 'image': self
.clone_name
, 'trash': False,
1685 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1686 self
.check_descendants(
1687 [{'pool': pool_name
, 'pool_namespace': '',
1688 'image': self
.clone_name
, 'trash': False,
1689 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1691 self
.rbd
.remove(ioctx
, self
.clone_name
)
1692 eq(self
.image
.list_children(), [])
1693 eq(list(self
.image
.list_children2()), [])
1694 eq(list(self
.image
.list_descendants()), [])
1696 clone_name
= get_temp_image_name() + '_'
1697 expected_children
= []
1698 expected_children2
= []
1700 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
,
1701 clone_name
+ str(i
), features
)
1702 expected_children
.append((pool_name
, clone_name
+ str(i
)))
1703 expected_children2
.append(
1704 {'pool': pool_name
, 'pool_namespace': '',
1705 'image': clone_name
+ str(i
), 'trash': False,
1706 'id': self
.get_image_id(ioctx
, clone_name
+ str(i
))})
1707 self
.check_children(expected_children
)
1708 self
.check_children2(expected_children2
)
1709 self
.check_descendants(expected_children2
)
1711 image6_id
= self
.get_image_id(ioctx
, clone_name
+ str(5))
1712 RBD().trash_move(ioctx
, clone_name
+ str(5), 0)
1713 expected_children
.remove((pool_name
, clone_name
+ str(5)))
1714 for item
in expected_children2
:
1715 for k
, v
in item
.items():
1717 item
["trash"] = True
1718 self
.check_children(expected_children
)
1719 self
.check_children2(expected_children2
)
1720 self
.check_descendants(expected_children2
)
1722 RBD().trash_restore(ioctx
, image6_id
, clone_name
+ str(5))
1723 expected_children
.append((pool_name
, clone_name
+ str(5)))
1724 for item
in expected_children2
:
1725 for k
, v
in item
.items():
1727 item
["trash"] = False
1728 self
.check_children(expected_children
)
1729 self
.check_children2(expected_children2
)
1730 self
.check_descendants(expected_children2
)
1733 self
.rbd
.remove(ioctx
, clone_name
+ str(i
))
1734 expected_children
.remove((pool_name
, clone_name
+ str(i
)))
1735 expected_children2
.pop(0)
1736 self
.check_children(expected_children
)
1737 self
.check_children2(expected_children2
)
1738 self
.check_descendants(expected_children2
)
1740 eq(self
.image
.list_children(), [])
1741 eq(list(self
.image
.list_children2()), [])
1742 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
1744 self
.check_children([(pool_name
, self
.clone_name
)])
1745 self
.check_children2(
1746 [{'pool': pool_name
, 'pool_namespace': '',
1747 'image': self
.clone_name
, 'trash': False,
1748 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1749 self
.check_descendants(
1750 [{'pool': pool_name
, 'pool_namespace': '',
1751 'image': self
.clone_name
, 'trash': False,
1752 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1753 self
.clone
= Image(ioctx
, self
.clone_name
)
1755 def test_flatten_errors(self
):
1756 # test that we can't flatten a non-clone
1757 assert_raises(InvalidArgument
, self
.image
.flatten
)
1759 # test that we can't flatten a snapshot
1760 self
.clone
.create_snap('snap2')
1761 self
.clone
.set_snap('snap2')
1762 assert_raises(ReadOnlyImage
, self
.clone
.flatten
)
1763 self
.clone
.remove_snap('snap2')
1765 def check_flatten_with_order(self
, new_order
, stripe_unit
=None,
1769 clone_name2
= get_temp_image_name()
1770 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1771 features
, new_order
, stripe_unit
, stripe_count
)
1772 #with Image(ioctx, 'clone2') as clone:
1773 clone2
= Image(ioctx
, clone_name2
)
1775 eq(clone2
.overlap(), 0)
1777 self
.rbd
.remove(ioctx
, clone_name2
)
1779 # flatten after resizing to non-block size
1780 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1781 features
, new_order
, stripe_unit
, stripe_count
)
1782 with
Image(ioctx
, clone_name2
) as clone
:
1783 clone
.resize(IMG_SIZE
// 2 - 1)
1785 eq(0, clone
.overlap())
1786 self
.rbd
.remove(ioctx
, clone_name2
)
1788 # flatten after resizing to non-block size
1789 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1790 features
, new_order
, stripe_unit
, stripe_count
)
1791 with
Image(ioctx
, clone_name2
) as clone
:
1792 clone
.resize(IMG_SIZE
// 2 + 1)
1794 eq(clone
.overlap(), 0)
1795 self
.rbd
.remove(ioctx
, clone_name2
)
1797 def test_flatten_basic(self
):
1798 self
.check_flatten_with_order(IMG_ORDER
)
1800 def test_flatten_smaller_order(self
):
1801 self
.check_flatten_with_order(IMG_ORDER
- 2, 1048576, 1)
1803 def test_flatten_larger_order(self
):
1804 self
.check_flatten_with_order(IMG_ORDER
+ 2)
1806 def test_flatten_drops_cache(self
):
1809 clone_name2
= get_temp_image_name()
1810 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1811 features
, IMG_ORDER
)
1812 with
Image(ioctx
, clone_name2
) as clone
:
1813 with
Image(ioctx
, clone_name2
) as clone2
:
1814 # cache object non-existence
1815 data
= clone
.read(IMG_SIZE
// 2, 256)
1816 clone2_data
= clone2
.read(IMG_SIZE
// 2, 256)
1817 eq(data
, clone2_data
)
1819 assert_raises(ImageNotFound
, clone
.parent_info
)
1820 assert_raises(ImageNotFound
, clone2
.parent_info
)
1821 assert_raises(ImageNotFound
, clone
.parent_id
)
1822 assert_raises(ImageNotFound
, clone2
.parent_id
)
1823 after_flatten
= clone
.read(IMG_SIZE
// 2, 256)
1824 eq(data
, after_flatten
)
1825 after_flatten
= clone2
.read(IMG_SIZE
// 2, 256)
1826 eq(data
, after_flatten
)
1827 self
.rbd
.remove(ioctx
, clone_name2
)
1829 def test_flatten_multi_level(self
):
1830 self
.clone
.create_snap('snap2')
1831 self
.clone
.protect_snap('snap2')
1832 clone_name3
= get_temp_image_name()
1833 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1835 self
.clone
.flatten()
1836 with
Image(ioctx
, clone_name3
) as clone3
:
1838 self
.clone
.unprotect_snap('snap2')
1839 self
.clone
.remove_snap('snap2')
1840 self
.rbd
.remove(ioctx
, clone_name3
)
1842 def test_flatten_with_progress(self
):
1843 d
= {'received_callback': False}
1844 def progress_cb(current
, total
):
1845 d
['received_callback'] = True
1850 clone_name
= get_temp_image_name()
1851 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name
,
1853 with
Image(ioctx
, clone_name
) as clone
:
1854 clone
.flatten(on_progress
=progress_cb
)
1855 self
.rbd
.remove(ioctx
, clone_name
)
1856 eq(True, d
['received_callback'])
1858 def test_resize_flatten_multi_level(self
):
1859 self
.clone
.create_snap('snap2')
1860 self
.clone
.protect_snap('snap2')
1861 clone_name3
= get_temp_image_name()
1862 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1864 self
.clone
.resize(1)
1865 orig_data
= self
.image
.read(0, 256)
1866 with
Image(ioctx
, clone_name3
) as clone3
:
1867 clone3_data
= clone3
.read(0, 256)
1868 eq(orig_data
, clone3_data
)
1869 self
.clone
.flatten()
1870 with
Image(ioctx
, clone_name3
) as clone3
:
1871 clone3_data
= clone3
.read(0, 256)
1872 eq(orig_data
, clone3_data
)
1873 self
.rbd
.remove(ioctx
, clone_name3
)
1874 self
.clone
.unprotect_snap('snap2')
1875 self
.clone
.remove_snap('snap2')
1877 def test_trash_snapshot(self
):
1878 self
.image
.create_snap('snap2')
1880 clone_name
= get_temp_image_name()
1881 rados
.conf_set("rbd_default_clone_format", "2")
1882 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name
, features
)
1883 rados
.conf_set("rbd_default_clone_format", "auto")
1885 self
.image
.remove_snap('snap2')
1887 snaps
= [s
for s
in self
.image
.list_snaps() if s
['name'] != 'snap1']
1888 eq([RBD_SNAP_NAMESPACE_TYPE_TRASH
], [s
['namespace'] for s
in snaps
])
1889 eq([{'original_name' : 'snap2'}], [s
['trash'] for s
in snaps
])
1891 self
.rbd
.remove(ioctx
, clone_name
)
1892 eq([], [s
for s
in self
.image
.list_snaps() if s
['name'] != 'snap1'])
1895 @blocklist_features([RBD_FEATURE_JOURNALING
])
1896 def test_encryption_luks1(self
):
1897 data
= b
'hello world'
1901 self
.clone
.resize(image_size
)
1902 self
.clone
.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1
, "password")
1903 self
.clone
.encryption_load2(
1904 ((RBD_ENCRYPTION_FORMAT_LUKS1
, "password"),))
1905 self
.clone
.write(data
, offset
)
1906 eq(self
.clone
.read(0, 16), self
.image
.read(0, 16))
1909 @blocklist_features([RBD_FEATURE_JOURNALING
])
1910 def test_encryption_luks2(self
):
1911 data
= b
'hello world'
1915 self
.clone
.resize(image_size
)
1916 self
.clone
.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2
, "password")
1917 self
.clone
.encryption_load2(
1918 ((RBD_ENCRYPTION_FORMAT_LUKS2
, "password"),))
1919 self
.clone
.write(data
, offset
)
1920 eq(self
.clone
.read(0, 16), self
.image
.read(0, 16))
1922 class TestExclusiveLock(object):
1924 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
1925 def setup_method(self
, method
):
1927 rados2
= Rados(conffile
='')
1930 ioctx2
= rados2
.open_ioctx(pool_name
)
1933 def teardown_method(self
, method
):
1940 def test_ownership(self
):
1941 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1942 image1
.write(b
'0'*256, 0)
1943 eq(image1
.is_exclusive_lock_owner(), True)
1944 eq(image2
.is_exclusive_lock_owner(), False)
1946 def test_snapshot_leadership(self
):
1947 with
Image(ioctx
, image_name
) as image
:
1948 image
.create_snap('snap')
1949 eq(image
.is_exclusive_lock_owner(), True)
1951 with
Image(ioctx
, image_name
) as image
:
1952 image
.write(b
'0'*256, 0)
1953 eq(image
.is_exclusive_lock_owner(), True)
1954 image
.set_snap('snap')
1955 eq(image
.is_exclusive_lock_owner(), False)
1956 with
Image(ioctx
, image_name
, snapshot
='snap') as image
:
1957 eq(image
.is_exclusive_lock_owner(), False)
1959 with
Image(ioctx
, image_name
) as image
:
1960 image
.remove_snap('snap')
1962 def test_read_only_leadership(self
):
1963 with
Image(ioctx
, image_name
, read_only
=True) as image
:
1964 eq(image
.is_exclusive_lock_owner(), False)
1966 def test_follower_flatten(self
):
1967 with
Image(ioctx
, image_name
) as image
:
1968 image
.create_snap('snap')
1969 image
.protect_snap('snap')
1971 RBD().clone(ioctx
, image_name
, 'snap', ioctx
, 'clone', features
)
1972 with
Image(ioctx
, 'clone') as image1
, Image(ioctx2
, 'clone') as image2
:
1973 data
= rand_data(256)
1974 image1
.write(data
, 0)
1976 assert_raises(ImageNotFound
, image1
.parent_info
)
1977 assert_raises(ImageNotFound
, image1
.parent_id
)
1981 image2
.parent_info()
1982 except ImageNotFound
:
1987 RBD().remove(ioctx
, 'clone')
1988 with
Image(ioctx
, image_name
) as image
:
1989 image
.unprotect_snap('snap')
1990 image
.remove_snap('snap')
1992 def test_follower_resize(self
):
1993 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1994 image1
.write(b
'0'*256, 0)
1995 for new_size
in [IMG_SIZE
* 2, IMG_SIZE
// 2]:
1996 image2
.resize(new_size
);
1997 eq(new_size
, image1
.size())
1999 if new_size
== image2
.size():
2002 eq(new_size
, image2
.size())
2004 def test_follower_snap_create(self
):
2005 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
2006 image2
.create_snap('snap1')
2007 image1
.remove_snap('snap1')
2009 def test_follower_snap_rollback(self
):
2010 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
2011 image1
.create_snap('snap')
2013 assert_raises(ReadOnlyImage
, image2
.rollback_to_snap
, 'snap')
2014 image1
.rollback_to_snap('snap')
2016 image1
.remove_snap('snap')
2018 def test_follower_discard(self
):
2020 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
2021 data
= rand_data(256)
2022 image1
.write(data
, 0)
2023 image2
.discard(0, 256)
2024 eq(image1
.is_exclusive_lock_owner(), False)
2025 eq(image2
.is_exclusive_lock_owner(), True)
2026 read
= image2
.read(0, 256)
2027 if rados
.conf_get('rbd_skip_partial_discard') == 'false':
2028 eq(256 * b
'\0', read
)
2032 def test_follower_write(self
):
2033 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
2034 data
= rand_data(256)
2035 image1
.write(data
, 0)
2036 image2
.write(data
, IMG_SIZE
// 2)
2037 eq(image1
.is_exclusive_lock_owner(), False)
2038 eq(image2
.is_exclusive_lock_owner(), True)
2039 for offset
in [0, IMG_SIZE
// 2]:
2040 read
= image2
.read(offset
, 256)
2042 def test_acquire_release_lock(self
):
2043 with
Image(ioctx
, image_name
) as image
:
2044 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
2045 image
.lock_release()
2047 @pytest.mark
.skip_if_crimson
2048 def test_break_lock(self
):
2049 blocklist_rados
= Rados(conffile
='')
2050 blocklist_rados
.connect()
2052 blocklist_ioctx
= blocklist_rados
.open_ioctx(pool_name
)
2054 rados2
.conf_set('rbd_blocklist_on_break_lock', 'true')
2055 with
Image(ioctx2
, image_name
) as image
, \
2056 Image(blocklist_ioctx
, image_name
) as blocklist_image
:
2058 lock_owners
= list(image
.lock_get_owners())
2059 eq(0, len(lock_owners
))
2061 blocklist_image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
2062 assert_raises(ReadOnlyImage
, image
.lock_acquire
,
2063 RBD_LOCK_MODE_EXCLUSIVE
)
2064 lock_owners
= list(image
.lock_get_owners())
2065 eq(1, len(lock_owners
))
2066 eq(RBD_LOCK_MODE_EXCLUSIVE
, lock_owners
[0]['mode'])
2067 image
.lock_break(RBD_LOCK_MODE_EXCLUSIVE
,
2068 lock_owners
[0]['owner'])
2070 assert_raises(ConnectionShutdown
,
2071 blocklist_image
.is_exclusive_lock_owner
)
2073 blocklist_rados
.wait_for_latest_osdmap()
2074 data
= rand_data(256)
2075 assert_raises(ConnectionShutdown
,
2076 blocklist_image
.write
, data
, 0)
2078 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
2081 blocklist_image
.close()
2082 except ConnectionShutdown
:
2085 blocklist_ioctx
.close()
2087 blocklist_rados
.shutdown()
2089 class TestMirroring(object):
2092 def check_info(info
, global_id
, state
, primary
=None):
2093 eq(global_id
, info
['global_id'])
2094 eq(state
, info
['state'])
2095 if primary
is not None:
2096 eq(primary
, info
['primary'])
2098 def setup_method(self
, method
):
2100 self
.initial_mirror_mode
= self
.rbd
.mirror_mode_get(ioctx
)
2101 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
2103 self
.image
= Image(ioctx
, image_name
)
2105 def teardown_method(self
, method
):
2108 self
.rbd
.mirror_mode_set(ioctx
, self
.initial_mirror_mode
)
2110 def test_uuid(self
):
2111 mirror_uuid
= self
.rbd
.mirror_uuid_get(ioctx
)
2114 def test_site_name(self
):
2115 site_name
= "us-west-1"
2116 self
.rbd
.mirror_site_name_set(rados
, site_name
)
2117 eq(site_name
, self
.rbd
.mirror_site_name_get(rados
))
2118 self
.rbd
.mirror_site_name_set(rados
, "")
2119 eq(rados
.get_fsid(), self
.rbd
.mirror_site_name_get(rados
))
2121 def test_mirror_peer_bootstrap(self
):
2122 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
2124 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_DISABLED
)
2125 assert_raises(InvalidArgument
, self
.rbd
.mirror_peer_bootstrap_create
,
2128 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
2129 token_b64
= self
.rbd
.mirror_peer_bootstrap_create(ioctx
)
2130 token
= base64
.b64decode(token_b64
)
2131 token_dict
= json
.loads(token
)
2132 eq(sorted(['fsid', 'client_id', 'key', 'mon_host']),
2133 sorted(list(token_dict
.keys())))
2135 # requires different cluster
2136 assert_raises(InvalidArgument
, self
.rbd
.mirror_peer_bootstrap_import
,
2137 ioctx
, RBD_MIRROR_PEER_DIRECTION_RX
, token_b64
)
2139 def test_mirror_peer(self
):
2140 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
2141 site_name
= "test_site"
2142 client_name
= "test_client"
2143 uuid
= self
.rbd
.mirror_peer_add(ioctx
, site_name
, client_name
,
2144 direction
=RBD_MIRROR_PEER_DIRECTION_RX_TX
)
2148 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX
,
2149 'site_name' : site_name
,
2150 'cluster_name' : site_name
,
2152 'client_name' : client_name
,
2154 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
2155 cluster_name
= "test_cluster1"
2156 self
.rbd
.mirror_peer_set_cluster(ioctx
, uuid
, cluster_name
)
2157 client_name
= "test_client1"
2158 self
.rbd
.mirror_peer_set_client(ioctx
, uuid
, client_name
)
2161 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX
,
2162 'site_name' : cluster_name
,
2163 'cluster_name' : cluster_name
,
2165 'client_name' : client_name
,
2167 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
2170 RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST
: 'host1',
2171 RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY
: 'abc'
2173 self
.rbd
.mirror_peer_set_attributes(ioctx
, uuid
, attribs
)
2174 eq(attribs
, self
.rbd
.mirror_peer_get_attributes(ioctx
, uuid
))
2176 self
.rbd
.mirror_peer_remove(ioctx
, uuid
)
2177 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
2179 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
2180 RBD_FEATURE_JOURNALING
])
2181 def test_mirror_image(self
):
2183 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
2184 self
.image
.mirror_image_disable(True)
2185 info
= self
.image
.mirror_image_get_info()
2186 self
.check_info(info
, '', RBD_MIRROR_IMAGE_DISABLED
, False)
2188 self
.image
.mirror_image_enable()
2189 info
= self
.image
.mirror_image_get_info()
2190 global_id
= info
['global_id']
2191 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2193 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
2196 self
.image
.mirror_image_disable(True)
2197 except InvalidArgument
:
2199 eq(True, fail
) # Fails because of mirror mode pool
2201 self
.image
.mirror_image_demote()
2202 info
= self
.image
.mirror_image_get_info()
2203 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, False)
2205 entries
= dict(self
.rbd
.mirror_image_info_list(ioctx
))
2206 info
['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL
;
2207 eq(info
, entries
[self
.image
.id()])
2209 self
.image
.mirror_image_resync()
2211 self
.image
.mirror_image_promote(True)
2212 info
= self
.image
.mirror_image_get_info()
2213 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2215 entries
= dict(self
.rbd
.mirror_image_info_list(ioctx
))
2216 info
['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL
;
2217 eq(info
, entries
[self
.image
.id()])
2221 self
.image
.mirror_image_resync()
2222 except InvalidArgument
:
2224 eq(True, fail
) # Fails because it is primary
2226 status
= self
.image
.mirror_image_get_status()
2227 eq(image_name
, status
['name'])
2228 eq(False, status
['up'])
2229 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
2230 info
= status
['info']
2231 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2233 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
2234 RBD_FEATURE_JOURNALING
])
2235 def test_mirror_image_status(self
):
2236 info
= self
.image
.mirror_image_get_info()
2237 global_id
= info
['global_id']
2238 state
= info
['state']
2239 primary
= info
['primary']
2241 status
= self
.image
.mirror_image_get_status()
2242 eq(image_name
, status
['name'])
2243 eq(False, status
['up'])
2244 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
2245 eq([], status
['remote_statuses'])
2246 info
= status
['info']
2247 self
.check_info(info
, global_id
, state
, primary
)
2249 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
2252 eq(image_name
, status
['name'])
2253 eq(False, status
['up'])
2254 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
2255 info
= status
['info']
2256 self
.check_info(info
, global_id
, state
)
2258 states
= self
.rbd
.mirror_image_status_summary(ioctx
)
2259 eq([(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, 1)], states
)
2261 assert_raises(ImageNotFound
, self
.image
.mirror_image_get_instance_id
)
2262 instance_ids
= list(self
.rbd
.mirror_image_instance_id_list(ioctx
))
2263 eq(0, len(instance_ids
))
2267 self
.rbd
.create(ioctx
, image_name
+ str(i
), IMG_SIZE
, IMG_ORDER
,
2268 old_format
=False, features
=int(features
))
2269 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
2270 eq(N
+ 1, len(images
))
2272 self
.rbd
.remove(ioctx
, image_name
+ str(i
))
2274 def test_mirror_image_create_snapshot(self
):
2275 assert_raises(InvalidArgument
, self
.image
.mirror_image_create_snapshot
)
2277 peer1_uuid
= self
.rbd
.mirror_peer_add(ioctx
, "cluster1", "client")
2278 peer2_uuid
= self
.rbd
.mirror_peer_add(ioctx
, "cluster2", "client")
2279 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
2280 self
.image
.mirror_image_disable(False)
2281 self
.image
.mirror_image_enable(RBD_MIRROR_IMAGE_MODE_SNAPSHOT
)
2282 mode
= self
.image
.mirror_image_get_mode()
2283 eq(RBD_MIRROR_IMAGE_MODE_SNAPSHOT
, mode
)
2285 snaps
= list(self
.image
.list_snaps())
2288 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2289 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2291 info
= self
.image
.mirror_image_get_info()
2292 eq(True, info
['primary'])
2294 self
.rbd
.mirror_image_info_list(ioctx
,
2295 RBD_MIRROR_IMAGE_MODE_SNAPSHOT
))
2296 info
['mode'] = RBD_MIRROR_IMAGE_MODE_SNAPSHOT
;
2297 eq(info
, entries
[self
.image
.id()])
2299 snap_id
= self
.image
.mirror_image_create_snapshot(
2300 RBD_SNAP_CREATE_SKIP_QUIESCE
)
2302 snaps
= list(self
.image
.list_snaps())
2305 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2306 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2308 eq(snap
['id'], snap_id
)
2309 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2310 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2311 eq(sorted([peer1_uuid
, peer2_uuid
]),
2312 sorted(snap
['mirror']['mirror_peer_uuids']))
2314 eq(RBD_SNAP_NAMESPACE_TYPE_MIRROR
,
2315 self
.image
.snap_get_namespace_type(snap_id
))
2316 mirror_snap
= self
.image
.snap_get_mirror_namespace(snap_id
)
2317 eq(mirror_snap
, snap
['mirror'])
2319 self
.image
.mirror_image_demote()
2321 assert_raises(InvalidArgument
, self
.image
.mirror_image_create_snapshot
)
2323 snaps
= list(self
.image
.list_snaps())
2326 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2328 eq(snap
['id'], snap_id
)
2329 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2331 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2332 eq(RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED
, snap
['mirror']['state'])
2333 eq(sorted([peer1_uuid
, peer2_uuid
]),
2334 sorted(snap
['mirror']['mirror_peer_uuids']))
2336 self
.rbd
.mirror_peer_remove(ioctx
, peer1_uuid
)
2337 self
.rbd
.mirror_peer_remove(ioctx
, peer2_uuid
)
2338 self
.image
.mirror_image_promote(False)
2340 def test_aio_mirror_image_create_snapshot(self
):
2341 peer_uuid
= self
.rbd
.mirror_peer_add(ioctx
, "cluster", "client")
2342 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
2343 self
.image
.mirror_image_disable(False)
2344 self
.image
.mirror_image_enable(RBD_MIRROR_IMAGE_MODE_SNAPSHOT
)
2346 snaps
= list(self
.image
.list_snaps())
2349 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2350 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2352 # this is a list so that the local cb() can modify it
2357 comp
= self
.image
.aio_mirror_image_get_info(cb
)
2358 comp
.wait_for_complete_and_cb()
2359 assert_not_equal(info
[0], None)
2360 eq(comp
.get_return_value(), 0)
2361 eq(sys
.getrefcount(comp
), 2)
2363 global_id
= info
['global_id']
2364 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2370 comp
= self
.image
.aio_mirror_image_get_mode(cb
)
2371 comp
.wait_for_complete_and_cb()
2372 eq(comp
.get_return_value(), 0)
2373 eq(sys
.getrefcount(comp
), 2)
2374 eq(mode
[0], RBD_MIRROR_IMAGE_MODE_SNAPSHOT
)
2377 def cb(_
, _snap_id
):
2378 snap_id
[0] = _snap_id
2380 comp
= self
.image
.aio_mirror_image_create_snapshot(0, cb
)
2381 comp
.wait_for_complete_and_cb()
2382 assert_not_equal(snap_id
[0], None)
2383 eq(comp
.get_return_value(), 0)
2384 eq(sys
.getrefcount(comp
), 2)
2386 snaps
= list(self
.image
.list_snaps())
2389 eq(snap
['id'], snap_id
[0])
2390 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2391 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2392 eq([peer_uuid
], snap
['mirror']['mirror_peer_uuids'])
2394 self
.rbd
.mirror_peer_remove(ioctx
, peer_uuid
)
2396 class TestTrash(object):
2398 def setup_method(self
, method
):
2400 rados2
= Rados(conffile
='')
2403 ioctx2
= rados2
.open_ioctx(pool_name
)
2405 def teardown_method(self
, method
):
2411 def test_move(self
):
2413 with
Image(ioctx
, image_name
) as image
:
2414 image_id
= image
.id()
2416 RBD().trash_move(ioctx
, image_name
, 1000)
2417 RBD().trash_remove(ioctx
, image_id
, True)
2419 def test_purge(self
):
2421 with
Image(ioctx
, image_name
) as image
:
2422 image_name1
= image_name
2423 image_id1
= image
.id()
2426 with
Image(ioctx
, image_name
) as image
:
2427 image_name2
= image_name
2428 image_id2
= image
.id()
2430 RBD().trash_move(ioctx
, image_name1
, 0)
2431 RBD().trash_move(ioctx
, image_name2
, 1000)
2432 RBD().trash_purge(ioctx
, datetime
.now())
2434 entries
= list(RBD().trash_list(ioctx
))
2435 eq([image_id2
], [x
['id'] for x
in entries
])
2436 RBD().trash_remove(ioctx
, image_id2
, True)
2438 def test_remove_denied(self
):
2440 with
Image(ioctx
, image_name
) as image
:
2441 image_id
= image
.id()
2443 RBD().trash_move(ioctx
, image_name
, 1000)
2444 assert_raises(PermissionError
, RBD().trash_remove
, ioctx
, image_id
)
2445 RBD().trash_remove(ioctx
, image_id
, True)
2447 def test_remove(self
):
2449 with
Image(ioctx
, image_name
) as image
:
2450 image_id
= image
.id()
2452 RBD().trash_move(ioctx
, image_name
, 0)
2453 RBD().trash_remove(ioctx
, image_id
)
2455 def test_remove_with_progress(self
):
2456 d
= {'received_callback': False}
2457 def progress_cb(current
, total
):
2458 d
['received_callback'] = True
2462 with
Image(ioctx
, image_name
) as image
:
2463 image_id
= image
.id()
2465 RBD().trash_move(ioctx
, image_name
, 0)
2466 RBD().trash_remove(ioctx
, image_id
, on_progress
=progress_cb
)
2467 eq(True, d
['received_callback'])
2471 with
Image(ioctx
, image_name
) as image
:
2472 image_id
= image
.id()
2474 RBD().trash_move(ioctx
, image_name
, 1000)
2476 info
= RBD().trash_get(ioctx
, image_id
)
2477 eq(image_id
, info
['id'])
2478 eq(image_name
, info
['name'])
2479 eq('USER', info
['source'])
2480 assert(info
['deferment_end_time'] > info
['deletion_time'])
2482 RBD().trash_remove(ioctx
, image_id
, True)
2484 def test_list(self
):
2486 with
Image(ioctx
, image_name
) as image
:
2487 image_id1
= image
.id()
2488 image_name1
= image_name
2489 RBD().trash_move(ioctx
, image_name
, 1000)
2492 with
Image(ioctx
, image_name
) as image
:
2493 image_id2
= image
.id()
2494 image_name2
= image_name
2495 RBD().trash_move(ioctx
, image_name
, 1000)
2497 entries
= list(RBD().trash_list(ioctx
))
2499 if e
['id'] == image_id1
:
2500 eq(e
['name'], image_name1
)
2501 elif e
['id'] == image_id2
:
2502 eq(e
['name'], image_name2
)
2505 eq(e
['source'], 'USER')
2506 assert e
['deferment_end_time'] > e
['deletion_time']
2508 RBD().trash_remove(ioctx
, image_id1
, True)
2509 RBD().trash_remove(ioctx
, image_id2
, True)
2511 def test_restore(self
):
2513 with
Image(ioctx
, image_name
) as image
:
2514 image_id
= image
.id()
2515 RBD().trash_move(ioctx
, image_name
, 1000)
2516 RBD().trash_restore(ioctx
, image_id
, image_name
)
2519 def test_create_group():
2523 def test_rename_group():
2525 if group_name
is not None:
2527 eq(["new" + group_name
], RBD().group_list(ioctx
))
2528 RBD().group_remove(ioctx
, "new" + group_name
)
2532 def test_list_groups_empty():
2533 eq([], RBD().group_list(ioctx
))
2535 def test_list_groups(tmp_group
):
2536 eq([group_name
], RBD().group_list(ioctx
))
2538 def test_list_groups_after_removed():
2541 eq([], RBD().group_list(ioctx
))
2543 class TestGroups(object):
2545 def setup_method(self
, method
):
2549 self
.image_names
= [image_name
]
2550 self
.image
= Image(ioctx
, image_name
)
2553 snap_name
= get_temp_snap_name()
2554 self
.group
= Group(ioctx
, group_name
)
2556 def teardown_method(self
, method
):
2559 for name
in self
.image_names
:
2560 RBD().remove(ioctx
, name
)
2562 def test_group_image_add(self
):
2563 self
.group
.add_image(ioctx
, image_name
)
2565 def test_group_image_list_empty(self
):
2566 eq([], list(self
.group
.list_images()))
2568 def test_group_image_list(self
):
2569 eq([], list(self
.group
.list_images()))
2570 self
.group
.add_image(ioctx
, image_name
)
2571 eq([image_name
], [img
['name'] for img
in self
.group
.list_images()])
2573 def test_group_image_list_move_to_trash(self
):
2574 eq([], list(self
.group
.list_images()))
2575 with
Image(ioctx
, image_name
) as image
:
2576 image_id
= image
.id()
2577 self
.group
.add_image(ioctx
, image_name
)
2578 eq([image_name
], [img
['name'] for img
in self
.group
.list_images()])
2579 RBD().trash_move(ioctx
, image_name
, 0)
2580 eq([], list(self
.group
.list_images()))
2581 RBD().trash_restore(ioctx
, image_id
, image_name
)
2583 def test_group_image_many_images(self
):
2584 eq([], list(self
.group
.list_images()))
2585 self
.group
.add_image(ioctx
, image_name
)
2587 for x
in range(0, 20):
2589 self
.image_names
.append(image_name
)
2590 self
.group
.add_image(ioctx
, image_name
)
2592 self
.image_names
.sort()
2593 answer
= [img
['name'] for img
in self
.group
.list_images()]
2595 eq(self
.image_names
, answer
)
2597 def test_group_image_remove(self
):
2598 eq([], list(self
.group
.list_images()))
2599 self
.group
.add_image(ioctx
, image_name
)
2600 with
Image(ioctx
, image_name
) as image
:
2601 eq(RBD_OPERATION_FEATURE_GROUP
,
2602 image
.op_features() & RBD_OPERATION_FEATURE_GROUP
)
2603 group
= image
.group()
2604 eq(group_name
, group
['name'])
2606 eq([image_name
], [img
['name'] for img
in self
.group
.list_images()])
2607 self
.group
.remove_image(ioctx
, image_name
)
2608 eq([], list(self
.group
.list_images()))
2609 with
Image(ioctx
, image_name
) as image
:
2610 eq(0, image
.op_features() & RBD_OPERATION_FEATURE_GROUP
)
2612 def test_group_snap(self
):
2614 eq([], list(self
.group
.list_snaps()))
2615 self
.group
.create_snap(snap_name
)
2616 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2618 for snap
in self
.image
.list_snaps():
2619 eq(rbd
.RBD_SNAP_NAMESPACE_TYPE_GROUP
, snap
['namespace'])
2620 info
= snap
['group']
2621 eq(group_name
, info
['group_name'])
2622 eq(snap_name
, info
['group_snap_name'])
2624 self
.group
.remove_snap(snap_name
)
2625 eq([], list(self
.group
.list_snaps()))
2627 def test_group_snap_flags(self
):
2629 eq([], list(self
.group
.list_snaps()))
2631 self
.group
.create_snap(snap_name
, 0)
2632 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2633 self
.group
.remove_snap(snap_name
)
2635 self
.group
.create_snap(snap_name
, RBD_SNAP_CREATE_SKIP_QUIESCE
)
2636 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2637 self
.group
.remove_snap(snap_name
)
2639 self
.group
.create_snap(snap_name
, RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR
)
2640 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2641 self
.group
.remove_snap(snap_name
)
2643 assert_raises(InvalidArgument
, self
.group
.create_snap
, snap_name
,
2644 RBD_SNAP_CREATE_SKIP_QUIESCE |
2645 RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR
)
2646 eq([], list(self
.group
.list_snaps()))
2648 def test_group_snap_list_many(self
):
2650 eq([], list(self
.group
.list_snaps()))
2652 for x
in range(0, 20):
2653 snap_names
.append(snap_name
)
2654 self
.group
.create_snap(snap_name
)
2655 snap_name
= get_temp_snap_name()
2658 answer
= [snap
['name'] for snap
in self
.group
.list_snaps()]
2660 eq(snap_names
, answer
)
2662 def test_group_snap_namespace(self
):
2664 eq([], list(self
.group
.list_snaps()))
2665 self
.group
.add_image(ioctx
, image_name
)
2666 self
.group
.create_snap(snap_name
)
2667 eq(1, len([snap
['name'] for snap
in self
.image
.list_snaps()]))
2668 self
.group
.remove_image(ioctx
, image_name
)
2669 self
.group
.remove_snap(snap_name
)
2670 eq([], list(self
.group
.list_snaps()))
2672 def test_group_snap_rename(self
):
2674 new_snap_name
= "new" + snap_name
2676 eq([], list(self
.group
.list_snaps()))
2677 self
.group
.create_snap(snap_name
)
2678 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2679 self
.group
.rename_snap(snap_name
, new_snap_name
)
2680 eq([new_snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2681 self
.group
.remove_snap(new_snap_name
)
2682 eq([], list(self
.group
.list_snaps()))
2684 def test_group_snap_rollback(self
):
2685 eq([], list(self
.group
.list_images()))
2686 self
.group
.add_image(ioctx
, image_name
)
2687 with
Image(ioctx
, image_name
) as image
:
2688 image
.write(b
'\0' * 256, 0)
2689 read
= image
.read(0, 256)
2690 eq(read
, b
'\0' * 256)
2693 eq([], list(self
.group
.list_snaps()))
2694 self
.group
.create_snap(snap_name
)
2695 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2697 with
Image(ioctx
, image_name
) as image
:
2698 data
= rand_data(256)
2699 image
.write(data
, 0)
2700 read
= image
.read(0, 256)
2703 self
.group
.rollback_to_snap(snap_name
)
2704 with
Image(ioctx
, image_name
) as image
:
2705 read
= image
.read(0, 256)
2706 eq(read
, b
'\0' * 256)
2708 self
.group
.remove_image(ioctx
, image_name
)
2709 eq([], list(self
.group
.list_images()))
2710 self
.group
.remove_snap(snap_name
)
2711 eq([], list(self
.group
.list_snaps()))
2713 class TestMigration(object):
2715 def test_migration(self
):
2717 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2718 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2721 status
= RBD().migration_status(ioctx
, image_name
)
2722 eq(image_name
, status
['source_image_name'])
2723 eq(image_name
, status
['dest_image_name'])
2724 eq(RBD_IMAGE_MIGRATION_STATE_PREPARED
, status
['state'])
2726 with
Image(ioctx
, image_name
) as image
:
2727 source_spec
= image
.migration_source_spec()
2728 eq("native", source_spec
["type"])
2730 RBD().migration_execute(ioctx
, image_name
)
2731 RBD().migration_commit(ioctx
, image_name
)
2734 def test_migration_import(self
):
2736 with
Image(ioctx
, image_name
) as image
:
2737 image_id
= image
.id()
2738 image
.create_snap('snap')
2740 source_spec
= json
.dumps(
2742 'pool_id': ioctx
.get_pool_id(),
2743 'pool_namespace': '',
2744 'image_name': image_name
,
2745 'image_id': image_id
,
2746 'snap_name': 'snap'})
2747 dst_image_name
= get_temp_image_name()
2748 RBD().migration_prepare_import(source_spec
, ioctx
, dst_image_name
,
2749 features
=63, order
=23, stripe_unit
=1<<23,
2750 stripe_count
=1, data_pool
=None)
2752 status
= RBD().migration_status(ioctx
, dst_image_name
)
2753 eq('', status
['source_image_name'])
2754 eq(dst_image_name
, status
['dest_image_name'])
2755 eq(RBD_IMAGE_MIGRATION_STATE_PREPARED
, status
['state'])
2757 with
Image(ioctx
, dst_image_name
) as image
:
2758 source_spec
= image
.migration_source_spec()
2759 eq("native", source_spec
["type"])
2761 RBD().migration_execute(ioctx
, dst_image_name
)
2762 RBD().migration_commit(ioctx
, dst_image_name
)
2764 with
Image(ioctx
, image_name
) as image
:
2765 image
.remove_snap('snap')
2766 with
Image(ioctx
, dst_image_name
) as image
:
2767 image
.remove_snap('snap')
2769 RBD().remove(ioctx
, dst_image_name
)
2770 RBD().remove(ioctx
, image_name
)
2772 def test_migration_with_progress(self
):
2773 d
= {'received_callback': False}
2774 def progress_cb(current
, total
):
2775 d
['received_callback'] = True
2779 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2780 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2782 RBD().migration_execute(ioctx
, image_name
, on_progress
=progress_cb
)
2783 eq(True, d
['received_callback'])
2784 d
['received_callback'] = False
2786 RBD().migration_commit(ioctx
, image_name
, on_progress
=progress_cb
)
2787 eq(True, d
['received_callback'])
2790 def test_migrate_abort(self
):
2792 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2793 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2795 RBD().migration_abort(ioctx
, image_name
)
2798 def test_migrate_abort_with_progress(self
):
2799 d
= {'received_callback': False}
2800 def progress_cb(current
, total
):
2801 d
['received_callback'] = True
2805 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2806 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2808 RBD().migration_abort(ioctx
, image_name
, on_progress
=progress_cb
)
2809 eq(True, d
['received_callback'])