1 # vim: expandtab smarttab shiftwidth=4 softtabstop=4
13 from datetime
import datetime
, timedelta
14 from nose
import with_setup
, SkipTest
15 from nose
.tools
import eq_
as eq
, assert_raises
, assert_not_equal
16 from rados
import (Rados
,
17 LIBRADOS_OP_FLAG_FADVISE_DONTNEED
,
18 LIBRADOS_OP_FLAG_FADVISE_NOCACHE
,
19 LIBRADOS_OP_FLAG_FADVISE_RANDOM
)
20 from rbd
import (RBD
, Group
, Image
, ImageNotFound
, InvalidArgument
, ImageExists
,
21 ImageBusy
, ImageHasSnapshots
, ReadOnlyImage
,
22 FunctionNotSupported
, ArgumentOutOfRange
,
23 ECANCELED
, OperationCanceled
,
24 DiskQuotaExceeded
, ConnectionShutdown
, PermissionError
,
25 RBD_FEATURE_LAYERING
, RBD_FEATURE_STRIPINGV2
,
26 RBD_FEATURE_EXCLUSIVE_LOCK
, RBD_FEATURE_JOURNALING
,
27 RBD_FEATURE_DEEP_FLATTEN
, RBD_FEATURE_FAST_DIFF
,
28 RBD_FEATURE_OBJECT_MAP
,
29 RBD_MIRROR_MODE_DISABLED
, RBD_MIRROR_MODE_IMAGE
,
30 RBD_MIRROR_MODE_POOL
, RBD_MIRROR_IMAGE_ENABLED
,
31 RBD_MIRROR_IMAGE_DISABLED
, MIRROR_IMAGE_STATUS_STATE_UNKNOWN
,
32 RBD_MIRROR_IMAGE_MODE_JOURNAL
, RBD_MIRROR_IMAGE_MODE_SNAPSHOT
,
33 RBD_LOCK_MODE_EXCLUSIVE
, RBD_OPERATION_FEATURE_GROUP
,
34 RBD_SNAP_NAMESPACE_TYPE_TRASH
,
35 RBD_SNAP_NAMESPACE_TYPE_MIRROR
,
36 RBD_IMAGE_MIGRATION_STATE_PREPARED
, RBD_CONFIG_SOURCE_CONFIG
,
37 RBD_CONFIG_SOURCE_POOL
, RBD_CONFIG_SOURCE_IMAGE
,
38 RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST
,
39 RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY
,
40 RBD_MIRROR_PEER_DIRECTION_RX
, RBD_MIRROR_PEER_DIRECTION_RX_TX
,
41 RBD_SNAP_REMOVE_UNPROTECT
, RBD_SNAP_MIRROR_STATE_PRIMARY
,
42 RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED
,
43 RBD_SNAP_CREATE_SKIP_QUIESCE
,
44 RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR
,
45 RBD_WRITE_ZEROES_FLAG_THICK_PROVISION
,
46 RBD_ENCRYPTION_FORMAT_LUKS1
, RBD_ENCRYPTION_FORMAT_LUKS2
)
59 IMG_SIZE
= 8 << 20 # 8 MiB
60 IMG_ORDER
= 22 # 4 MiB objects
62 os
.environ
["RBD_FORCE_ALLOW_V1"] = "1"
66 rados
= Rados(conffile
='')
69 pool_name
= get_temp_pool_name()
70 rados
.create_pool(pool_name
)
72 ioctx
= rados
.open_ioctx(pool_name
)
73 RBD().pool_init(ioctx
, True)
75 features
= os
.getenv("RBD_FEATURES")
76 features
= int(features
) if features
is not None else 61
78 def teardown_module():
82 rados
.delete_pool(pool_name
)
85 def get_temp_pool_name():
88 return "test-rbd-api-" + socket
.gethostname() + '-' + str(os
.getpid()) + \
91 def get_temp_image_name():
94 return "image" + str(image_idx
)
96 def get_temp_group_name():
99 return "group" + str(group_idx
)
101 def get_temp_snap_name():
104 return "snap" + str(snap_idx
)
108 image_name
= get_temp_image_name()
109 if features
is not None:
110 RBD().create(ioctx
, image_name
, IMG_SIZE
, IMG_ORDER
, old_format
=False,
111 features
=int(features
))
113 RBD().create(ioctx
, image_name
, IMG_SIZE
, IMG_ORDER
, old_format
=True)
117 if image_name
is not None:
118 RBD().remove(ioctx
, image_name
)
122 group_name
= get_temp_group_name()
123 RBD().group_create(ioctx
, group_name
)
126 if group_name
is not None:
127 RBD().group_remove(ioctx
, group_name
)
130 new_group_name
= "new" + group_name
131 RBD().group_rename(ioctx
, group_name
, new_group_name
)
133 def require_new_format():
135 def _require_new_format(*args
, **kwargs
):
139 return fn(*args
, **kwargs
)
140 return functools
.wraps(fn
)(_require_new_format
)
143 def require_features(required_features
):
145 def _require_features(*args
, **kwargs
):
149 for feature
in required_features
:
150 if feature
& features
!= feature
:
152 return fn(*args
, **kwargs
)
153 return functools
.wraps(fn
)(_require_features
)
158 def _require_linux(*args
, **kwargs
):
159 if platform
.system() != "Linux":
161 return fn(*args
, **kwargs
)
162 return functools
.wraps(fn
)(_require_linux
)
165 def blocklist_features(blocklisted_features
):
167 def _blocklist_features(*args
, **kwargs
):
169 for feature
in blocklisted_features
:
170 if features
is not None and feature
& features
== feature
:
172 return fn(*args
, **kwargs
)
173 return functools
.wraps(fn
)(_blocklist_features
)
183 def check_default_params(format
, order
=None, features
=None, stripe_count
=None,
184 stripe_unit
=None, exception
=None):
188 for k
in ['rbd_default_format', 'rbd_default_order', 'rbd_default_features',
189 'rbd_default_stripe_count', 'rbd_default_stripe_unit']:
190 orig_vals
[k
] = rados
.conf_get(k
)
192 rados
.conf_set('rbd_default_format', str(format
))
193 if order
is not None:
194 rados
.conf_set('rbd_default_order', str(order
or 0))
195 if features
is not None:
196 rados
.conf_set('rbd_default_features', str(features
or 0))
197 if stripe_count
is not None:
198 rados
.conf_set('rbd_default_stripe_count', str(stripe_count
or 0))
199 if stripe_unit
is not None:
200 rados
.conf_set('rbd_default_stripe_unit', str(stripe_unit
or 0))
201 feature_data_pool
= 0
202 datapool
= rados
.conf_get('rbd_default_data_pool')
203 if not len(datapool
) == 0:
204 feature_data_pool
= 128
205 image_name
= get_temp_image_name()
206 if exception
is None:
207 RBD().create(ioctx
, image_name
, IMG_SIZE
, old_format
=(format
== 1))
209 with
Image(ioctx
, image_name
) as image
:
210 eq(format
== 1, image
.old_format())
212 expected_order
= int(rados
.conf_get('rbd_default_order'))
213 actual_order
= image
.stat()['order']
214 eq(expected_order
, actual_order
)
216 expected_features
= features
218 expected_features
= 0
219 elif expected_features
is None:
220 expected_features
= 61 | feature_data_pool
222 expected_features |
= feature_data_pool
223 eq(expected_features
, image
.features())
225 expected_stripe_count
= stripe_count
226 if not expected_stripe_count
or format
== 1 or \
227 features
& RBD_FEATURE_STRIPINGV2
== 0:
228 expected_stripe_count
= 1
229 eq(expected_stripe_count
, image
.stripe_count())
231 expected_stripe_unit
= stripe_unit
232 if not expected_stripe_unit
or format
== 1 or \
233 features
& RBD_FEATURE_STRIPINGV2
== 0:
234 expected_stripe_unit
= 1 << actual_order
235 eq(expected_stripe_unit
, image
.stripe_unit())
237 RBD().remove(ioctx
, image_name
)
239 assert_raises(exception
, RBD().create
, ioctx
, image_name
, IMG_SIZE
)
241 for k
, v
in orig_vals
.items():
244 def test_create_defaults():
245 # basic format 1 and 2
246 check_default_params(1)
247 check_default_params(2)
249 check_default_params(1, 0, exception
=ArgumentOutOfRange
)
250 check_default_params(2, 0, exception
=ArgumentOutOfRange
)
251 check_default_params(1, 11, exception
=ArgumentOutOfRange
)
252 check_default_params(2, 11, exception
=ArgumentOutOfRange
)
253 check_default_params(1, 65, exception
=ArgumentOutOfRange
)
254 check_default_params(2, 65, exception
=ArgumentOutOfRange
)
255 # striping and features are ignored for format 1
256 check_default_params(1, 20, 0, 1, 1)
257 check_default_params(1, 20, 3, 1, 1)
258 check_default_params(1, 20, 0, 0, 0)
259 # striping is ignored if stripingv2 is not set
260 check_default_params(2, 20, 0, 1, 1 << 20)
261 check_default_params(2, 20, RBD_FEATURE_LAYERING
, 1, 1 << 20)
262 check_default_params(2, 20, 0, 0, 0)
263 # striping with stripingv2 is fine
264 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 1, 1 << 16)
265 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 10, 1 << 20)
266 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 10, 1 << 16)
267 check_default_params(2, 20, 0, 0, 0)
268 # make sure invalid combinations of stripe unit and order are still invalid
269 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 10, 1 << 50, exception
=InvalidArgument
)
270 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 10, 100, exception
=InvalidArgument
)
271 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 0, 1, exception
=InvalidArgument
)
272 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 1, 0, exception
=InvalidArgument
)
273 # 0 stripe unit and count are still ignored
274 check_default_params(2, 22, 0, 0, 0)
276 def test_context_manager():
277 with
Rados(conffile
='') as cluster
:
278 with cluster
.open_ioctx(pool_name
) as ioctx
:
279 image_name
= get_temp_image_name()
280 RBD().create(ioctx
, image_name
, IMG_SIZE
)
281 with
Image(ioctx
, image_name
) as image
:
282 data
= rand_data(256)
284 read
= image
.read(0, 256)
285 RBD().remove(ioctx
, image_name
)
288 def test_open_read_only():
289 with
Rados(conffile
='') as cluster
:
290 with cluster
.open_ioctx(pool_name
) as ioctx
:
291 image_name
= get_temp_image_name()
292 RBD().create(ioctx
, image_name
, IMG_SIZE
)
293 data
= rand_data(256)
294 with
Image(ioctx
, image_name
) as image
:
296 image
.create_snap('snap')
297 with
Image(ioctx
, image_name
, read_only
=True) as image
:
298 read
= image
.read(0, 256)
300 assert_raises(ReadOnlyImage
, image
.write
, data
, 0)
301 assert_raises(ReadOnlyImage
, image
.create_snap
, 'test')
302 assert_raises(ReadOnlyImage
, image
.remove_snap
, 'snap')
303 assert_raises(ReadOnlyImage
, image
.rollback_to_snap
, 'snap')
304 assert_raises(ReadOnlyImage
, image
.protect_snap
, 'snap')
305 assert_raises(ReadOnlyImage
, image
.unprotect_snap
, 'snap')
306 assert_raises(ReadOnlyImage
, image
.unprotect_snap
, 'snap')
307 assert_raises(ReadOnlyImage
, image
.flatten
)
308 with
Image(ioctx
, image_name
) as image
:
309 image
.remove_snap('snap')
310 RBD().remove(ioctx
, image_name
)
315 image_name
= get_temp_image_name()
316 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
+ 'dne')
317 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
, 'snap')
319 def test_open_readonly_dne():
321 image_name
= get_temp_image_name()
322 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
+ 'dne',
324 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
, 'snap',
327 @require_new_format()
328 def test_open_by_id():
329 with
Rados(conffile
='') as cluster
:
330 with cluster
.open_ioctx(pool_name
) as ioctx
:
331 image_name
= get_temp_image_name()
332 RBD().create(ioctx
, image_name
, IMG_SIZE
)
333 with
Image(ioctx
, image_name
) as image
:
334 image_id
= image
.id()
335 with
Image(ioctx
, image_id
=image_id
) as image
:
336 eq(image
.get_name(), image_name
)
337 RBD().remove(ioctx
, image_name
)
340 with
Rados(conffile
='') as cluster
:
341 with cluster
.open_ioctx(pool_name
) as ioctx
:
342 image_name
= get_temp_image_name()
344 RBD().create(ioctx
, image_name
, IMG_SIZE
, order
)
346 # this is a list so that the open_cb() can modify it
348 def open_cb(_
, image_
):
351 comp
= RBD().aio_open_image(open_cb
, ioctx
, image_name
)
352 comp
.wait_for_complete_and_cb()
353 eq(comp
.get_return_value(), 0)
354 eq(sys
.getrefcount(comp
), 2)
355 assert_not_equal(image
[0], None)
358 eq(image
.get_name(), image_name
)
359 check_stat(image
.stat(), IMG_SIZE
, order
)
365 comp
= image
.aio_close(close_cb
)
366 comp
.wait_for_complete_and_cb()
367 eq(comp
.get_return_value(), 0)
368 eq(sys
.getrefcount(comp
), 2)
371 RBD().remove(ioctx
, image_name
)
373 def test_remove_dne():
374 assert_raises(ImageNotFound
, remove_image
)
376 def test_list_empty():
377 eq([], RBD().list(ioctx
))
379 @with_setup(create_image
, remove_image
)
381 eq([image_name
], RBD().list(ioctx
))
383 with
Image(ioctx
, image_name
) as image
:
384 image_id
= image
.id()
385 eq([{'id': image_id
, 'name': image_name
}], list(RBD().list2(ioctx
)))
387 @with_setup(create_image
)
388 def test_remove_with_progress():
389 d
= {'received_callback': False}
390 def progress_cb(current
, total
):
391 d
['received_callback'] = True
394 RBD().remove(ioctx
, image_name
, on_progress
=progress_cb
)
395 eq(True, d
['received_callback'])
397 @with_setup(create_image
)
398 def test_remove_canceled():
399 def progress_cb(current
, total
):
402 assert_raises(OperationCanceled
, RBD().remove
, ioctx
, image_name
,
403 on_progress
=progress_cb
)
405 @with_setup(create_image
, remove_image
)
408 image_name2
= get_temp_image_name()
409 rbd
.rename(ioctx
, image_name
, image_name2
)
410 eq([image_name2
], rbd
.list(ioctx
))
411 rbd
.rename(ioctx
, image_name2
, image_name
)
412 eq([image_name
], rbd
.list(ioctx
))
414 def test_pool_metadata():
416 metadata
= list(rbd
.pool_metadata_list(ioctx
))
418 assert_raises(KeyError, rbd
.pool_metadata_get
, ioctx
, "key1")
419 rbd
.pool_metadata_set(ioctx
, "key1", "value1")
420 rbd
.pool_metadata_set(ioctx
, "key2", "value2")
421 value
= rbd
.pool_metadata_get(ioctx
, "key1")
423 value
= rbd
.pool_metadata_get(ioctx
, "key2")
425 metadata
= list(rbd
.pool_metadata_list(ioctx
))
427 rbd
.pool_metadata_remove(ioctx
, "key1")
428 metadata
= list(rbd
.pool_metadata_list(ioctx
))
430 eq(metadata
[0], ("key2", "value2"))
431 rbd
.pool_metadata_remove(ioctx
, "key2")
432 assert_raises(KeyError, rbd
.pool_metadata_remove
, ioctx
, "key2")
433 metadata
= list(rbd
.pool_metadata_list(ioctx
))
438 rbd
.pool_metadata_set(ioctx
, "key" + str(i
), "X" * 1025)
439 metadata
= list(rbd
.pool_metadata_list(ioctx
))
442 rbd
.pool_metadata_remove(ioctx
, "key" + str(i
))
443 metadata
= list(rbd
.pool_metadata_list(ioctx
))
444 eq(len(metadata
), N
- i
- 1)
446 def test_config_list():
449 for option
in rbd
.config_list(ioctx
):
450 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
452 rbd
.pool_metadata_set(ioctx
, "conf_rbd_cache", "true")
454 for option
in rbd
.config_list(ioctx
):
455 if option
['name'] == "rbd_cache":
456 eq(option
['source'], RBD_CONFIG_SOURCE_POOL
)
458 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
460 rbd
.pool_metadata_remove(ioctx
, "conf_rbd_cache")
462 for option
in rbd
.config_list(ioctx
):
463 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
465 def test_pool_config_set_and_get_and_remove():
468 for option
in rbd
.config_list(ioctx
):
469 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
471 rbd
.config_set(ioctx
, "rbd_request_timed_out_seconds", "100")
472 new_value
= rbd
.config_get(ioctx
, "rbd_request_timed_out_seconds")
474 rbd
.config_remove(ioctx
, "rbd_request_timed_out_seconds")
476 for option
in rbd
.config_list(ioctx
):
477 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
479 def test_namespaces():
482 eq(False, rbd
.namespace_exists(ioctx
, 'ns1'))
483 eq([], rbd
.namespace_list(ioctx
))
484 assert_raises(ImageNotFound
, rbd
.namespace_remove
, ioctx
, 'ns1')
486 rbd
.namespace_create(ioctx
, 'ns1')
487 eq(True, rbd
.namespace_exists(ioctx
, 'ns1'))
489 assert_raises(ImageExists
, rbd
.namespace_create
, ioctx
, 'ns1')
490 eq(['ns1'], rbd
.namespace_list(ioctx
))
491 rbd
.namespace_remove(ioctx
, 'ns1')
492 eq([], rbd
.namespace_list(ioctx
))
494 @require_new_format()
495 def test_pool_stats():
499 image1
= create_image()
500 image2
= create_image()
501 image3
= create_image()
502 image4
= create_image()
503 with
Image(ioctx
, image4
) as image
:
504 image
.create_snap('snap')
507 stats
= rbd
.pool_stats_get(ioctx
)
508 eq(stats
['image_count'], 4)
509 eq(stats
['image_provisioned_bytes'], 3 * IMG_SIZE
)
510 eq(stats
['image_max_provisioned_bytes'], 4 * IMG_SIZE
)
511 eq(stats
['image_snap_count'], 1)
512 eq(stats
['trash_count'], 0)
513 eq(stats
['trash_provisioned_bytes'], 0)
514 eq(stats
['trash_max_provisioned_bytes'], 0)
515 eq(stats
['trash_snap_count'], 0)
517 rbd
.remove(ioctx
, image1
)
518 rbd
.remove(ioctx
, image2
)
519 rbd
.remove(ioctx
, image3
)
520 with
Image(ioctx
, image4
) as image
:
521 image
.remove_snap('snap')
522 rbd
.remove(ioctx
, image4
)
525 return os
.urandom(size
)
527 def check_stat(info
, size
, order
):
528 assert 'block_name_prefix' in info
529 eq(info
['size'], size
)
530 eq(info
['order'], order
)
531 eq(info
['num_objs'], size
// (1 << order
))
532 eq(info
['obj_size'], 1 << order
)
534 @require_new_format()
535 def test_features_to_string():
537 features
= RBD_FEATURE_DEEP_FLATTEN | RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_FAST_DIFF \
538 | RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP
539 expected_features_string
= "deep-flatten,exclusive-lock,fast-diff,layering,object-map"
540 features_string
= rbd
.features_to_string(features
)
541 eq(expected_features_string
, features_string
)
543 features
= RBD_FEATURE_LAYERING
544 features_string
= rbd
.features_to_string(features
)
545 eq(features_string
, "layering")
548 assert_raises(InvalidArgument
, rbd
.features_to_string
, features
)
550 @require_new_format()
551 def test_features_from_string():
553 features_string
= "deep-flatten,exclusive-lock,fast-diff,layering,object-map"
554 expected_features_bitmask
= RBD_FEATURE_DEEP_FLATTEN | RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_FAST_DIFF \
555 | RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP
556 features
= rbd
.features_from_string(features_string
)
557 eq(expected_features_bitmask
, features
)
559 features_string
= "layering"
560 features
= rbd
.features_from_string(features_string
)
561 eq(features
, RBD_FEATURE_LAYERING
)
563 class TestImage(object):
568 self
.image
= Image(ioctx
, image_name
)
575 @require_new_format()
576 @blocklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
577 def test_update_features(self
):
578 features
= self
.image
.features()
579 self
.image
.update_features(RBD_FEATURE_EXCLUSIVE_LOCK
, True)
580 eq(features | RBD_FEATURE_EXCLUSIVE_LOCK
, self
.image
.features())
582 @require_features([RBD_FEATURE_STRIPINGV2
])
583 def test_create_with_params(self
):
585 image_name
= get_temp_image_name()
587 stripe_unit
= 1 << 20
589 self
.rbd
.create(ioctx
, image_name
, IMG_SIZE
, order
,
590 False, features
, stripe_unit
, stripe_count
)
591 image
= Image(ioctx
, image_name
)
593 check_stat(info
, IMG_SIZE
, order
)
594 eq(image
.features(), features
)
595 eq(image
.stripe_unit(), stripe_unit
)
596 eq(image
.stripe_count(), stripe_count
)
598 RBD().remove(ioctx
, image_name
)
600 @require_new_format()
602 assert_not_equal(b
'', self
.image
.id())
604 def test_block_name_prefix(self
):
605 assert_not_equal(b
'', self
.image
.block_name_prefix())
607 def test_create_timestamp(self
):
608 timestamp
= self
.image
.create_timestamp()
609 assert_not_equal(0, timestamp
.year
)
610 assert_not_equal(1970, timestamp
.year
)
612 def test_access_timestamp(self
):
613 timestamp
= self
.image
.access_timestamp()
614 assert_not_equal(0, timestamp
.year
)
615 assert_not_equal(1970, timestamp
.year
)
617 def test_modify_timestamp(self
):
618 timestamp
= self
.image
.modify_timestamp()
619 assert_not_equal(0, timestamp
.year
)
620 assert_not_equal(1970, timestamp
.year
)
622 def test_invalidate_cache(self
):
623 self
.image
.write(b
'abc', 0)
624 eq(b
'abc', self
.image
.read(0, 3))
625 self
.image
.invalidate_cache()
626 eq(b
'abc', self
.image
.read(0, 3))
629 info
= self
.image
.stat()
630 check_stat(info
, IMG_SIZE
, IMG_ORDER
)
632 def test_flags(self
):
633 flags
= self
.image
.flags()
636 def test_image_auto_close(self
):
637 image
= Image(ioctx
, image_name
)
639 def test_use_after_close(self
):
641 assert_raises(InvalidArgument
, self
.image
.stat
)
643 def test_write(self
):
644 data
= rand_data(256)
645 self
.image
.write(data
, 0)
647 def test_write_with_fadvise_flags(self
):
648 data
= rand_data(256)
649 self
.image
.write(data
, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED
)
650 self
.image
.write(data
, 0, LIBRADOS_OP_FLAG_FADVISE_NOCACHE
)
652 def test_write_zeroes(self
):
653 data
= rand_data(256)
654 self
.image
.write(data
, 0)
655 self
.image
.write_zeroes(0, 256)
656 eq(self
.image
.read(256, 256), b
'\0' * 256)
657 check_diff(self
.image
, 0, IMG_SIZE
, None, [])
659 def test_write_zeroes_thick_provision(self
):
660 data
= rand_data(256)
661 self
.image
.write(data
, 0)
662 self
.image
.write_zeroes(0, 256, RBD_WRITE_ZEROES_FLAG_THICK_PROVISION
)
663 eq(self
.image
.read(256, 256), b
'\0' * 256)
664 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 256, True)])
667 data
= self
.image
.read(0, 20)
670 def test_read_with_fadvise_flags(self
):
671 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED
)
673 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM
)
676 def test_large_write(self
):
677 data
= rand_data(IMG_SIZE
)
678 self
.image
.write(data
, 0)
680 def test_large_read(self
):
681 data
= self
.image
.read(0, IMG_SIZE
)
682 eq(data
, b
'\0' * IMG_SIZE
)
684 def test_write_read(self
):
685 data
= rand_data(256)
687 self
.image
.write(data
, offset
)
688 read
= self
.image
.read(offset
, 256)
691 def test_read_bad_offset(self
):
692 assert_raises(InvalidArgument
, self
.image
.read
, IMG_SIZE
+ 1, IMG_SIZE
)
694 def test_resize(self
):
695 new_size
= IMG_SIZE
* 2
696 self
.image
.resize(new_size
)
697 info
= self
.image
.stat()
698 check_stat(info
, new_size
, IMG_ORDER
)
700 def test_resize_allow_shrink_False(self
):
701 new_size
= IMG_SIZE
* 2
702 self
.image
.resize(new_size
)
703 info
= self
.image
.stat()
704 check_stat(info
, new_size
, IMG_ORDER
)
705 assert_raises(InvalidArgument
, self
.image
.resize
, IMG_SIZE
, False)
708 eq(IMG_SIZE
, self
.image
.size())
709 self
.image
.create_snap('snap1')
710 new_size
= IMG_SIZE
* 2
711 self
.image
.resize(new_size
)
712 eq(new_size
, self
.image
.size())
713 self
.image
.create_snap('snap2')
714 self
.image
.set_snap('snap2')
715 eq(new_size
, self
.image
.size())
716 self
.image
.set_snap('snap1')
717 eq(IMG_SIZE
, self
.image
.size())
718 self
.image
.set_snap(None)
719 eq(new_size
, self
.image
.size())
720 self
.image
.remove_snap('snap1')
721 self
.image
.remove_snap('snap2')
723 def test_resize_down(self
):
724 new_size
= IMG_SIZE
// 2
725 data
= rand_data(256)
726 self
.image
.write(data
, IMG_SIZE
// 2);
727 self
.image
.resize(new_size
)
728 self
.image
.resize(IMG_SIZE
)
729 read
= self
.image
.read(IMG_SIZE
// 2, 256)
730 eq(b
'\0' * 256, read
)
732 def test_resize_bytes(self
):
733 new_size
= IMG_SIZE
// 2 - 5
734 data
= rand_data(256)
735 self
.image
.write(data
, IMG_SIZE
// 2 - 10);
736 self
.image
.resize(new_size
)
737 self
.image
.resize(IMG_SIZE
)
738 read
= self
.image
.read(IMG_SIZE
// 2 - 10, 5)
740 read
= self
.image
.read(IMG_SIZE
// 2 - 5, 251)
741 eq(b
'\0' * 251, read
)
743 def _test_copy(self
, features
=None, order
=None, stripe_unit
=None,
746 data
= rand_data(256)
747 self
.image
.write(data
, 256)
748 image_name
= get_temp_image_name()
750 self
.image
.copy(ioctx
, image_name
)
752 self
.image
.copy(ioctx
, image_name
, features
)
753 elif stripe_unit
is None:
754 self
.image
.copy(ioctx
, image_name
, features
, order
)
755 elif stripe_count
is None:
756 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
)
758 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
,
760 assert_raises(ImageExists
, self
.image
.copy
, ioctx
, image_name
)
761 copy
= Image(ioctx
, image_name
)
762 copy_data
= copy
.read(256, 256)
764 self
.rbd
.remove(ioctx
, image_name
)
770 def test_copy2(self
):
771 self
._test
_copy
(self
.image
.features(), self
.image
.stat()['order'])
773 @require_features([RBD_FEATURE_STRIPINGV2
])
774 def test_copy3(self
):
776 self
._test
_copy
(features
, self
.image
.stat()['order'],
777 self
.image
.stripe_unit(), self
.image
.stripe_count())
779 def test_deep_copy(self
):
782 self
.image
.write(b
'a' * 256, 0)
783 self
.image
.create_snap('snap1')
784 self
.image
.write(b
'b' * 256, 0)
785 dst_name
= get_temp_image_name()
786 self
.image
.deep_copy(ioctx
, dst_name
, features
=features
,
787 order
=self
.image
.stat()['order'],
788 stripe_unit
=self
.image
.stripe_unit(),
789 stripe_count
=self
.image
.stripe_count(),
791 self
.image
.remove_snap('snap1')
792 with
Image(ioctx
, dst_name
, 'snap1') as copy
:
793 copy_data
= copy
.read(0, 256)
794 eq(b
'a' * 256, copy_data
)
795 with
Image(ioctx
, dst_name
) as copy
:
796 copy_data
= copy
.read(0, 256)
797 eq(b
'b' * 256, copy_data
)
798 copy
.remove_snap('snap1')
799 self
.rbd
.remove(ioctx
, dst_name
)
801 @require_features([RBD_FEATURE_LAYERING
])
802 def test_deep_copy_clone(self
):
805 self
.image
.write(b
'a' * 256, 0)
806 self
.image
.create_snap('snap1')
807 self
.image
.write(b
'b' * 256, 0)
808 self
.image
.protect_snap('snap1')
809 clone_name
= get_temp_image_name()
810 dst_name
= get_temp_image_name()
811 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name
)
812 with
Image(ioctx
, clone_name
) as child
:
813 child
.create_snap('snap1')
814 child
.deep_copy(ioctx
, dst_name
, features
=features
,
815 order
=self
.image
.stat()['order'],
816 stripe_unit
=self
.image
.stripe_unit(),
817 stripe_count
=self
.image
.stripe_count(),
819 child
.remove_snap('snap1')
821 with
Image(ioctx
, dst_name
) as copy
:
822 copy_data
= copy
.read(0, 256)
823 eq(b
'a' * 256, copy_data
)
824 copy
.remove_snap('snap1')
825 self
.rbd
.remove(ioctx
, dst_name
)
826 self
.rbd
.remove(ioctx
, clone_name
)
827 self
.image
.unprotect_snap('snap1')
828 self
.image
.remove_snap('snap1')
830 def test_create_snap(self
):
832 self
.image
.create_snap('snap1')
833 read
= self
.image
.read(0, 256)
834 eq(read
, b
'\0' * 256)
835 data
= rand_data(256)
836 self
.image
.write(data
, 0)
837 read
= self
.image
.read(0, 256)
839 at_snapshot
= Image(ioctx
, image_name
, 'snap1')
840 snap_data
= at_snapshot
.read(0, 256)
842 eq(snap_data
, b
'\0' * 256)
843 self
.image
.remove_snap('snap1')
845 def test_create_snap_exists(self
):
846 self
.image
.create_snap('snap1')
847 assert_raises(ImageExists
, self
.image
.create_snap
, 'snap1')
848 self
.image
.remove_snap('snap1')
850 def test_create_snap_flags(self
):
851 self
.image
.create_snap('snap1', 0)
852 self
.image
.remove_snap('snap1')
853 self
.image
.create_snap('snap1', RBD_SNAP_CREATE_SKIP_QUIESCE
)
854 self
.image
.remove_snap('snap1')
855 self
.image
.create_snap('snap1', RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR
)
856 self
.image
.remove_snap('snap1')
858 def test_list_snaps(self
):
859 eq([], list(self
.image
.list_snaps()))
860 self
.image
.create_snap('snap1')
861 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
862 self
.image
.create_snap('snap2')
863 eq(['snap1', 'snap2'], [snap
['name'] for snap
in self
.image
.list_snaps()])
864 self
.image
.remove_snap('snap1')
865 self
.image
.remove_snap('snap2')
867 def test_list_snaps_iterator_auto_close(self
):
868 self
.image
.create_snap('snap1')
869 self
.image
.list_snaps()
870 self
.image
.remove_snap('snap1')
872 def test_remove_snap(self
):
873 eq([], list(self
.image
.list_snaps()))
874 self
.image
.create_snap('snap1')
875 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
876 self
.image
.remove_snap('snap1')
877 eq([], list(self
.image
.list_snaps()))
879 def test_remove_snap_not_found(self
):
880 assert_raises(ImageNotFound
, self
.image
.remove_snap
, 'snap1')
882 @require_features([RBD_FEATURE_LAYERING
])
883 def test_remove_snap2(self
):
884 self
.image
.create_snap('snap1')
885 self
.image
.protect_snap('snap1')
886 assert(self
.image
.is_protected_snap('snap1'))
887 self
.image
.remove_snap2('snap1', RBD_SNAP_REMOVE_UNPROTECT
)
888 eq([], list(self
.image
.list_snaps()))
890 def test_remove_snap_by_id(self
):
891 eq([], list(self
.image
.list_snaps()))
892 self
.image
.create_snap('snap1')
893 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
894 for snap
in self
.image
.list_snaps():
896 self
.image
.remove_snap_by_id(snap_id
)
897 eq([], list(self
.image
.list_snaps()))
899 def test_rename_snap(self
):
900 eq([], list(self
.image
.list_snaps()))
901 self
.image
.create_snap('snap1')
902 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
903 self
.image
.rename_snap("snap1", "snap1-rename")
904 eq(['snap1-rename'], [snap
['name'] for snap
in self
.image
.list_snaps()])
905 self
.image
.remove_snap('snap1-rename')
906 eq([], list(self
.image
.list_snaps()))
908 @require_features([RBD_FEATURE_LAYERING
])
909 def test_protect_snap(self
):
910 self
.image
.create_snap('snap1')
911 assert(not self
.image
.is_protected_snap('snap1'))
912 self
.image
.protect_snap('snap1')
913 assert(self
.image
.is_protected_snap('snap1'))
914 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
915 self
.image
.unprotect_snap('snap1')
916 assert(not self
.image
.is_protected_snap('snap1'))
917 self
.image
.remove_snap('snap1')
918 assert_raises(ImageNotFound
, self
.image
.unprotect_snap
, 'snap1')
919 assert_raises(ImageNotFound
, self
.image
.is_protected_snap
, 'snap1')
921 def test_snap_exists(self
):
922 self
.image
.create_snap('snap1')
923 eq(self
.image
.snap_exists('snap1'), True)
924 self
.image
.remove_snap('snap1')
925 eq(self
.image
.snap_exists('snap1'), False)
927 def test_snap_timestamp(self
):
928 self
.image
.create_snap('snap1')
929 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
930 for snap
in self
.image
.list_snaps():
932 time
= self
.image
.get_snap_timestamp(snap_id
)
933 assert_not_equal(b
'', time
.year
)
934 assert_not_equal(0, time
.year
)
935 assert_not_equal(time
.year
, '1970')
936 self
.image
.remove_snap('snap1')
938 def test_limit_snaps(self
):
939 self
.image
.set_snap_limit(2)
940 eq(2, self
.image
.get_snap_limit())
941 self
.image
.create_snap('snap1')
942 self
.image
.create_snap('snap2')
943 assert_raises(DiskQuotaExceeded
, self
.image
.create_snap
, 'snap3')
944 self
.image
.remove_snap_limit()
945 self
.image
.create_snap('snap3')
947 self
.image
.remove_snap('snap1')
948 self
.image
.remove_snap('snap2')
949 self
.image
.remove_snap('snap3')
951 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
952 def test_remove_with_exclusive_lock(self
):
953 assert_raises(ImageBusy
, remove_image
)
955 @blocklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
956 def test_remove_with_snap(self
):
957 self
.image
.create_snap('snap1')
958 assert_raises(ImageHasSnapshots
, remove_image
)
959 self
.image
.remove_snap('snap1')
961 @blocklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
962 def test_remove_with_watcher(self
):
963 data
= rand_data(256)
964 self
.image
.write(data
, 0)
965 assert_raises(ImageBusy
, remove_image
)
966 read
= self
.image
.read(0, 256)
969 def test_rollback_to_snap(self
):
970 self
.image
.write(b
'\0' * 256, 0)
971 self
.image
.create_snap('snap1')
972 read
= self
.image
.read(0, 256)
973 eq(read
, b
'\0' * 256)
974 data
= rand_data(256)
975 self
.image
.write(data
, 0)
976 read
= self
.image
.read(0, 256)
978 self
.image
.rollback_to_snap('snap1')
979 read
= self
.image
.read(0, 256)
980 eq(read
, b
'\0' * 256)
981 self
.image
.remove_snap('snap1')
983 def test_rollback_to_snap_sparse(self
):
984 self
.image
.create_snap('snap1')
985 read
= self
.image
.read(0, 256)
986 eq(read
, b
'\0' * 256)
987 data
= rand_data(256)
988 self
.image
.write(data
, 0)
989 read
= self
.image
.read(0, 256)
991 self
.image
.rollback_to_snap('snap1')
992 read
= self
.image
.read(0, 256)
993 eq(read
, b
'\0' * 256)
994 self
.image
.remove_snap('snap1')
996 def test_rollback_with_resize(self
):
997 read
= self
.image
.read(0, 256)
998 eq(read
, b
'\0' * 256)
999 data
= rand_data(256)
1000 self
.image
.write(data
, 0)
1001 self
.image
.create_snap('snap1')
1002 read
= self
.image
.read(0, 256)
1004 new_size
= IMG_SIZE
* 2
1005 self
.image
.resize(new_size
)
1006 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
1007 self
.image
.write(data
, new_size
- 256)
1008 self
.image
.create_snap('snap2')
1009 read
= self
.image
.read(new_size
- 256, 256)
1011 self
.image
.rollback_to_snap('snap1')
1012 check_stat(self
.image
.stat(), IMG_SIZE
, IMG_ORDER
)
1013 assert_raises(InvalidArgument
, self
.image
.read
, new_size
- 256, 256)
1014 self
.image
.rollback_to_snap('snap2')
1015 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
1016 read
= self
.image
.read(new_size
- 256, 256)
1018 self
.image
.remove_snap('snap1')
1019 self
.image
.remove_snap('snap2')
1021 def test_set_snap(self
):
1022 self
.image
.write(b
'\0' * 256, 0)
1023 self
.image
.create_snap('snap1')
1024 read
= self
.image
.read(0, 256)
1025 eq(read
, b
'\0' * 256)
1026 data
= rand_data(256)
1027 self
.image
.write(data
, 0)
1028 read
= self
.image
.read(0, 256)
1030 self
.image
.set_snap('snap1')
1031 read
= self
.image
.read(0, 256)
1032 eq(read
, b
'\0' * 256)
1033 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
1034 self
.image
.remove_snap('snap1')
1036 def test_set_no_snap(self
):
1037 self
.image
.write(b
'\0' * 256, 0)
1038 self
.image
.create_snap('snap1')
1039 read
= self
.image
.read(0, 256)
1040 eq(read
, b
'\0' * 256)
1041 data
= rand_data(256)
1042 self
.image
.write(data
, 0)
1043 read
= self
.image
.read(0, 256)
1045 self
.image
.set_snap('snap1')
1046 read
= self
.image
.read(0, 256)
1047 eq(read
, b
'\0' * 256)
1048 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
1049 self
.image
.set_snap(None)
1050 read
= self
.image
.read(0, 256)
1052 self
.image
.remove_snap('snap1')
1054 def test_set_snap_by_id(self
):
1055 self
.image
.write(b
'\0' * 256, 0)
1056 self
.image
.create_snap('snap1')
1057 read
= self
.image
.read(0, 256)
1058 eq(read
, b
'\0' * 256)
1059 data
= rand_data(256)
1060 self
.image
.write(data
, 0)
1061 read
= self
.image
.read(0, 256)
1063 snaps
= list(self
.image
.list_snaps())
1064 self
.image
.set_snap_by_id(snaps
[0]['id'])
1065 read
= self
.image
.read(0, 256)
1066 eq(read
, b
'\0' * 256)
1067 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
1068 self
.image
.set_snap_by_id(None)
1069 read
= self
.image
.read(0, 256)
1071 self
.image
.remove_snap('snap1')
1073 def test_snap_get_name(self
):
1074 eq([], list(self
.image
.list_snaps()))
1075 self
.image
.create_snap('snap1')
1076 self
.image
.create_snap('snap2')
1077 self
.image
.create_snap('snap3')
1079 for snap
in self
.image
.list_snaps():
1080 expected_snap_name
= self
.image
.snap_get_name(snap
['id'])
1081 eq(expected_snap_name
, snap
['name'])
1082 self
.image
.remove_snap('snap1')
1083 self
.image
.remove_snap('snap2')
1084 self
.image
.remove_snap('snap3')
1085 eq([], list(self
.image
.list_snaps()))
1087 assert_raises(ImageNotFound
, self
.image
.snap_get_name
, 1)
1089 def test_snap_get_id(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_id
= self
.image
.snap_get_id(snap
['name'])
1097 eq(expected_snap_id
, snap
['id'])
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_id
, 'snap1')
1105 def test_set_snap_sparse(self
):
1106 self
.image
.create_snap('snap1')
1107 read
= self
.image
.read(0, 256)
1108 eq(read
, b
'\0' * 256)
1109 data
= rand_data(256)
1110 self
.image
.write(data
, 0)
1111 read
= self
.image
.read(0, 256)
1113 self
.image
.set_snap('snap1')
1114 read
= self
.image
.read(0, 256)
1115 eq(read
, b
'\0' * 256)
1116 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
1117 self
.image
.remove_snap('snap1')
1119 def test_many_snaps(self
):
1121 for i
in range(num_snaps
):
1122 self
.image
.create_snap(str(i
))
1123 snaps
= sorted(self
.image
.list_snaps(),
1124 key
=lambda snap
: int(snap
['name']))
1125 eq(len(snaps
), num_snaps
)
1126 for i
, snap
in enumerate(snaps
):
1127 eq(snap
['size'], IMG_SIZE
)
1128 eq(snap
['name'], str(i
))
1129 for i
in range(num_snaps
):
1130 self
.image
.remove_snap(str(i
))
1132 def test_set_snap_deleted(self
):
1133 self
.image
.write(b
'\0' * 256, 0)
1134 self
.image
.create_snap('snap1')
1135 read
= self
.image
.read(0, 256)
1136 eq(read
, b
'\0' * 256)
1137 data
= rand_data(256)
1138 self
.image
.write(data
, 0)
1139 read
= self
.image
.read(0, 256)
1141 self
.image
.set_snap('snap1')
1142 self
.image
.remove_snap('snap1')
1143 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
1144 self
.image
.set_snap(None)
1145 read
= self
.image
.read(0, 256)
1148 def test_set_snap_recreated(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 self
.image
.create_snap('snap1')
1160 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
1161 self
.image
.set_snap(None)
1162 read
= self
.image
.read(0, 256)
1164 self
.image
.remove_snap('snap1')
1166 def test_lock_unlock(self
):
1167 assert_raises(ImageNotFound
, self
.image
.unlock
, '')
1168 self
.image
.lock_exclusive('')
1169 assert_raises(ImageExists
, self
.image
.lock_exclusive
, '')
1170 assert_raises(ImageBusy
, self
.image
.lock_exclusive
, 'test')
1171 assert_raises(ImageExists
, self
.image
.lock_shared
, '', '')
1172 assert_raises(ImageBusy
, self
.image
.lock_shared
, 'foo', '')
1173 self
.image
.unlock('')
1175 def test_list_lockers(self
):
1176 eq([], self
.image
.list_lockers())
1177 self
.image
.lock_exclusive('test')
1178 lockers
= self
.image
.list_lockers()
1179 eq(1, len(lockers
['lockers']))
1180 _
, cookie
, _
= lockers
['lockers'][0]
1182 eq('', lockers
['tag'])
1183 assert lockers
['exclusive']
1184 self
.image
.unlock('test')
1185 eq([], self
.image
.list_lockers())
1188 for i
in range(num_shared
):
1189 self
.image
.lock_shared(str(i
), 'tag')
1190 lockers
= self
.image
.list_lockers()
1191 eq('tag', lockers
['tag'])
1192 assert not lockers
['exclusive']
1193 eq(num_shared
, len(lockers
['lockers']))
1194 cookies
= sorted(map(lambda x
: x
[1], lockers
['lockers']))
1195 for i
in range(num_shared
):
1196 eq(str(i
), cookies
[i
])
1197 self
.image
.unlock(str(i
))
1198 eq([], self
.image
.list_lockers())
1200 def test_diff_iterate(self
):
1201 check_diff(self
.image
, 0, IMG_SIZE
, None, [])
1202 self
.image
.write(b
'a' * 256, 0)
1203 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 256, True)])
1204 self
.image
.write(b
'b' * 256, 256)
1205 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
1206 self
.image
.discard(128, 256)
1207 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
1209 self
.image
.create_snap('snap1')
1210 self
.image
.discard(0, 1 << IMG_ORDER
)
1211 self
.image
.create_snap('snap2')
1212 self
.image
.set_snap('snap2')
1213 check_diff(self
.image
, 0, IMG_SIZE
, 'snap1', [(0, 512, False)])
1214 self
.image
.remove_snap('snap1')
1215 self
.image
.remove_snap('snap2')
1217 def test_aio_read(self
):
1218 # this is a list so that the local cb() can modify it
1223 # test1: success case
1224 comp
= self
.image
.aio_read(0, 20, cb
)
1225 comp
.wait_for_complete_and_cb()
1226 eq(retval
[0], b
'\0' * 20)
1227 eq(comp
.get_return_value(), 20)
1228 eq(sys
.getrefcount(comp
), 2)
1232 comp
= self
.image
.aio_read(IMG_SIZE
, 20, cb
)
1233 comp
.wait_for_complete_and_cb()
1235 assert(comp
.get_return_value() < 0)
1236 eq(sys
.getrefcount(comp
), 2)
1238 def test_aio_write(self
):
1241 retval
[0] = comp
.get_return_value()
1243 data
= rand_data(256)
1244 comp
= self
.image
.aio_write(data
, 256, cb
)
1245 comp
.wait_for_complete_and_cb()
1247 eq(comp
.get_return_value(), 0)
1248 eq(sys
.getrefcount(comp
), 2)
1249 eq(self
.image
.read(256, 256), data
)
1251 def test_aio_discard(self
):
1254 retval
[0] = comp
.get_return_value()
1256 data
= rand_data(256)
1257 self
.image
.write(data
, 0)
1258 comp
= self
.image
.aio_discard(0, 256, cb
)
1259 comp
.wait_for_complete_and_cb()
1261 eq(comp
.get_return_value(), 0)
1262 eq(sys
.getrefcount(comp
), 2)
1263 eq(self
.image
.read(256, 256), b
'\0' * 256)
1265 def test_aio_write_zeroes(self
):
1268 retval
[0] = comp
.get_return_value()
1270 data
= rand_data(256)
1271 self
.image
.write(data
, 0)
1272 comp
= self
.image
.aio_write_zeroes(0, 256, cb
)
1273 comp
.wait_for_complete_and_cb()
1275 eq(comp
.get_return_value(), 0)
1276 eq(sys
.getrefcount(comp
), 2)
1277 eq(self
.image
.read(256, 256), b
'\0' * 256)
1279 def test_aio_flush(self
):
1282 retval
[0] = comp
.get_return_value()
1284 comp
= self
.image
.aio_flush(cb
)
1285 comp
.wait_for_complete_and_cb()
1287 eq(sys
.getrefcount(comp
), 2)
1289 def test_metadata(self
):
1290 metadata
= list(self
.image
.metadata_list())
1291 eq(len(metadata
), 0)
1292 assert_raises(KeyError, self
.image
.metadata_get
, "key1")
1293 self
.image
.metadata_set("key1", "value1")
1294 self
.image
.metadata_set("key2", "value2")
1295 value
= self
.image
.metadata_get("key1")
1297 value
= self
.image
.metadata_get("key2")
1299 metadata
= list(self
.image
.metadata_list())
1300 eq(len(metadata
), 2)
1301 self
.image
.metadata_remove("key1")
1302 metadata
= list(self
.image
.metadata_list())
1303 eq(len(metadata
), 1)
1304 eq(metadata
[0], ("key2", "value2"))
1305 self
.image
.metadata_remove("key2")
1306 assert_raises(KeyError, self
.image
.metadata_remove
, "key2")
1307 metadata
= list(self
.image
.metadata_list())
1308 eq(len(metadata
), 0)
1312 self
.image
.metadata_set("key" + str(i
), "X" * 1025)
1313 metadata
= list(self
.image
.metadata_list())
1314 eq(len(metadata
), N
)
1316 self
.image
.metadata_remove("key" + str(i
))
1317 metadata
= list(self
.image
.metadata_list())
1318 eq(len(metadata
), N
- i
- 1)
1320 def test_watchers_list(self
):
1321 watchers
= list(self
.image
.watchers_list())
1322 # The image is open (in r/w mode) from setup, so expect there to be one
1324 eq(len(watchers
), 1)
1326 def test_config_list(self
):
1327 with
Image(ioctx
, image_name
) as image
:
1328 for option
in image
.config_list():
1329 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1331 image
.metadata_set("conf_rbd_cache", "true")
1333 for option
in image
.config_list():
1334 if option
['name'] == "rbd_cache":
1335 eq(option
['source'], RBD_CONFIG_SOURCE_IMAGE
)
1337 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1339 image
.metadata_remove("conf_rbd_cache")
1341 for option
in image
.config_list():
1342 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1344 def test_image_config_set_and_get_and_remove(self
):
1345 with
Image(ioctx
, image_name
) as image
:
1346 for option
in image
.config_list():
1347 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1349 image
.config_set("rbd_request_timed_out_seconds", "100")
1350 modify_value
= image
.config_get("rbd_request_timed_out_seconds")
1351 eq(modify_value
, '100')
1353 image
.config_remove("rbd_request_timed_out_seconds")
1355 for option
in image
.config_list():
1356 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1358 def test_sparsify(self
):
1359 assert_raises(InvalidArgument
, self
.image
.sparsify
, 16)
1360 self
.image
.sparsify(4096)
1363 @blocklist_features([RBD_FEATURE_JOURNALING
])
1364 def test_encryption_luks1(self
):
1365 data
= b
'hello world'
1369 with
Image(ioctx
, image_name
) as image
:
1370 image
.resize(image_size
)
1371 image
.write(data
, offset
)
1372 image
.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1
, "password")
1373 assert_not_equal(data
, image
.read(offset
, len(data
)))
1374 with
Image(ioctx
, image_name
) as image
:
1375 image
.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS1
, "password")
1376 assert_not_equal(data
, image
.read(offset
, len(data
)))
1377 image
.write(data
, offset
)
1378 with
Image(ioctx
, image_name
) as image
:
1379 image
.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS1
, "password")
1380 eq(data
, image
.read(offset
, len(data
)))
1383 @blocklist_features([RBD_FEATURE_JOURNALING
])
1384 def test_encryption_luks2(self
):
1385 data
= b
'hello world'
1387 image_size
= 256<<20
1389 with
Image(ioctx
, image_name
) as image
:
1390 image
.resize(image_size
)
1391 image
.write(data
, offset
)
1392 image
.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2
, "password")
1393 assert_not_equal(data
, image
.read(offset
, len(data
)))
1394 with
Image(ioctx
, image_name
) as image
:
1395 image
.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS2
, "password")
1396 assert_not_equal(data
, image
.read(offset
, len(data
)))
1397 image
.write(data
, offset
)
1398 with
Image(ioctx
, image_name
) as image
:
1399 image
.encryption_load(RBD_ENCRYPTION_FORMAT_LUKS2
, "password")
1400 eq(data
, image
.read(offset
, len(data
)))
1403 class TestImageId(object):
1408 self
.image
= Image(ioctx
, image_name
)
1409 self
.image2
= Image(ioctx
, None, None, False, self
.image
.id())
1418 def test_read(self
):
1419 data
= self
.image2
.read(0, 20)
1420 eq(data
, b
'\0' * 20)
1422 def test_write(self
):
1423 data
= rand_data(256)
1424 self
.image2
.write(data
, 0)
1426 def test_resize(self
):
1427 new_size
= IMG_SIZE
* 2
1428 self
.image2
.resize(new_size
)
1429 info
= self
.image2
.stat()
1430 check_stat(info
, new_size
, IMG_ORDER
)
1432 def check_diff(image
, offset
, length
, from_snapshot
, expected
):
1434 def cb(offset
, length
, exists
):
1435 extents
.append((offset
, length
, exists
))
1436 image
.diff_iterate(0, IMG_SIZE
, None, cb
)
1437 eq(extents
, expected
)
1439 class TestClone(object):
1441 @require_features([RBD_FEATURE_LAYERING
])
1447 self
.image
= Image(ioctx
, image_name
)
1448 data
= rand_data(256)
1449 self
.image
.write(data
, IMG_SIZE
// 2)
1450 self
.image
.create_snap('snap1')
1452 self
.image
.protect_snap('snap1')
1453 self
.clone_name
= get_temp_image_name()
1454 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
1456 self
.clone
= Image(ioctx
, self
.clone_name
)
1461 self
.rbd
.remove(ioctx
, self
.clone_name
)
1462 self
.image
.unprotect_snap('snap1')
1463 self
.image
.remove_snap('snap1')
1467 def _test_with_params(self
, features
=None, order
=None, stripe_unit
=None,
1469 self
.image
.create_snap('snap2')
1470 self
.image
.protect_snap('snap2')
1471 clone_name2
= get_temp_image_name()
1472 if features
is None:
1473 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
)
1475 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1477 elif stripe_unit
is None:
1478 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1480 elif stripe_count
is None:
1481 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1482 features
, order
, stripe_unit
)
1484 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1485 features
, order
, stripe_unit
, stripe_count
)
1486 self
.rbd
.remove(ioctx
, clone_name2
)
1487 self
.image
.unprotect_snap('snap2')
1488 self
.image
.remove_snap('snap2')
1490 def test_with_params(self
):
1491 self
._test
_with
_params
()
1493 def test_with_params2(self
):
1495 self
._test
_with
_params
(features
, self
.image
.stat()['order'])
1497 @require_features([RBD_FEATURE_STRIPINGV2
])
1498 def test_with_params3(self
):
1500 self
._test
_with
_params
(features
, self
.image
.stat()['order'],
1501 self
.image
.stripe_unit(),
1502 self
.image
.stripe_count())
1504 def test_stripe_unit_and_count(self
):
1507 image_name
= get_temp_image_name()
1508 RBD().create(ioctx
, image_name
, IMG_SIZE
, IMG_ORDER
, old_format
=False,
1509 features
=int(features
), stripe_unit
=1048576, stripe_count
=8)
1510 image
= Image(ioctx
, image_name
)
1511 image
.create_snap('snap1')
1512 image
.protect_snap('snap1')
1513 clone_name
= get_temp_image_name()
1514 RBD().clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name
)
1515 clone
= Image(ioctx
, clone_name
)
1517 eq(1048576, clone
.stripe_unit())
1518 eq(8, clone
.stripe_count())
1521 RBD().remove(ioctx
, clone_name
)
1522 image
.unprotect_snap('snap1')
1523 image
.remove_snap('snap1')
1525 RBD().remove(ioctx
, image_name
)
1528 def test_unprotected(self
):
1529 self
.image
.create_snap('snap2')
1531 clone_name2
= get_temp_image_name()
1532 rados
.conf_set("rbd_default_clone_format", "1")
1533 assert_raises(InvalidArgument
, self
.rbd
.clone
, ioctx
, image_name
,
1534 'snap2', ioctx
, clone_name2
, features
)
1535 rados
.conf_set("rbd_default_clone_format", "auto")
1536 self
.image
.remove_snap('snap2')
1538 def test_unprotect_with_children(self
):
1540 # can't remove a snapshot that has dependent clones
1541 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
1543 # validate parent info of clone created by TestClone.setUp
1544 (pool
, image
, snap
) = self
.clone
.parent_info()
1546 eq(image
, image_name
)
1548 eq(self
.image
.id(), self
.clone
.parent_id())
1550 # create a new pool...
1551 pool_name2
= get_temp_pool_name()
1552 rados
.create_pool(pool_name2
)
1553 other_ioctx
= rados
.open_ioctx(pool_name2
)
1554 other_ioctx
.application_enable('rbd')
1556 # ...with a clone of the same parent
1557 other_clone_name
= get_temp_image_name()
1558 rados
.conf_set("rbd_default_clone_format", "1")
1559 self
.rbd
.clone(ioctx
, image_name
, 'snap1', other_ioctx
,
1560 other_clone_name
, features
)
1561 rados
.conf_set("rbd_default_clone_format", "auto")
1562 self
.other_clone
= Image(other_ioctx
, other_clone_name
)
1563 # validate its parent info
1564 (pool
, image
, snap
) = self
.other_clone
.parent_info()
1566 eq(image
, image_name
)
1568 eq(self
.image
.id(), self
.other_clone
.parent_id())
1570 # can't unprotect snap with children
1571 assert_raises(ImageBusy
, self
.image
.unprotect_snap
, 'snap1')
1573 # 2 children, check that cannot remove the parent snap
1574 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
1576 # close and remove other pool's clone
1577 self
.other_clone
.close()
1578 self
.rbd
.remove(other_ioctx
, other_clone_name
)
1580 # check that we cannot yet remove the parent snap
1581 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
1584 rados
.delete_pool(pool_name2
)
1586 # unprotect, remove parent snap happen in cleanup, and should succeed
1588 def test_stat(self
):
1589 image_info
= self
.image
.stat()
1590 clone_info
= self
.clone
.stat()
1591 eq(clone_info
['size'], image_info
['size'])
1592 eq(clone_info
['size'], self
.clone
.overlap())
1594 def test_resize_stat(self
):
1595 self
.clone
.resize(IMG_SIZE
// 2)
1596 image_info
= self
.image
.stat()
1597 clone_info
= self
.clone
.stat()
1598 eq(clone_info
['size'], IMG_SIZE
// 2)
1599 eq(image_info
['size'], IMG_SIZE
)
1600 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
1602 self
.clone
.resize(IMG_SIZE
* 2)
1603 image_info
= self
.image
.stat()
1604 clone_info
= self
.clone
.stat()
1605 eq(clone_info
['size'], IMG_SIZE
* 2)
1606 eq(image_info
['size'], IMG_SIZE
)
1607 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
1609 def test_resize_io(self
):
1610 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1611 self
.image
.resize(0)
1612 self
.clone
.resize(IMG_SIZE
// 2 + 128)
1613 child_data
= self
.clone
.read(IMG_SIZE
// 2, 128)
1614 eq(child_data
, parent_data
[:128])
1615 self
.clone
.resize(IMG_SIZE
)
1616 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1617 eq(child_data
, parent_data
[:128] + (b
'\0' * 128))
1618 self
.clone
.resize(IMG_SIZE
// 2 + 1)
1619 child_data
= self
.clone
.read(IMG_SIZE
// 2, 1)
1620 eq(child_data
, parent_data
[0:1])
1621 self
.clone
.resize(0)
1622 self
.clone
.resize(IMG_SIZE
)
1623 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1624 eq(child_data
, b
'\0' * 256)
1626 def test_read(self
):
1627 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1628 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1629 eq(child_data
, parent_data
)
1631 def test_write(self
):
1632 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1633 new_data
= rand_data(256)
1634 self
.clone
.write(new_data
, IMG_SIZE
// 2 + 256)
1635 child_data
= self
.clone
.read(IMG_SIZE
// 2 + 256, 256)
1636 eq(child_data
, new_data
)
1637 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1638 eq(child_data
, parent_data
)
1639 parent_data
= self
.image
.read(IMG_SIZE
// 2 + 256, 256)
1640 eq(parent_data
, b
'\0' * 256)
1642 def check_children(self
, expected
):
1643 actual
= self
.image
.list_children()
1644 # dedup for cache pools until
1645 # http://tracker.ceph.com/issues/8187 is fixed
1646 deduped
= set([(pool_name
, image
[1]) for image
in actual
])
1647 eq(deduped
, set(expected
))
1649 def check_children2(self
, expected
):
1650 actual
= [{k
:v
for k
,v
in x
.items() if k
in expected
[0]} \
1651 for x
in self
.image
.list_children2()]
1652 eq(actual
, expected
)
1654 def check_descendants(self
, expected
):
1655 eq(list(self
.image
.list_descendants()), expected
)
1657 def get_image_id(self
, ioctx
, name
):
1658 with
Image(ioctx
, name
) as image
:
1661 def test_list_children(self
):
1664 self
.image
.set_snap('snap1')
1665 self
.check_children([(pool_name
, self
.clone_name
)])
1666 self
.check_children2(
1667 [{'pool': pool_name
, 'pool_namespace': '',
1668 'image': self
.clone_name
, 'trash': False,
1669 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1670 self
.check_descendants(
1671 [{'pool': pool_name
, 'pool_namespace': '',
1672 'image': self
.clone_name
, 'trash': False,
1673 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1675 self
.rbd
.remove(ioctx
, self
.clone_name
)
1676 eq(self
.image
.list_children(), [])
1677 eq(list(self
.image
.list_children2()), [])
1678 eq(list(self
.image
.list_descendants()), [])
1680 clone_name
= get_temp_image_name() + '_'
1681 expected_children
= []
1682 expected_children2
= []
1684 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
,
1685 clone_name
+ str(i
), features
)
1686 expected_children
.append((pool_name
, clone_name
+ str(i
)))
1687 expected_children2
.append(
1688 {'pool': pool_name
, 'pool_namespace': '',
1689 'image': clone_name
+ str(i
), 'trash': False,
1690 'id': self
.get_image_id(ioctx
, clone_name
+ str(i
))})
1691 self
.check_children(expected_children
)
1692 self
.check_children2(expected_children2
)
1693 self
.check_descendants(expected_children2
)
1695 image6_id
= self
.get_image_id(ioctx
, clone_name
+ str(5))
1696 RBD().trash_move(ioctx
, clone_name
+ str(5), 0)
1697 expected_children
.remove((pool_name
, clone_name
+ str(5)))
1698 for item
in expected_children2
:
1699 for k
, v
in item
.items():
1701 item
["trash"] = True
1702 self
.check_children(expected_children
)
1703 self
.check_children2(expected_children2
)
1704 self
.check_descendants(expected_children2
)
1706 RBD().trash_restore(ioctx
, image6_id
, clone_name
+ str(5))
1707 expected_children
.append((pool_name
, clone_name
+ str(5)))
1708 for item
in expected_children2
:
1709 for k
, v
in item
.items():
1711 item
["trash"] = False
1712 self
.check_children(expected_children
)
1713 self
.check_children2(expected_children2
)
1714 self
.check_descendants(expected_children2
)
1717 self
.rbd
.remove(ioctx
, clone_name
+ str(i
))
1718 expected_children
.remove((pool_name
, clone_name
+ str(i
)))
1719 expected_children2
.pop(0)
1720 self
.check_children(expected_children
)
1721 self
.check_children2(expected_children2
)
1722 self
.check_descendants(expected_children2
)
1724 eq(self
.image
.list_children(), [])
1725 eq(list(self
.image
.list_children2()), [])
1726 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
1728 self
.check_children([(pool_name
, self
.clone_name
)])
1729 self
.check_children2(
1730 [{'pool': pool_name
, 'pool_namespace': '',
1731 'image': self
.clone_name
, 'trash': False,
1732 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1733 self
.check_descendants(
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
.clone
= Image(ioctx
, self
.clone_name
)
1739 def test_flatten_errors(self
):
1740 # test that we can't flatten a non-clone
1741 assert_raises(InvalidArgument
, self
.image
.flatten
)
1743 # test that we can't flatten a snapshot
1744 self
.clone
.create_snap('snap2')
1745 self
.clone
.set_snap('snap2')
1746 assert_raises(ReadOnlyImage
, self
.clone
.flatten
)
1747 self
.clone
.remove_snap('snap2')
1749 def check_flatten_with_order(self
, new_order
, stripe_unit
=None,
1753 clone_name2
= get_temp_image_name()
1754 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1755 features
, new_order
, stripe_unit
, stripe_count
)
1756 #with Image(ioctx, 'clone2') as clone:
1757 clone2
= Image(ioctx
, clone_name2
)
1759 eq(clone2
.overlap(), 0)
1761 self
.rbd
.remove(ioctx
, clone_name2
)
1763 # flatten after resizing to non-block size
1764 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1765 features
, new_order
, stripe_unit
, stripe_count
)
1766 with
Image(ioctx
, clone_name2
) as clone
:
1767 clone
.resize(IMG_SIZE
// 2 - 1)
1769 eq(0, clone
.overlap())
1770 self
.rbd
.remove(ioctx
, clone_name2
)
1772 # flatten after resizing to non-block size
1773 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1774 features
, new_order
, stripe_unit
, stripe_count
)
1775 with
Image(ioctx
, clone_name2
) as clone
:
1776 clone
.resize(IMG_SIZE
// 2 + 1)
1778 eq(clone
.overlap(), 0)
1779 self
.rbd
.remove(ioctx
, clone_name2
)
1781 def test_flatten_basic(self
):
1782 self
.check_flatten_with_order(IMG_ORDER
)
1784 def test_flatten_smaller_order(self
):
1785 self
.check_flatten_with_order(IMG_ORDER
- 2, 1048576, 1)
1787 def test_flatten_larger_order(self
):
1788 self
.check_flatten_with_order(IMG_ORDER
+ 2)
1790 def test_flatten_drops_cache(self
):
1793 clone_name2
= get_temp_image_name()
1794 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1795 features
, IMG_ORDER
)
1796 with
Image(ioctx
, clone_name2
) as clone
:
1797 with
Image(ioctx
, clone_name2
) as clone2
:
1798 # cache object non-existence
1799 data
= clone
.read(IMG_SIZE
// 2, 256)
1800 clone2_data
= clone2
.read(IMG_SIZE
// 2, 256)
1801 eq(data
, clone2_data
)
1803 assert_raises(ImageNotFound
, clone
.parent_info
)
1804 assert_raises(ImageNotFound
, clone2
.parent_info
)
1805 assert_raises(ImageNotFound
, clone
.parent_id
)
1806 assert_raises(ImageNotFound
, clone2
.parent_id
)
1807 after_flatten
= clone
.read(IMG_SIZE
// 2, 256)
1808 eq(data
, after_flatten
)
1809 after_flatten
= clone2
.read(IMG_SIZE
// 2, 256)
1810 eq(data
, after_flatten
)
1811 self
.rbd
.remove(ioctx
, clone_name2
)
1813 def test_flatten_multi_level(self
):
1814 self
.clone
.create_snap('snap2')
1815 self
.clone
.protect_snap('snap2')
1816 clone_name3
= get_temp_image_name()
1817 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1819 self
.clone
.flatten()
1820 with
Image(ioctx
, clone_name3
) as clone3
:
1822 self
.clone
.unprotect_snap('snap2')
1823 self
.clone
.remove_snap('snap2')
1824 self
.rbd
.remove(ioctx
, clone_name3
)
1826 def test_flatten_with_progress(self
):
1827 d
= {'received_callback': False}
1828 def progress_cb(current
, total
):
1829 d
['received_callback'] = True
1834 clone_name
= get_temp_image_name()
1835 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name
,
1837 with
Image(ioctx
, clone_name
) as clone
:
1838 clone
.flatten(on_progress
=progress_cb
)
1839 self
.rbd
.remove(ioctx
, clone_name
)
1840 eq(True, d
['received_callback'])
1842 def test_resize_flatten_multi_level(self
):
1843 self
.clone
.create_snap('snap2')
1844 self
.clone
.protect_snap('snap2')
1845 clone_name3
= get_temp_image_name()
1846 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1848 self
.clone
.resize(1)
1849 orig_data
= self
.image
.read(0, 256)
1850 with
Image(ioctx
, clone_name3
) as clone3
:
1851 clone3_data
= clone3
.read(0, 256)
1852 eq(orig_data
, clone3_data
)
1853 self
.clone
.flatten()
1854 with
Image(ioctx
, clone_name3
) as clone3
:
1855 clone3_data
= clone3
.read(0, 256)
1856 eq(orig_data
, clone3_data
)
1857 self
.rbd
.remove(ioctx
, clone_name3
)
1858 self
.clone
.unprotect_snap('snap2')
1859 self
.clone
.remove_snap('snap2')
1861 def test_trash_snapshot(self
):
1862 self
.image
.create_snap('snap2')
1864 clone_name
= get_temp_image_name()
1865 rados
.conf_set("rbd_default_clone_format", "2")
1866 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name
, features
)
1867 rados
.conf_set("rbd_default_clone_format", "auto")
1869 self
.image
.remove_snap('snap2')
1871 snaps
= [s
for s
in self
.image
.list_snaps() if s
['name'] != 'snap1']
1872 eq([RBD_SNAP_NAMESPACE_TYPE_TRASH
], [s
['namespace'] for s
in snaps
])
1873 eq([{'original_name' : 'snap2'}], [s
['trash'] for s
in snaps
])
1875 self
.rbd
.remove(ioctx
, clone_name
)
1876 eq([], [s
for s
in self
.image
.list_snaps() if s
['name'] != 'snap1'])
1878 class TestExclusiveLock(object):
1880 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
1883 rados2
= Rados(conffile
='')
1886 ioctx2
= rados2
.open_ioctx(pool_name
)
1896 def test_ownership(self
):
1897 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1898 image1
.write(b
'0'*256, 0)
1899 eq(image1
.is_exclusive_lock_owner(), True)
1900 eq(image2
.is_exclusive_lock_owner(), False)
1902 def test_snapshot_leadership(self
):
1903 with
Image(ioctx
, image_name
) as image
:
1904 image
.create_snap('snap')
1905 eq(image
.is_exclusive_lock_owner(), True)
1907 with
Image(ioctx
, image_name
) as image
:
1908 image
.write(b
'0'*256, 0)
1909 eq(image
.is_exclusive_lock_owner(), True)
1910 image
.set_snap('snap')
1911 eq(image
.is_exclusive_lock_owner(), False)
1912 with
Image(ioctx
, image_name
, snapshot
='snap') as image
:
1913 eq(image
.is_exclusive_lock_owner(), False)
1915 with
Image(ioctx
, image_name
) as image
:
1916 image
.remove_snap('snap')
1918 def test_read_only_leadership(self
):
1919 with
Image(ioctx
, image_name
, read_only
=True) as image
:
1920 eq(image
.is_exclusive_lock_owner(), False)
1922 def test_follower_flatten(self
):
1923 with
Image(ioctx
, image_name
) as image
:
1924 image
.create_snap('snap')
1925 image
.protect_snap('snap')
1927 RBD().clone(ioctx
, image_name
, 'snap', ioctx
, 'clone', features
)
1928 with
Image(ioctx
, 'clone') as image1
, Image(ioctx2
, 'clone') as image2
:
1929 data
= rand_data(256)
1930 image1
.write(data
, 0)
1932 assert_raises(ImageNotFound
, image1
.parent_info
)
1933 assert_raises(ImageNotFound
, image1
.parent_id
)
1937 image2
.parent_info()
1938 except ImageNotFound
:
1943 RBD().remove(ioctx
, 'clone')
1944 with
Image(ioctx
, image_name
) as image
:
1945 image
.unprotect_snap('snap')
1946 image
.remove_snap('snap')
1948 def test_follower_resize(self
):
1949 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1950 image1
.write(b
'0'*256, 0)
1951 for new_size
in [IMG_SIZE
* 2, IMG_SIZE
// 2]:
1952 image2
.resize(new_size
);
1953 eq(new_size
, image1
.size())
1955 if new_size
== image2
.size():
1958 eq(new_size
, image2
.size())
1960 def test_follower_snap_create(self
):
1961 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1962 image2
.create_snap('snap1')
1963 image1
.remove_snap('snap1')
1965 def test_follower_snap_rollback(self
):
1966 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1967 image1
.create_snap('snap')
1969 assert_raises(ReadOnlyImage
, image2
.rollback_to_snap
, 'snap')
1970 image1
.rollback_to_snap('snap')
1972 image1
.remove_snap('snap')
1974 def test_follower_discard(self
):
1976 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1977 data
= rand_data(256)
1978 image1
.write(data
, 0)
1979 image2
.discard(0, 256)
1980 eq(image1
.is_exclusive_lock_owner(), False)
1981 eq(image2
.is_exclusive_lock_owner(), True)
1982 read
= image2
.read(0, 256)
1983 if rados
.conf_get('rbd_skip_partial_discard') == 'false':
1984 eq(256 * b
'\0', read
)
1988 def test_follower_write(self
):
1989 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1990 data
= rand_data(256)
1991 image1
.write(data
, 0)
1992 image2
.write(data
, IMG_SIZE
// 2)
1993 eq(image1
.is_exclusive_lock_owner(), False)
1994 eq(image2
.is_exclusive_lock_owner(), True)
1995 for offset
in [0, IMG_SIZE
// 2]:
1996 read
= image2
.read(offset
, 256)
1998 def test_acquire_release_lock(self
):
1999 with
Image(ioctx
, image_name
) as image
:
2000 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
2001 image
.lock_release()
2003 def test_break_lock(self
):
2004 blocklist_rados
= Rados(conffile
='')
2005 blocklist_rados
.connect()
2007 blocklist_ioctx
= blocklist_rados
.open_ioctx(pool_name
)
2009 rados2
.conf_set('rbd_blocklist_on_break_lock', 'true')
2010 with
Image(ioctx2
, image_name
) as image
, \
2011 Image(blocklist_ioctx
, image_name
) as blocklist_image
:
2013 lock_owners
= list(image
.lock_get_owners())
2014 eq(0, len(lock_owners
))
2016 blocklist_image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
2017 assert_raises(ReadOnlyImage
, image
.lock_acquire
,
2018 RBD_LOCK_MODE_EXCLUSIVE
)
2019 lock_owners
= list(image
.lock_get_owners())
2020 eq(1, len(lock_owners
))
2021 eq(RBD_LOCK_MODE_EXCLUSIVE
, lock_owners
[0]['mode'])
2022 image
.lock_break(RBD_LOCK_MODE_EXCLUSIVE
,
2023 lock_owners
[0]['owner'])
2025 assert_raises(ConnectionShutdown
,
2026 blocklist_image
.is_exclusive_lock_owner
)
2028 blocklist_rados
.wait_for_latest_osdmap()
2029 data
= rand_data(256)
2030 assert_raises(ConnectionShutdown
,
2031 blocklist_image
.write
, data
, 0)
2033 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
2036 blocklist_image
.close()
2037 except ConnectionShutdown
:
2040 blocklist_ioctx
.close()
2042 blocklist_rados
.shutdown()
2044 class TestMirroring(object):
2047 def check_info(info
, global_id
, state
, primary
=None):
2048 eq(global_id
, info
['global_id'])
2049 eq(state
, info
['state'])
2050 if primary
is not None:
2051 eq(primary
, info
['primary'])
2055 self
.initial_mirror_mode
= self
.rbd
.mirror_mode_get(ioctx
)
2056 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
2058 self
.image
= Image(ioctx
, image_name
)
2063 self
.rbd
.mirror_mode_set(ioctx
, self
.initial_mirror_mode
)
2065 def test_uuid(self
):
2066 mirror_uuid
= self
.rbd
.mirror_uuid_get(ioctx
)
2069 def test_site_name(self
):
2070 site_name
= "us-west-1"
2071 self
.rbd
.mirror_site_name_set(rados
, site_name
)
2072 eq(site_name
, self
.rbd
.mirror_site_name_get(rados
))
2073 self
.rbd
.mirror_site_name_set(rados
, "")
2074 eq(rados
.get_fsid(), self
.rbd
.mirror_site_name_get(rados
))
2076 def test_mirror_peer_bootstrap(self
):
2077 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
2079 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_DISABLED
)
2080 assert_raises(InvalidArgument
, self
.rbd
.mirror_peer_bootstrap_create
,
2083 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
2084 token_b64
= self
.rbd
.mirror_peer_bootstrap_create(ioctx
)
2085 token
= base64
.b64decode(token_b64
)
2086 token_dict
= json
.loads(token
)
2087 eq(sorted(['fsid', 'client_id', 'key', 'mon_host']),
2088 sorted(list(token_dict
.keys())))
2090 # requires different cluster
2091 assert_raises(InvalidArgument
, self
.rbd
.mirror_peer_bootstrap_import
,
2092 ioctx
, RBD_MIRROR_PEER_DIRECTION_RX
, token_b64
)
2094 def test_mirror_peer(self
):
2095 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
2096 site_name
= "test_site"
2097 client_name
= "test_client"
2098 uuid
= self
.rbd
.mirror_peer_add(ioctx
, site_name
, client_name
,
2099 direction
=RBD_MIRROR_PEER_DIRECTION_RX_TX
)
2103 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX
,
2104 'site_name' : site_name
,
2105 'cluster_name' : site_name
,
2107 'client_name' : client_name
,
2109 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
2110 cluster_name
= "test_cluster1"
2111 self
.rbd
.mirror_peer_set_cluster(ioctx
, uuid
, cluster_name
)
2112 client_name
= "test_client1"
2113 self
.rbd
.mirror_peer_set_client(ioctx
, uuid
, client_name
)
2116 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX
,
2117 'site_name' : cluster_name
,
2118 'cluster_name' : cluster_name
,
2120 'client_name' : client_name
,
2122 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
2125 RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST
: 'host1',
2126 RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY
: 'abc'
2128 self
.rbd
.mirror_peer_set_attributes(ioctx
, uuid
, attribs
)
2129 eq(attribs
, self
.rbd
.mirror_peer_get_attributes(ioctx
, uuid
))
2131 self
.rbd
.mirror_peer_remove(ioctx
, uuid
)
2132 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
2134 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
2135 RBD_FEATURE_JOURNALING
])
2136 def test_mirror_image(self
):
2138 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
2139 self
.image
.mirror_image_disable(True)
2140 info
= self
.image
.mirror_image_get_info()
2141 self
.check_info(info
, '', RBD_MIRROR_IMAGE_DISABLED
, False)
2143 self
.image
.mirror_image_enable()
2144 info
= self
.image
.mirror_image_get_info()
2145 global_id
= info
['global_id']
2146 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2148 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
2151 self
.image
.mirror_image_disable(True)
2152 except InvalidArgument
:
2154 eq(True, fail
) # Fails because of mirror mode pool
2156 self
.image
.mirror_image_demote()
2157 info
= self
.image
.mirror_image_get_info()
2158 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, False)
2160 entries
= dict(self
.rbd
.mirror_image_info_list(ioctx
))
2161 info
['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL
;
2162 eq(info
, entries
[self
.image
.id()])
2164 self
.image
.mirror_image_resync()
2166 self
.image
.mirror_image_promote(True)
2167 info
= self
.image
.mirror_image_get_info()
2168 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2170 entries
= dict(self
.rbd
.mirror_image_info_list(ioctx
))
2171 info
['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL
;
2172 eq(info
, entries
[self
.image
.id()])
2176 self
.image
.mirror_image_resync()
2177 except InvalidArgument
:
2179 eq(True, fail
) # Fails because it is primary
2181 status
= self
.image
.mirror_image_get_status()
2182 eq(image_name
, status
['name'])
2183 eq(False, status
['up'])
2184 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
2185 info
= status
['info']
2186 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2188 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
2189 RBD_FEATURE_JOURNALING
])
2190 def test_mirror_image_status(self
):
2191 info
= self
.image
.mirror_image_get_info()
2192 global_id
= info
['global_id']
2193 state
= info
['state']
2194 primary
= info
['primary']
2196 status
= self
.image
.mirror_image_get_status()
2197 eq(image_name
, status
['name'])
2198 eq(False, status
['up'])
2199 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
2200 eq([], status
['remote_statuses'])
2201 info
= status
['info']
2202 self
.check_info(info
, global_id
, state
, primary
)
2204 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
2207 eq(image_name
, status
['name'])
2208 eq(False, status
['up'])
2209 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
2210 info
= status
['info']
2211 self
.check_info(info
, global_id
, state
)
2213 states
= self
.rbd
.mirror_image_status_summary(ioctx
)
2214 eq([(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, 1)], states
)
2216 assert_raises(ImageNotFound
, self
.image
.mirror_image_get_instance_id
)
2217 instance_ids
= list(self
.rbd
.mirror_image_instance_id_list(ioctx
))
2218 eq(0, len(instance_ids
))
2222 self
.rbd
.create(ioctx
, image_name
+ str(i
), IMG_SIZE
, IMG_ORDER
,
2223 old_format
=False, features
=int(features
))
2224 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
2225 eq(N
+ 1, len(images
))
2227 self
.rbd
.remove(ioctx
, image_name
+ str(i
))
2229 def test_mirror_image_create_snapshot(self
):
2230 assert_raises(InvalidArgument
, self
.image
.mirror_image_create_snapshot
)
2232 peer1_uuid
= self
.rbd
.mirror_peer_add(ioctx
, "cluster1", "client")
2233 peer2_uuid
= self
.rbd
.mirror_peer_add(ioctx
, "cluster2", "client")
2234 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
2235 self
.image
.mirror_image_disable(False)
2236 self
.image
.mirror_image_enable(RBD_MIRROR_IMAGE_MODE_SNAPSHOT
)
2237 mode
= self
.image
.mirror_image_get_mode()
2238 eq(RBD_MIRROR_IMAGE_MODE_SNAPSHOT
, mode
)
2240 snaps
= list(self
.image
.list_snaps())
2243 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2244 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2246 info
= self
.image
.mirror_image_get_info()
2247 eq(True, info
['primary'])
2249 self
.rbd
.mirror_image_info_list(ioctx
,
2250 RBD_MIRROR_IMAGE_MODE_SNAPSHOT
))
2251 info
['mode'] = RBD_MIRROR_IMAGE_MODE_SNAPSHOT
;
2252 eq(info
, entries
[self
.image
.id()])
2254 snap_id
= self
.image
.mirror_image_create_snapshot(
2255 RBD_SNAP_CREATE_SKIP_QUIESCE
)
2257 snaps
= list(self
.image
.list_snaps())
2260 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2261 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2263 eq(snap
['id'], snap_id
)
2264 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2265 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2266 eq(sorted([peer1_uuid
, peer2_uuid
]),
2267 sorted(snap
['mirror']['mirror_peer_uuids']))
2269 eq(RBD_SNAP_NAMESPACE_TYPE_MIRROR
,
2270 self
.image
.snap_get_namespace_type(snap_id
))
2271 mirror_snap
= self
.image
.snap_get_mirror_namespace(snap_id
)
2272 eq(mirror_snap
, snap
['mirror'])
2274 self
.image
.mirror_image_demote()
2276 assert_raises(InvalidArgument
, self
.image
.mirror_image_create_snapshot
)
2278 snaps
= list(self
.image
.list_snaps())
2281 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2283 eq(snap
['id'], snap_id
)
2284 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2286 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2287 eq(RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED
, snap
['mirror']['state'])
2288 eq(sorted([peer1_uuid
, peer2_uuid
]),
2289 sorted(snap
['mirror']['mirror_peer_uuids']))
2291 self
.rbd
.mirror_peer_remove(ioctx
, peer1_uuid
)
2292 self
.rbd
.mirror_peer_remove(ioctx
, peer2_uuid
)
2293 self
.image
.mirror_image_promote(False)
2295 def test_aio_mirror_image_create_snapshot(self
):
2296 peer_uuid
= self
.rbd
.mirror_peer_add(ioctx
, "cluster", "client")
2297 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
2298 self
.image
.mirror_image_disable(False)
2299 self
.image
.mirror_image_enable(RBD_MIRROR_IMAGE_MODE_SNAPSHOT
)
2301 snaps
= list(self
.image
.list_snaps())
2304 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2305 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2307 # this is a list so that the local cb() can modify it
2312 comp
= self
.image
.aio_mirror_image_get_info(cb
)
2313 comp
.wait_for_complete_and_cb()
2314 assert_not_equal(info
[0], None)
2315 eq(comp
.get_return_value(), 0)
2316 eq(sys
.getrefcount(comp
), 2)
2318 global_id
= info
['global_id']
2319 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2325 comp
= self
.image
.aio_mirror_image_get_mode(cb
)
2326 comp
.wait_for_complete_and_cb()
2327 eq(comp
.get_return_value(), 0)
2328 eq(sys
.getrefcount(comp
), 2)
2329 eq(mode
[0], RBD_MIRROR_IMAGE_MODE_SNAPSHOT
)
2332 def cb(_
, _snap_id
):
2333 snap_id
[0] = _snap_id
2335 comp
= self
.image
.aio_mirror_image_create_snapshot(0, cb
)
2336 comp
.wait_for_complete_and_cb()
2337 assert_not_equal(snap_id
[0], None)
2338 eq(comp
.get_return_value(), 0)
2339 eq(sys
.getrefcount(comp
), 2)
2341 snaps
= list(self
.image
.list_snaps())
2344 eq(snap
['id'], snap_id
[0])
2345 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2346 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2347 eq([peer_uuid
], snap
['mirror']['mirror_peer_uuids'])
2349 self
.rbd
.mirror_peer_remove(ioctx
, peer_uuid
)
2351 class TestTrash(object):
2355 rados2
= Rados(conffile
='')
2358 ioctx2
= rados2
.open_ioctx(pool_name
)
2366 def test_move(self
):
2368 with
Image(ioctx
, image_name
) as image
:
2369 image_id
= image
.id()
2371 RBD().trash_move(ioctx
, image_name
, 1000)
2372 RBD().trash_remove(ioctx
, image_id
, True)
2374 def test_purge(self
):
2376 with
Image(ioctx
, image_name
) as image
:
2377 image_name1
= image_name
2378 image_id1
= image
.id()
2381 with
Image(ioctx
, image_name
) as image
:
2382 image_name2
= image_name
2383 image_id2
= image
.id()
2385 RBD().trash_move(ioctx
, image_name1
, 0)
2386 RBD().trash_move(ioctx
, image_name2
, 1000)
2387 RBD().trash_purge(ioctx
, datetime
.now())
2389 entries
= list(RBD().trash_list(ioctx
))
2390 eq([image_id2
], [x
['id'] for x
in entries
])
2391 RBD().trash_remove(ioctx
, image_id2
, True)
2393 def test_remove_denied(self
):
2395 with
Image(ioctx
, image_name
) as image
:
2396 image_id
= image
.id()
2398 RBD().trash_move(ioctx
, image_name
, 1000)
2399 assert_raises(PermissionError
, RBD().trash_remove
, ioctx
, image_id
)
2400 RBD().trash_remove(ioctx
, image_id
, True)
2402 def test_remove(self
):
2404 with
Image(ioctx
, image_name
) as image
:
2405 image_id
= image
.id()
2407 RBD().trash_move(ioctx
, image_name
, 0)
2408 RBD().trash_remove(ioctx
, image_id
)
2410 def test_remove_with_progress(self
):
2411 d
= {'received_callback': False}
2412 def progress_cb(current
, total
):
2413 d
['received_callback'] = True
2417 with
Image(ioctx
, image_name
) as image
:
2418 image_id
= image
.id()
2420 RBD().trash_move(ioctx
, image_name
, 0)
2421 RBD().trash_remove(ioctx
, image_id
, on_progress
=progress_cb
)
2422 eq(True, d
['received_callback'])
2426 with
Image(ioctx
, image_name
) as image
:
2427 image_id
= image
.id()
2429 RBD().trash_move(ioctx
, image_name
, 1000)
2431 info
= RBD().trash_get(ioctx
, image_id
)
2432 eq(image_id
, info
['id'])
2433 eq(image_name
, info
['name'])
2434 eq('USER', info
['source'])
2435 assert(info
['deferment_end_time'] > info
['deletion_time'])
2437 RBD().trash_remove(ioctx
, image_id
, True)
2439 def test_list(self
):
2441 with
Image(ioctx
, image_name
) as image
:
2442 image_id1
= image
.id()
2443 image_name1
= image_name
2444 RBD().trash_move(ioctx
, image_name
, 1000)
2447 with
Image(ioctx
, image_name
) as image
:
2448 image_id2
= image
.id()
2449 image_name2
= image_name
2450 RBD().trash_move(ioctx
, image_name
, 1000)
2452 entries
= list(RBD().trash_list(ioctx
))
2454 if e
['id'] == image_id1
:
2455 eq(e
['name'], image_name1
)
2456 elif e
['id'] == image_id2
:
2457 eq(e
['name'], image_name2
)
2460 eq(e
['source'], 'USER')
2461 assert e
['deferment_end_time'] > e
['deletion_time']
2463 RBD().trash_remove(ioctx
, image_id1
, True)
2464 RBD().trash_remove(ioctx
, image_id2
, True)
2466 def test_restore(self
):
2468 with
Image(ioctx
, image_name
) as image
:
2469 image_id
= image
.id()
2470 RBD().trash_move(ioctx
, image_name
, 1000)
2471 RBD().trash_restore(ioctx
, image_id
, image_name
)
2474 def test_create_group():
2478 def test_rename_group():
2480 if group_name
is not None:
2482 eq(["new" + group_name
], RBD().group_list(ioctx
))
2483 RBD().group_remove(ioctx
, "new" + group_name
)
2487 def test_list_groups_empty():
2488 eq([], RBD().group_list(ioctx
))
2490 @with_setup(create_group
, remove_group
)
2491 def test_list_groups():
2492 eq([group_name
], RBD().group_list(ioctx
))
2494 @with_setup(create_group
)
2495 def test_list_groups_after_removed():
2497 eq([], RBD().group_list(ioctx
))
2499 class TestGroups(object):
2505 self
.image_names
= [image_name
]
2506 self
.image
= Image(ioctx
, image_name
)
2509 snap_name
= get_temp_snap_name()
2510 self
.group
= Group(ioctx
, group_name
)
2515 for name
in self
.image_names
:
2516 RBD().remove(ioctx
, name
)
2518 def test_group_image_add(self
):
2519 self
.group
.add_image(ioctx
, image_name
)
2521 def test_group_image_list_empty(self
):
2522 eq([], list(self
.group
.list_images()))
2524 def test_group_image_list(self
):
2525 eq([], list(self
.group
.list_images()))
2526 self
.group
.add_image(ioctx
, image_name
)
2527 eq([image_name
], [img
['name'] for img
in self
.group
.list_images()])
2529 def test_group_image_list_move_to_trash(self
):
2530 eq([], list(self
.group
.list_images()))
2531 with
Image(ioctx
, image_name
) as image
:
2532 image_id
= image
.id()
2533 self
.group
.add_image(ioctx
, image_name
)
2534 eq([image_name
], [img
['name'] for img
in self
.group
.list_images()])
2535 RBD().trash_move(ioctx
, image_name
, 0)
2536 eq([], list(self
.group
.list_images()))
2537 RBD().trash_restore(ioctx
, image_id
, image_name
)
2539 def test_group_image_many_images(self
):
2540 eq([], list(self
.group
.list_images()))
2541 self
.group
.add_image(ioctx
, image_name
)
2543 for x
in range(0, 20):
2545 self
.image_names
.append(image_name
)
2546 self
.group
.add_image(ioctx
, image_name
)
2548 self
.image_names
.sort()
2549 answer
= [img
['name'] for img
in self
.group
.list_images()]
2551 eq(self
.image_names
, answer
)
2553 def test_group_image_remove(self
):
2554 eq([], list(self
.group
.list_images()))
2555 self
.group
.add_image(ioctx
, image_name
)
2556 with
Image(ioctx
, image_name
) as image
:
2557 eq(RBD_OPERATION_FEATURE_GROUP
,
2558 image
.op_features() & RBD_OPERATION_FEATURE_GROUP
)
2559 group
= image
.group()
2560 eq(group_name
, group
['name'])
2562 eq([image_name
], [img
['name'] for img
in self
.group
.list_images()])
2563 self
.group
.remove_image(ioctx
, image_name
)
2564 eq([], list(self
.group
.list_images()))
2565 with
Image(ioctx
, image_name
) as image
:
2566 eq(0, image
.op_features() & RBD_OPERATION_FEATURE_GROUP
)
2568 def test_group_snap(self
):
2570 eq([], list(self
.group
.list_snaps()))
2571 self
.group
.create_snap(snap_name
)
2572 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2574 for snap
in self
.image
.list_snaps():
2575 eq(rbd
.RBD_SNAP_NAMESPACE_TYPE_GROUP
, snap
['namespace'])
2576 info
= snap
['group']
2577 eq(group_name
, info
['group_name'])
2578 eq(snap_name
, info
['group_snap_name'])
2580 self
.group
.remove_snap(snap_name
)
2581 eq([], list(self
.group
.list_snaps()))
2583 def test_group_snap_flags(self
):
2585 eq([], list(self
.group
.list_snaps()))
2587 self
.group
.create_snap(snap_name
, 0)
2588 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2589 self
.group
.remove_snap(snap_name
)
2591 self
.group
.create_snap(snap_name
, RBD_SNAP_CREATE_SKIP_QUIESCE
)
2592 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2593 self
.group
.remove_snap(snap_name
)
2595 self
.group
.create_snap(snap_name
, RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR
)
2596 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2597 self
.group
.remove_snap(snap_name
)
2599 assert_raises(InvalidArgument
, self
.group
.create_snap
, snap_name
,
2600 RBD_SNAP_CREATE_SKIP_QUIESCE |
2601 RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR
)
2602 eq([], list(self
.group
.list_snaps()))
2604 def test_group_snap_list_many(self
):
2606 eq([], list(self
.group
.list_snaps()))
2608 for x
in range(0, 20):
2609 snap_names
.append(snap_name
)
2610 self
.group
.create_snap(snap_name
)
2611 snap_name
= get_temp_snap_name()
2614 answer
= [snap
['name'] for snap
in self
.group
.list_snaps()]
2616 eq(snap_names
, answer
)
2618 def test_group_snap_namespace(self
):
2620 eq([], list(self
.group
.list_snaps()))
2621 self
.group
.add_image(ioctx
, image_name
)
2622 self
.group
.create_snap(snap_name
)
2623 eq(1, len([snap
['name'] for snap
in self
.image
.list_snaps()]))
2624 self
.group
.remove_image(ioctx
, image_name
)
2625 self
.group
.remove_snap(snap_name
)
2626 eq([], list(self
.group
.list_snaps()))
2628 def test_group_snap_rename(self
):
2630 new_snap_name
= "new" + snap_name
2632 eq([], list(self
.group
.list_snaps()))
2633 self
.group
.create_snap(snap_name
)
2634 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2635 self
.group
.rename_snap(snap_name
, new_snap_name
)
2636 eq([new_snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2637 self
.group
.remove_snap(new_snap_name
)
2638 eq([], list(self
.group
.list_snaps()))
2640 def test_group_snap_rollback(self
):
2641 eq([], list(self
.group
.list_images()))
2642 self
.group
.add_image(ioctx
, image_name
)
2643 with
Image(ioctx
, image_name
) as image
:
2644 image
.write(b
'\0' * 256, 0)
2645 read
= image
.read(0, 256)
2646 eq(read
, b
'\0' * 256)
2649 eq([], list(self
.group
.list_snaps()))
2650 self
.group
.create_snap(snap_name
)
2651 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2653 with
Image(ioctx
, image_name
) as image
:
2654 data
= rand_data(256)
2655 image
.write(data
, 0)
2656 read
= image
.read(0, 256)
2659 self
.group
.rollback_to_snap(snap_name
)
2660 with
Image(ioctx
, image_name
) as image
:
2661 read
= image
.read(0, 256)
2662 eq(read
, b
'\0' * 256)
2664 self
.group
.remove_image(ioctx
, image_name
)
2665 eq([], list(self
.group
.list_images()))
2666 self
.group
.remove_snap(snap_name
)
2667 eq([], list(self
.group
.list_snaps()))
2669 @with_setup(create_image
, remove_image
)
2672 image_name2
= get_temp_image_name()
2674 class TestMigration(object):
2676 def test_migration(self
):
2678 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2679 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2682 status
= RBD().migration_status(ioctx
, image_name
)
2683 eq(image_name
, status
['source_image_name'])
2684 eq(image_name
, status
['dest_image_name'])
2685 eq(RBD_IMAGE_MIGRATION_STATE_PREPARED
, status
['state'])
2687 with
Image(ioctx
, image_name
) as image
:
2688 source_spec
= image
.migration_source_spec()
2689 eq("native", source_spec
["type"])
2691 RBD().migration_execute(ioctx
, image_name
)
2692 RBD().migration_commit(ioctx
, image_name
)
2695 def test_migration_import(self
):
2697 with
Image(ioctx
, image_name
) as image
:
2698 image_id
= image
.id()
2699 image
.create_snap('snap')
2701 source_spec
= json
.dumps(
2703 'pool_id': ioctx
.get_pool_id(),
2704 'pool_namespace': '',
2705 'image_name': image_name
,
2706 'image_id': image_id
,
2707 'snap_name': 'snap'})
2708 dst_image_name
= get_temp_image_name()
2709 RBD().migration_prepare_import(source_spec
, ioctx
, dst_image_name
,
2710 features
=63, order
=23, stripe_unit
=1<<23,
2711 stripe_count
=1, data_pool
=None)
2713 status
= RBD().migration_status(ioctx
, dst_image_name
)
2714 eq('', status
['source_image_name'])
2715 eq(dst_image_name
, status
['dest_image_name'])
2716 eq(RBD_IMAGE_MIGRATION_STATE_PREPARED
, status
['state'])
2718 with
Image(ioctx
, dst_image_name
) as image
:
2719 source_spec
= image
.migration_source_spec()
2720 eq("native", source_spec
["type"])
2722 RBD().migration_execute(ioctx
, dst_image_name
)
2723 RBD().migration_commit(ioctx
, dst_image_name
)
2725 with
Image(ioctx
, image_name
) as image
:
2726 image
.remove_snap('snap')
2727 with
Image(ioctx
, dst_image_name
) as image
:
2728 image
.remove_snap('snap')
2730 RBD().remove(ioctx
, dst_image_name
)
2731 RBD().remove(ioctx
, image_name
)
2733 def test_migration_with_progress(self
):
2734 d
= {'received_callback': False}
2735 def progress_cb(current
, total
):
2736 d
['received_callback'] = True
2740 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2741 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2743 RBD().migration_execute(ioctx
, image_name
, on_progress
=progress_cb
)
2744 eq(True, d
['received_callback'])
2745 d
['received_callback'] = False
2747 RBD().migration_commit(ioctx
, image_name
, on_progress
=progress_cb
)
2748 eq(True, d
['received_callback'])
2751 def test_migrate_abort(self
):
2753 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2754 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2756 RBD().migration_abort(ioctx
, image_name
)
2759 def test_migrate_abort_with_progress(self
):
2760 d
= {'received_callback': False}
2761 def progress_cb(current
, total
):
2762 d
['received_callback'] = True
2766 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2767 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2769 RBD().migration_abort(ioctx
, image_name
, on_progress
=progress_cb
)
2770 eq(True, d
['received_callback'])