1 # vim: expandtab smarttab shiftwidth=4 softtabstop=4
12 from datetime
import datetime
, timedelta
13 from nose
import with_setup
, SkipTest
14 from nose
.tools
import eq_
as eq
, assert_raises
, assert_not_equal
15 from rados
import (Rados
,
16 LIBRADOS_OP_FLAG_FADVISE_DONTNEED
,
17 LIBRADOS_OP_FLAG_FADVISE_NOCACHE
,
18 LIBRADOS_OP_FLAG_FADVISE_RANDOM
)
19 from rbd
import (RBD
, Group
, Image
, ImageNotFound
, InvalidArgument
, ImageExists
,
20 ImageBusy
, ImageHasSnapshots
, ReadOnlyImage
,
21 FunctionNotSupported
, ArgumentOutOfRange
,
22 ECANCELED
, OperationCanceled
,
23 DiskQuotaExceeded
, ConnectionShutdown
, PermissionError
,
24 RBD_FEATURE_LAYERING
, RBD_FEATURE_STRIPINGV2
,
25 RBD_FEATURE_EXCLUSIVE_LOCK
, RBD_FEATURE_JOURNALING
,
26 RBD_FEATURE_DEEP_FLATTEN
, RBD_FEATURE_FAST_DIFF
,
27 RBD_FEATURE_OBJECT_MAP
,
28 RBD_MIRROR_MODE_DISABLED
, RBD_MIRROR_MODE_IMAGE
,
29 RBD_MIRROR_MODE_POOL
, RBD_MIRROR_IMAGE_ENABLED
,
30 RBD_MIRROR_IMAGE_DISABLED
, MIRROR_IMAGE_STATUS_STATE_UNKNOWN
,
31 RBD_MIRROR_IMAGE_MODE_JOURNAL
, RBD_MIRROR_IMAGE_MODE_SNAPSHOT
,
32 RBD_LOCK_MODE_EXCLUSIVE
, RBD_OPERATION_FEATURE_GROUP
,
33 RBD_SNAP_NAMESPACE_TYPE_TRASH
,
34 RBD_SNAP_NAMESPACE_TYPE_MIRROR
,
35 RBD_IMAGE_MIGRATION_STATE_PREPARED
, RBD_CONFIG_SOURCE_CONFIG
,
36 RBD_CONFIG_SOURCE_POOL
, RBD_CONFIG_SOURCE_IMAGE
,
37 RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST
,
38 RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY
,
39 RBD_MIRROR_PEER_DIRECTION_RX
, RBD_MIRROR_PEER_DIRECTION_RX_TX
,
40 RBD_SNAP_REMOVE_UNPROTECT
, RBD_SNAP_MIRROR_STATE_PRIMARY
,
41 RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED
)
54 IMG_SIZE
= 8 << 20 # 8 MiB
55 IMG_ORDER
= 22 # 4 MiB objects
57 os
.environ
["RBD_FORCE_ALLOW_V1"] = "1"
61 rados
= Rados(conffile
='')
64 pool_name
= get_temp_pool_name()
65 rados
.create_pool(pool_name
)
67 ioctx
= rados
.open_ioctx(pool_name
)
68 RBD().pool_init(ioctx
, True)
70 features
= os
.getenv("RBD_FEATURES")
71 features
= int(features
) if features
is not None else 61
73 def teardown_module():
77 rados
.delete_pool(pool_name
)
80 def get_temp_pool_name():
83 return "test-rbd-api-" + socket
.gethostname() + '-' + str(os
.getpid()) + \
86 def get_temp_image_name():
89 return "image" + str(image_idx
)
91 def get_temp_group_name():
94 return "group" + str(group_idx
)
96 def get_temp_snap_name():
99 return "snap" + str(snap_idx
)
103 image_name
= get_temp_image_name()
104 if features
is not None:
105 RBD().create(ioctx
, image_name
, IMG_SIZE
, IMG_ORDER
, old_format
=False,
106 features
=int(features
))
108 RBD().create(ioctx
, image_name
, IMG_SIZE
, IMG_ORDER
, old_format
=True)
112 if image_name
is not None:
113 RBD().remove(ioctx
, image_name
)
117 group_name
= get_temp_group_name()
118 RBD().group_create(ioctx
, group_name
)
121 if group_name
is not None:
122 RBD().group_remove(ioctx
, group_name
)
125 new_group_name
= "new" + group_name
126 RBD().group_rename(ioctx
, group_name
, new_group_name
)
128 def require_new_format():
130 def _require_new_format(*args
, **kwargs
):
134 return fn(*args
, **kwargs
)
135 return functools
.wraps(fn
)(_require_new_format
)
138 def require_features(required_features
):
140 def _require_features(*args
, **kwargs
):
144 for feature
in required_features
:
145 if feature
& features
!= feature
:
147 return fn(*args
, **kwargs
)
148 return functools
.wraps(fn
)(_require_features
)
151 def blacklist_features(blacklisted_features
):
153 def _blacklist_features(*args
, **kwargs
):
155 for feature
in blacklisted_features
:
156 if features
is not None and feature
& features
== feature
:
158 return fn(*args
, **kwargs
)
159 return functools
.wraps(fn
)(_blacklist_features
)
169 def check_default_params(format
, order
=None, features
=None, stripe_count
=None,
170 stripe_unit
=None, exception
=None):
174 for k
in ['rbd_default_format', 'rbd_default_order', 'rbd_default_features',
175 'rbd_default_stripe_count', 'rbd_default_stripe_unit']:
176 orig_vals
[k
] = rados
.conf_get(k
)
178 rados
.conf_set('rbd_default_format', str(format
))
179 if order
is not None:
180 rados
.conf_set('rbd_default_order', str(order
or 0))
181 if features
is not None:
182 rados
.conf_set('rbd_default_features', str(features
or 0))
183 if stripe_count
is not None:
184 rados
.conf_set('rbd_default_stripe_count', str(stripe_count
or 0))
185 if stripe_unit
is not None:
186 rados
.conf_set('rbd_default_stripe_unit', str(stripe_unit
or 0))
187 feature_data_pool
= 0
188 datapool
= rados
.conf_get('rbd_default_data_pool')
189 if not len(datapool
) == 0:
190 feature_data_pool
= 128
191 image_name
= get_temp_image_name()
192 if exception
is None:
193 RBD().create(ioctx
, image_name
, IMG_SIZE
, old_format
=(format
== 1))
195 with
Image(ioctx
, image_name
) as image
:
196 eq(format
== 1, image
.old_format())
198 expected_order
= int(rados
.conf_get('rbd_default_order'))
199 actual_order
= image
.stat()['order']
200 eq(expected_order
, actual_order
)
202 expected_features
= features
204 expected_features
= 0
205 elif expected_features
is None:
206 expected_features
= 61 | feature_data_pool
208 expected_features |
= feature_data_pool
209 eq(expected_features
, image
.features())
211 expected_stripe_count
= stripe_count
212 if not expected_stripe_count
or format
== 1 or \
213 features
& RBD_FEATURE_STRIPINGV2
== 0:
214 expected_stripe_count
= 1
215 eq(expected_stripe_count
, image
.stripe_count())
217 expected_stripe_unit
= stripe_unit
218 if not expected_stripe_unit
or format
== 1 or \
219 features
& RBD_FEATURE_STRIPINGV2
== 0:
220 expected_stripe_unit
= 1 << actual_order
221 eq(expected_stripe_unit
, image
.stripe_unit())
223 RBD().remove(ioctx
, image_name
)
225 assert_raises(exception
, RBD().create
, ioctx
, image_name
, IMG_SIZE
)
227 for k
, v
in orig_vals
.items():
230 def test_create_defaults():
231 # basic format 1 and 2
232 check_default_params(1)
233 check_default_params(2)
235 check_default_params(1, 0, exception
=ArgumentOutOfRange
)
236 check_default_params(2, 0, exception
=ArgumentOutOfRange
)
237 check_default_params(1, 11, exception
=ArgumentOutOfRange
)
238 check_default_params(2, 11, exception
=ArgumentOutOfRange
)
239 check_default_params(1, 65, exception
=ArgumentOutOfRange
)
240 check_default_params(2, 65, exception
=ArgumentOutOfRange
)
241 # striping and features are ignored for format 1
242 check_default_params(1, 20, 0, 1, 1)
243 check_default_params(1, 20, 3, 1, 1)
244 check_default_params(1, 20, 0, 0, 0)
245 # striping is ignored if stripingv2 is not set
246 check_default_params(2, 20, 0, 1, 1 << 20)
247 check_default_params(2, 20, RBD_FEATURE_LAYERING
, 1, 1 << 20)
248 check_default_params(2, 20, 0, 0, 0)
249 # striping with stripingv2 is fine
250 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 1, 1 << 16)
251 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 10, 1 << 20)
252 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 10, 1 << 16)
253 check_default_params(2, 20, 0, 0, 0)
254 # make sure invalid combinations of stripe unit and order are still invalid
255 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 10, 1 << 50, exception
=InvalidArgument
)
256 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 10, 100, exception
=InvalidArgument
)
257 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 0, 1, exception
=InvalidArgument
)
258 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 1, 0, exception
=InvalidArgument
)
259 # 0 stripe unit and count are still ignored
260 check_default_params(2, 22, 0, 0, 0)
262 def test_context_manager():
263 with
Rados(conffile
='') as cluster
:
264 with cluster
.open_ioctx(pool_name
) as ioctx
:
265 image_name
= get_temp_image_name()
266 RBD().create(ioctx
, image_name
, IMG_SIZE
)
267 with
Image(ioctx
, image_name
) as image
:
268 data
= rand_data(256)
270 read
= image
.read(0, 256)
271 RBD().remove(ioctx
, image_name
)
274 def test_open_read_only():
275 with
Rados(conffile
='') as cluster
:
276 with cluster
.open_ioctx(pool_name
) as ioctx
:
277 image_name
= get_temp_image_name()
278 RBD().create(ioctx
, image_name
, IMG_SIZE
)
279 data
= rand_data(256)
280 with
Image(ioctx
, image_name
) as image
:
282 image
.create_snap('snap')
283 with
Image(ioctx
, image_name
, read_only
=True) as image
:
284 read
= image
.read(0, 256)
286 assert_raises(ReadOnlyImage
, image
.write
, data
, 0)
287 assert_raises(ReadOnlyImage
, image
.create_snap
, 'test')
288 assert_raises(ReadOnlyImage
, image
.remove_snap
, 'snap')
289 assert_raises(ReadOnlyImage
, image
.rollback_to_snap
, 'snap')
290 assert_raises(ReadOnlyImage
, image
.protect_snap
, 'snap')
291 assert_raises(ReadOnlyImage
, image
.unprotect_snap
, 'snap')
292 assert_raises(ReadOnlyImage
, image
.unprotect_snap
, 'snap')
293 assert_raises(ReadOnlyImage
, image
.flatten
)
294 with
Image(ioctx
, image_name
) as image
:
295 image
.remove_snap('snap')
296 RBD().remove(ioctx
, image_name
)
301 image_name
= get_temp_image_name()
302 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
+ 'dne')
303 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
, 'snap')
305 def test_open_readonly_dne():
307 image_name
= get_temp_image_name()
308 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
+ 'dne',
310 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
, 'snap',
313 @require_new_format()
314 def test_open_by_id():
315 with
Rados(conffile
='') as cluster
:
316 with cluster
.open_ioctx(pool_name
) as ioctx
:
317 image_name
= get_temp_image_name()
318 RBD().create(ioctx
, image_name
, IMG_SIZE
)
319 with
Image(ioctx
, image_name
) as image
:
320 image_id
= image
.id()
321 with
Image(ioctx
, image_id
=image_id
) as image
:
322 eq(image
.get_name(), image_name
)
323 RBD().remove(ioctx
, image_name
)
325 def test_remove_dne():
326 assert_raises(ImageNotFound
, remove_image
)
328 def test_list_empty():
329 eq([], RBD().list(ioctx
))
331 @with_setup(create_image
, remove_image
)
333 eq([image_name
], RBD().list(ioctx
))
335 with
Image(ioctx
, image_name
) as image
:
336 image_id
= image
.id()
337 eq([{'id': image_id
, 'name': image_name
}], list(RBD().list2(ioctx
)))
339 @with_setup(create_image
)
340 def test_remove_with_progress():
341 d
= {'received_callback': False}
342 def progress_cb(current
, total
):
343 d
['received_callback'] = True
346 RBD().remove(ioctx
, image_name
, on_progress
=progress_cb
)
347 eq(True, d
['received_callback'])
349 @with_setup(create_image
)
350 def test_remove_canceled():
351 def progress_cb(current
, total
):
354 assert_raises(OperationCanceled
, RBD().remove
, ioctx
, image_name
,
355 on_progress
=progress_cb
)
357 @with_setup(create_image
, remove_image
)
360 image_name2
= get_temp_image_name()
361 rbd
.rename(ioctx
, image_name
, image_name2
)
362 eq([image_name2
], rbd
.list(ioctx
))
363 rbd
.rename(ioctx
, image_name2
, image_name
)
364 eq([image_name
], rbd
.list(ioctx
))
366 def test_pool_metadata():
368 metadata
= list(rbd
.pool_metadata_list(ioctx
))
370 assert_raises(KeyError, rbd
.pool_metadata_get
, ioctx
, "key1")
371 rbd
.pool_metadata_set(ioctx
, "key1", "value1")
372 rbd
.pool_metadata_set(ioctx
, "key2", "value2")
373 value
= rbd
.pool_metadata_get(ioctx
, "key1")
375 value
= rbd
.pool_metadata_get(ioctx
, "key2")
377 metadata
= list(rbd
.pool_metadata_list(ioctx
))
379 rbd
.pool_metadata_remove(ioctx
, "key1")
380 metadata
= list(rbd
.pool_metadata_list(ioctx
))
382 eq(metadata
[0], ("key2", "value2"))
383 rbd
.pool_metadata_remove(ioctx
, "key2")
384 assert_raises(KeyError, rbd
.pool_metadata_remove
, ioctx
, "key2")
385 metadata
= list(rbd
.pool_metadata_list(ioctx
))
390 rbd
.pool_metadata_set(ioctx
, "key" + str(i
), "X" * 1025)
391 metadata
= list(rbd
.pool_metadata_list(ioctx
))
394 rbd
.pool_metadata_remove(ioctx
, "key" + str(i
))
395 metadata
= list(rbd
.pool_metadata_list(ioctx
))
396 eq(len(metadata
), N
- i
- 1)
398 def test_config_list():
401 for option
in rbd
.config_list(ioctx
):
402 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
404 rbd
.pool_metadata_set(ioctx
, "conf_rbd_cache", "true")
406 for option
in rbd
.config_list(ioctx
):
407 if option
['name'] == "rbd_cache":
408 eq(option
['source'], RBD_CONFIG_SOURCE_POOL
)
410 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
412 rbd
.pool_metadata_remove(ioctx
, "conf_rbd_cache")
414 for option
in rbd
.config_list(ioctx
):
415 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
417 def test_pool_config_set_and_get_and_remove():
420 for option
in rbd
.config_list(ioctx
):
421 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
423 rbd
.config_set(ioctx
, "rbd_request_timed_out_seconds", "100")
424 new_value
= rbd
.config_get(ioctx
, "rbd_request_timed_out_seconds")
426 rbd
.config_remove(ioctx
, "rbd_request_timed_out_seconds")
428 for option
in rbd
.config_list(ioctx
):
429 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
431 def test_namespaces():
434 eq(False, rbd
.namespace_exists(ioctx
, 'ns1'))
435 eq([], rbd
.namespace_list(ioctx
))
436 assert_raises(ImageNotFound
, rbd
.namespace_remove
, ioctx
, 'ns1')
438 rbd
.namespace_create(ioctx
, 'ns1')
439 eq(True, rbd
.namespace_exists(ioctx
, 'ns1'))
441 assert_raises(ImageExists
, rbd
.namespace_create
, ioctx
, 'ns1')
442 eq(['ns1'], rbd
.namespace_list(ioctx
))
443 rbd
.namespace_remove(ioctx
, 'ns1')
444 eq([], rbd
.namespace_list(ioctx
))
446 @require_new_format()
447 def test_pool_stats():
451 image1
= create_image()
452 image2
= create_image()
453 image3
= create_image()
454 image4
= create_image()
455 with
Image(ioctx
, image4
) as image
:
456 image
.create_snap('snap')
459 stats
= rbd
.pool_stats_get(ioctx
)
460 eq(stats
['image_count'], 4)
461 eq(stats
['image_provisioned_bytes'], 3 * IMG_SIZE
)
462 eq(stats
['image_max_provisioned_bytes'], 4 * IMG_SIZE
)
463 eq(stats
['image_snap_count'], 1)
464 eq(stats
['trash_count'], 0)
465 eq(stats
['trash_provisioned_bytes'], 0)
466 eq(stats
['trash_max_provisioned_bytes'], 0)
467 eq(stats
['trash_snap_count'], 0)
469 rbd
.remove(ioctx
, image1
)
470 rbd
.remove(ioctx
, image2
)
471 rbd
.remove(ioctx
, image3
)
472 with
Image(ioctx
, image4
) as image
:
473 image
.remove_snap('snap')
474 rbd
.remove(ioctx
, image4
)
477 return os
.urandom(size
)
479 def check_stat(info
, size
, order
):
480 assert 'block_name_prefix' in info
481 eq(info
['size'], size
)
482 eq(info
['order'], order
)
483 eq(info
['num_objs'], size
// (1 << order
))
484 eq(info
['obj_size'], 1 << order
)
486 @require_new_format()
487 def test_features_to_string():
489 features
= RBD_FEATURE_DEEP_FLATTEN | RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_FAST_DIFF \
490 | RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP
491 expected_features_string
= "deep-flatten,exclusive-lock,fast-diff,layering,object-map"
492 features_string
= rbd
.features_to_string(features
)
493 eq(expected_features_string
, features_string
)
495 features
= RBD_FEATURE_LAYERING
496 features_string
= rbd
.features_to_string(features
)
497 eq(features_string
, "layering")
500 assert_raises(InvalidArgument
, rbd
.features_to_string
, features
)
502 @require_new_format()
503 def test_features_from_string():
505 features_string
= "deep-flatten,exclusive-lock,fast-diff,layering,object-map"
506 expected_features_bitmask
= RBD_FEATURE_DEEP_FLATTEN | RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_FAST_DIFF \
507 | RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP
508 features
= rbd
.features_from_string(features_string
)
509 eq(expected_features_bitmask
, features
)
511 features_string
= "layering"
512 features
= rbd
.features_from_string(features_string
)
513 eq(features
, RBD_FEATURE_LAYERING
)
515 class TestImage(object):
520 self
.image
= Image(ioctx
, image_name
)
527 @require_new_format()
528 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
529 def test_update_features(self
):
530 features
= self
.image
.features()
531 self
.image
.update_features(RBD_FEATURE_EXCLUSIVE_LOCK
, True)
532 eq(features | RBD_FEATURE_EXCLUSIVE_LOCK
, self
.image
.features())
534 @require_features([RBD_FEATURE_STRIPINGV2
])
535 def test_create_with_params(self
):
537 image_name
= get_temp_image_name()
539 stripe_unit
= 1 << 20
541 self
.rbd
.create(ioctx
, image_name
, IMG_SIZE
, order
,
542 False, features
, stripe_unit
, stripe_count
)
543 image
= Image(ioctx
, image_name
)
545 check_stat(info
, IMG_SIZE
, order
)
546 eq(image
.features(), features
)
547 eq(image
.stripe_unit(), stripe_unit
)
548 eq(image
.stripe_count(), stripe_count
)
550 RBD().remove(ioctx
, image_name
)
552 @require_new_format()
554 assert_not_equal(b
'', self
.image
.id())
556 def test_block_name_prefix(self
):
557 assert_not_equal(b
'', self
.image
.block_name_prefix())
559 def test_create_timestamp(self
):
560 timestamp
= self
.image
.create_timestamp()
561 assert_not_equal(0, timestamp
.year
)
562 assert_not_equal(1970, timestamp
.year
)
564 def test_access_timestamp(self
):
565 timestamp
= self
.image
.access_timestamp()
566 assert_not_equal(0, timestamp
.year
)
567 assert_not_equal(1970, timestamp
.year
)
569 def test_modify_timestamp(self
):
570 timestamp
= self
.image
.modify_timestamp()
571 assert_not_equal(0, timestamp
.year
)
572 assert_not_equal(1970, timestamp
.year
)
574 def test_invalidate_cache(self
):
575 self
.image
.write(b
'abc', 0)
576 eq(b
'abc', self
.image
.read(0, 3))
577 self
.image
.invalidate_cache()
578 eq(b
'abc', self
.image
.read(0, 3))
581 info
= self
.image
.stat()
582 check_stat(info
, IMG_SIZE
, IMG_ORDER
)
584 def test_flags(self
):
585 flags
= self
.image
.flags()
588 def test_image_auto_close(self
):
589 image
= Image(ioctx
, image_name
)
591 def test_use_after_close(self
):
593 assert_raises(InvalidArgument
, self
.image
.stat
)
595 def test_write(self
):
596 data
= rand_data(256)
597 self
.image
.write(data
, 0)
599 def test_write_with_fadvise_flags(self
):
600 data
= rand_data(256)
601 self
.image
.write(data
, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED
)
602 self
.image
.write(data
, 0, LIBRADOS_OP_FLAG_FADVISE_NOCACHE
)
604 def test_write_zeroes(self
):
605 data
= rand_data(256)
606 self
.image
.write(data
, 0)
607 self
.image
.write_zeroes(0, 256)
608 eq(self
.image
.read(256, 256), b
'\0' * 256)
611 data
= self
.image
.read(0, 20)
614 def test_read_with_fadvise_flags(self
):
615 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED
)
617 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM
)
620 def test_large_write(self
):
621 data
= rand_data(IMG_SIZE
)
622 self
.image
.write(data
, 0)
624 def test_large_read(self
):
625 data
= self
.image
.read(0, IMG_SIZE
)
626 eq(data
, b
'\0' * IMG_SIZE
)
628 def test_write_read(self
):
629 data
= rand_data(256)
631 self
.image
.write(data
, offset
)
632 read
= self
.image
.read(offset
, 256)
635 def test_read_bad_offset(self
):
636 assert_raises(InvalidArgument
, self
.image
.read
, IMG_SIZE
+ 1, IMG_SIZE
)
638 def test_resize(self
):
639 new_size
= IMG_SIZE
* 2
640 self
.image
.resize(new_size
)
641 info
= self
.image
.stat()
642 check_stat(info
, new_size
, IMG_ORDER
)
644 def test_resize_allow_shrink_False(self
):
645 new_size
= IMG_SIZE
* 2
646 self
.image
.resize(new_size
)
647 info
= self
.image
.stat()
648 check_stat(info
, new_size
, IMG_ORDER
)
649 assert_raises(InvalidArgument
, self
.image
.resize
, IMG_SIZE
, False)
652 eq(IMG_SIZE
, self
.image
.size())
653 self
.image
.create_snap('snap1')
654 new_size
= IMG_SIZE
* 2
655 self
.image
.resize(new_size
)
656 eq(new_size
, self
.image
.size())
657 self
.image
.create_snap('snap2')
658 self
.image
.set_snap('snap2')
659 eq(new_size
, self
.image
.size())
660 self
.image
.set_snap('snap1')
661 eq(IMG_SIZE
, self
.image
.size())
662 self
.image
.set_snap(None)
663 eq(new_size
, self
.image
.size())
664 self
.image
.remove_snap('snap1')
665 self
.image
.remove_snap('snap2')
667 def test_resize_down(self
):
668 new_size
= IMG_SIZE
// 2
669 data
= rand_data(256)
670 self
.image
.write(data
, IMG_SIZE
// 2);
671 self
.image
.resize(new_size
)
672 self
.image
.resize(IMG_SIZE
)
673 read
= self
.image
.read(IMG_SIZE
// 2, 256)
674 eq(b
'\0' * 256, read
)
676 def test_resize_bytes(self
):
677 new_size
= IMG_SIZE
// 2 - 5
678 data
= rand_data(256)
679 self
.image
.write(data
, IMG_SIZE
// 2 - 10);
680 self
.image
.resize(new_size
)
681 self
.image
.resize(IMG_SIZE
)
682 read
= self
.image
.read(IMG_SIZE
// 2 - 10, 5)
684 read
= self
.image
.read(IMG_SIZE
// 2 - 5, 251)
685 eq(b
'\0' * 251, read
)
687 def _test_copy(self
, features
=None, order
=None, stripe_unit
=None,
690 data
= rand_data(256)
691 self
.image
.write(data
, 256)
692 image_name
= get_temp_image_name()
694 self
.image
.copy(ioctx
, image_name
)
696 self
.image
.copy(ioctx
, image_name
, features
)
697 elif stripe_unit
is None:
698 self
.image
.copy(ioctx
, image_name
, features
, order
)
699 elif stripe_count
is None:
700 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
)
702 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
,
704 assert_raises(ImageExists
, self
.image
.copy
, ioctx
, image_name
)
705 copy
= Image(ioctx
, image_name
)
706 copy_data
= copy
.read(256, 256)
708 self
.rbd
.remove(ioctx
, image_name
)
714 def test_copy2(self
):
715 self
._test
_copy
(self
.image
.features(), self
.image
.stat()['order'])
717 @require_features([RBD_FEATURE_STRIPINGV2
])
718 def test_copy3(self
):
720 self
._test
_copy
(features
, self
.image
.stat()['order'],
721 self
.image
.stripe_unit(), self
.image
.stripe_count())
723 def test_deep_copy(self
):
726 self
.image
.write(b
'a' * 256, 0)
727 self
.image
.create_snap('snap1')
728 self
.image
.write(b
'b' * 256, 0)
729 dst_name
= get_temp_image_name()
730 self
.image
.deep_copy(ioctx
, dst_name
, features
=features
,
731 order
=self
.image
.stat()['order'],
732 stripe_unit
=self
.image
.stripe_unit(),
733 stripe_count
=self
.image
.stripe_count(),
735 self
.image
.remove_snap('snap1')
736 with
Image(ioctx
, dst_name
, 'snap1') as copy
:
737 copy_data
= copy
.read(0, 256)
738 eq(b
'a' * 256, copy_data
)
739 with
Image(ioctx
, dst_name
) as copy
:
740 copy_data
= copy
.read(0, 256)
741 eq(b
'b' * 256, copy_data
)
742 copy
.remove_snap('snap1')
743 self
.rbd
.remove(ioctx
, dst_name
)
745 @require_features([RBD_FEATURE_LAYERING
])
746 def test_deep_copy_clone(self
):
749 self
.image
.write(b
'a' * 256, 0)
750 self
.image
.create_snap('snap1')
751 self
.image
.write(b
'b' * 256, 0)
752 self
.image
.protect_snap('snap1')
753 clone_name
= get_temp_image_name()
754 dst_name
= get_temp_image_name()
755 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name
)
756 with
Image(ioctx
, clone_name
) as child
:
757 child
.create_snap('snap1')
758 child
.deep_copy(ioctx
, dst_name
, features
=features
,
759 order
=self
.image
.stat()['order'],
760 stripe_unit
=self
.image
.stripe_unit(),
761 stripe_count
=self
.image
.stripe_count(),
763 child
.remove_snap('snap1')
765 with
Image(ioctx
, dst_name
) as copy
:
766 copy_data
= copy
.read(0, 256)
767 eq(b
'a' * 256, copy_data
)
768 copy
.remove_snap('snap1')
769 self
.rbd
.remove(ioctx
, dst_name
)
770 self
.rbd
.remove(ioctx
, clone_name
)
771 self
.image
.unprotect_snap('snap1')
772 self
.image
.remove_snap('snap1')
774 def test_create_snap(self
):
776 self
.image
.create_snap('snap1')
777 read
= self
.image
.read(0, 256)
778 eq(read
, b
'\0' * 256)
779 data
= rand_data(256)
780 self
.image
.write(data
, 0)
781 read
= self
.image
.read(0, 256)
783 at_snapshot
= Image(ioctx
, image_name
, 'snap1')
784 snap_data
= at_snapshot
.read(0, 256)
786 eq(snap_data
, b
'\0' * 256)
787 self
.image
.remove_snap('snap1')
789 def test_create_snap_exists(self
):
790 self
.image
.create_snap('snap1')
791 assert_raises(ImageExists
, self
.image
.create_snap
, 'snap1')
792 self
.image
.remove_snap('snap1')
794 def test_list_snaps(self
):
795 eq([], list(self
.image
.list_snaps()))
796 self
.image
.create_snap('snap1')
797 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
798 self
.image
.create_snap('snap2')
799 eq(['snap1', 'snap2'], [snap
['name'] for snap
in self
.image
.list_snaps()])
800 self
.image
.remove_snap('snap1')
801 self
.image
.remove_snap('snap2')
803 def test_list_snaps_iterator_auto_close(self
):
804 self
.image
.create_snap('snap1')
805 self
.image
.list_snaps()
806 self
.image
.remove_snap('snap1')
808 def test_remove_snap(self
):
809 eq([], list(self
.image
.list_snaps()))
810 self
.image
.create_snap('snap1')
811 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
812 self
.image
.remove_snap('snap1')
813 eq([], list(self
.image
.list_snaps()))
815 def test_remove_snap_not_found(self
):
816 assert_raises(ImageNotFound
, self
.image
.remove_snap
, 'snap1')
818 @require_features([RBD_FEATURE_LAYERING
])
819 def test_remove_snap2(self
):
820 self
.image
.create_snap('snap1')
821 self
.image
.protect_snap('snap1')
822 assert(self
.image
.is_protected_snap('snap1'))
823 self
.image
.remove_snap2('snap1', RBD_SNAP_REMOVE_UNPROTECT
)
824 eq([], list(self
.image
.list_snaps()))
826 def test_remove_snap_by_id(self
):
827 eq([], list(self
.image
.list_snaps()))
828 self
.image
.create_snap('snap1')
829 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
830 for snap
in self
.image
.list_snaps():
832 self
.image
.remove_snap_by_id(snap_id
)
833 eq([], list(self
.image
.list_snaps()))
835 def test_rename_snap(self
):
836 eq([], list(self
.image
.list_snaps()))
837 self
.image
.create_snap('snap1')
838 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
839 self
.image
.rename_snap("snap1", "snap1-rename")
840 eq(['snap1-rename'], [snap
['name'] for snap
in self
.image
.list_snaps()])
841 self
.image
.remove_snap('snap1-rename')
842 eq([], list(self
.image
.list_snaps()))
844 @require_features([RBD_FEATURE_LAYERING
])
845 def test_protect_snap(self
):
846 self
.image
.create_snap('snap1')
847 assert(not self
.image
.is_protected_snap('snap1'))
848 self
.image
.protect_snap('snap1')
849 assert(self
.image
.is_protected_snap('snap1'))
850 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
851 self
.image
.unprotect_snap('snap1')
852 assert(not self
.image
.is_protected_snap('snap1'))
853 self
.image
.remove_snap('snap1')
854 assert_raises(ImageNotFound
, self
.image
.unprotect_snap
, 'snap1')
855 assert_raises(ImageNotFound
, self
.image
.is_protected_snap
, 'snap1')
857 def test_snap_exists(self
):
858 self
.image
.create_snap('snap1')
859 eq(self
.image
.snap_exists('snap1'), True)
860 self
.image
.remove_snap('snap1')
861 eq(self
.image
.snap_exists('snap1'), False)
863 def test_snap_timestamp(self
):
864 self
.image
.create_snap('snap1')
865 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
866 for snap
in self
.image
.list_snaps():
868 time
= self
.image
.get_snap_timestamp(snap_id
)
869 assert_not_equal(b
'', time
.year
)
870 assert_not_equal(0, time
.year
)
871 assert_not_equal(time
.year
, '1970')
872 self
.image
.remove_snap('snap1')
874 def test_limit_snaps(self
):
875 self
.image
.set_snap_limit(2)
876 eq(2, self
.image
.get_snap_limit())
877 self
.image
.create_snap('snap1')
878 self
.image
.create_snap('snap2')
879 assert_raises(DiskQuotaExceeded
, self
.image
.create_snap
, 'snap3')
880 self
.image
.remove_snap_limit()
881 self
.image
.create_snap('snap3')
883 self
.image
.remove_snap('snap1')
884 self
.image
.remove_snap('snap2')
885 self
.image
.remove_snap('snap3')
887 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
888 def test_remove_with_exclusive_lock(self
):
889 assert_raises(ImageBusy
, remove_image
)
891 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
892 def test_remove_with_snap(self
):
893 self
.image
.create_snap('snap1')
894 assert_raises(ImageHasSnapshots
, remove_image
)
895 self
.image
.remove_snap('snap1')
897 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
898 def test_remove_with_watcher(self
):
899 data
= rand_data(256)
900 self
.image
.write(data
, 0)
901 assert_raises(ImageBusy
, remove_image
)
902 read
= self
.image
.read(0, 256)
905 def test_rollback_to_snap(self
):
906 self
.image
.write(b
'\0' * 256, 0)
907 self
.image
.create_snap('snap1')
908 read
= self
.image
.read(0, 256)
909 eq(read
, b
'\0' * 256)
910 data
= rand_data(256)
911 self
.image
.write(data
, 0)
912 read
= self
.image
.read(0, 256)
914 self
.image
.rollback_to_snap('snap1')
915 read
= self
.image
.read(0, 256)
916 eq(read
, b
'\0' * 256)
917 self
.image
.remove_snap('snap1')
919 def test_rollback_to_snap_sparse(self
):
920 self
.image
.create_snap('snap1')
921 read
= self
.image
.read(0, 256)
922 eq(read
, b
'\0' * 256)
923 data
= rand_data(256)
924 self
.image
.write(data
, 0)
925 read
= self
.image
.read(0, 256)
927 self
.image
.rollback_to_snap('snap1')
928 read
= self
.image
.read(0, 256)
929 eq(read
, b
'\0' * 256)
930 self
.image
.remove_snap('snap1')
932 def test_rollback_with_resize(self
):
933 read
= self
.image
.read(0, 256)
934 eq(read
, b
'\0' * 256)
935 data
= rand_data(256)
936 self
.image
.write(data
, 0)
937 self
.image
.create_snap('snap1')
938 read
= self
.image
.read(0, 256)
940 new_size
= IMG_SIZE
* 2
941 self
.image
.resize(new_size
)
942 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
943 self
.image
.write(data
, new_size
- 256)
944 self
.image
.create_snap('snap2')
945 read
= self
.image
.read(new_size
- 256, 256)
947 self
.image
.rollback_to_snap('snap1')
948 check_stat(self
.image
.stat(), IMG_SIZE
, IMG_ORDER
)
949 assert_raises(InvalidArgument
, self
.image
.read
, new_size
- 256, 256)
950 self
.image
.rollback_to_snap('snap2')
951 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
952 read
= self
.image
.read(new_size
- 256, 256)
954 self
.image
.remove_snap('snap1')
955 self
.image
.remove_snap('snap2')
957 def test_set_snap(self
):
958 self
.image
.write(b
'\0' * 256, 0)
959 self
.image
.create_snap('snap1')
960 read
= self
.image
.read(0, 256)
961 eq(read
, b
'\0' * 256)
962 data
= rand_data(256)
963 self
.image
.write(data
, 0)
964 read
= self
.image
.read(0, 256)
966 self
.image
.set_snap('snap1')
967 read
= self
.image
.read(0, 256)
968 eq(read
, b
'\0' * 256)
969 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
970 self
.image
.remove_snap('snap1')
972 def test_set_no_snap(self
):
973 self
.image
.write(b
'\0' * 256, 0)
974 self
.image
.create_snap('snap1')
975 read
= self
.image
.read(0, 256)
976 eq(read
, b
'\0' * 256)
977 data
= rand_data(256)
978 self
.image
.write(data
, 0)
979 read
= self
.image
.read(0, 256)
981 self
.image
.set_snap('snap1')
982 read
= self
.image
.read(0, 256)
983 eq(read
, b
'\0' * 256)
984 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
985 self
.image
.set_snap(None)
986 read
= self
.image
.read(0, 256)
988 self
.image
.remove_snap('snap1')
990 def test_set_snap_by_id(self
):
991 self
.image
.write(b
'\0' * 256, 0)
992 self
.image
.create_snap('snap1')
993 read
= self
.image
.read(0, 256)
994 eq(read
, b
'\0' * 256)
995 data
= rand_data(256)
996 self
.image
.write(data
, 0)
997 read
= self
.image
.read(0, 256)
999 snaps
= list(self
.image
.list_snaps())
1000 self
.image
.set_snap_by_id(snaps
[0]['id'])
1001 read
= self
.image
.read(0, 256)
1002 eq(read
, b
'\0' * 256)
1003 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
1004 self
.image
.set_snap_by_id(None)
1005 read
= self
.image
.read(0, 256)
1007 self
.image
.remove_snap('snap1')
1009 def test_snap_get_name(self
):
1010 eq([], list(self
.image
.list_snaps()))
1011 self
.image
.create_snap('snap1')
1012 self
.image
.create_snap('snap2')
1013 self
.image
.create_snap('snap3')
1015 for snap
in self
.image
.list_snaps():
1016 expected_snap_name
= self
.image
.snap_get_name(snap
['id'])
1017 eq(expected_snap_name
, snap
['name'])
1018 self
.image
.remove_snap('snap1')
1019 self
.image
.remove_snap('snap2')
1020 self
.image
.remove_snap('snap3')
1021 eq([], list(self
.image
.list_snaps()))
1023 assert_raises(ImageNotFound
, self
.image
.snap_get_name
, 1)
1025 def test_snap_get_id(self
):
1026 eq([], list(self
.image
.list_snaps()))
1027 self
.image
.create_snap('snap1')
1028 self
.image
.create_snap('snap2')
1029 self
.image
.create_snap('snap3')
1031 for snap
in self
.image
.list_snaps():
1032 expected_snap_id
= self
.image
.snap_get_id(snap
['name'])
1033 eq(expected_snap_id
, snap
['id'])
1034 self
.image
.remove_snap('snap1')
1035 self
.image
.remove_snap('snap2')
1036 self
.image
.remove_snap('snap3')
1037 eq([], list(self
.image
.list_snaps()))
1039 assert_raises(ImageNotFound
, self
.image
.snap_get_id
, 'snap1')
1041 def test_set_snap_sparse(self
):
1042 self
.image
.create_snap('snap1')
1043 read
= self
.image
.read(0, 256)
1044 eq(read
, b
'\0' * 256)
1045 data
= rand_data(256)
1046 self
.image
.write(data
, 0)
1047 read
= self
.image
.read(0, 256)
1049 self
.image
.set_snap('snap1')
1050 read
= self
.image
.read(0, 256)
1051 eq(read
, b
'\0' * 256)
1052 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
1053 self
.image
.remove_snap('snap1')
1055 def test_many_snaps(self
):
1057 for i
in range(num_snaps
):
1058 self
.image
.create_snap(str(i
))
1059 snaps
= sorted(self
.image
.list_snaps(),
1060 key
=lambda snap
: int(snap
['name']))
1061 eq(len(snaps
), num_snaps
)
1062 for i
, snap
in enumerate(snaps
):
1063 eq(snap
['size'], IMG_SIZE
)
1064 eq(snap
['name'], str(i
))
1065 for i
in range(num_snaps
):
1066 self
.image
.remove_snap(str(i
))
1068 def test_set_snap_deleted(self
):
1069 self
.image
.write(b
'\0' * 256, 0)
1070 self
.image
.create_snap('snap1')
1071 read
= self
.image
.read(0, 256)
1072 eq(read
, b
'\0' * 256)
1073 data
= rand_data(256)
1074 self
.image
.write(data
, 0)
1075 read
= self
.image
.read(0, 256)
1077 self
.image
.set_snap('snap1')
1078 self
.image
.remove_snap('snap1')
1079 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
1080 self
.image
.set_snap(None)
1081 read
= self
.image
.read(0, 256)
1084 def test_set_snap_recreated(self
):
1085 self
.image
.write(b
'\0' * 256, 0)
1086 self
.image
.create_snap('snap1')
1087 read
= self
.image
.read(0, 256)
1088 eq(read
, b
'\0' * 256)
1089 data
= rand_data(256)
1090 self
.image
.write(data
, 0)
1091 read
= self
.image
.read(0, 256)
1093 self
.image
.set_snap('snap1')
1094 self
.image
.remove_snap('snap1')
1095 self
.image
.create_snap('snap1')
1096 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
1097 self
.image
.set_snap(None)
1098 read
= self
.image
.read(0, 256)
1100 self
.image
.remove_snap('snap1')
1102 def test_lock_unlock(self
):
1103 assert_raises(ImageNotFound
, self
.image
.unlock
, '')
1104 self
.image
.lock_exclusive('')
1105 assert_raises(ImageExists
, self
.image
.lock_exclusive
, '')
1106 assert_raises(ImageBusy
, self
.image
.lock_exclusive
, 'test')
1107 assert_raises(ImageExists
, self
.image
.lock_shared
, '', '')
1108 assert_raises(ImageBusy
, self
.image
.lock_shared
, 'foo', '')
1109 self
.image
.unlock('')
1111 def test_list_lockers(self
):
1112 eq([], self
.image
.list_lockers())
1113 self
.image
.lock_exclusive('test')
1114 lockers
= self
.image
.list_lockers()
1115 eq(1, len(lockers
['lockers']))
1116 _
, cookie
, _
= lockers
['lockers'][0]
1118 eq('', lockers
['tag'])
1119 assert lockers
['exclusive']
1120 self
.image
.unlock('test')
1121 eq([], self
.image
.list_lockers())
1124 for i
in range(num_shared
):
1125 self
.image
.lock_shared(str(i
), 'tag')
1126 lockers
= self
.image
.list_lockers()
1127 eq('tag', lockers
['tag'])
1128 assert not lockers
['exclusive']
1129 eq(num_shared
, len(lockers
['lockers']))
1130 cookies
= sorted(map(lambda x
: x
[1], lockers
['lockers']))
1131 for i
in range(num_shared
):
1132 eq(str(i
), cookies
[i
])
1133 self
.image
.unlock(str(i
))
1134 eq([], self
.image
.list_lockers())
1136 def test_diff_iterate(self
):
1137 check_diff(self
.image
, 0, IMG_SIZE
, None, [])
1138 self
.image
.write(b
'a' * 256, 0)
1139 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 256, True)])
1140 self
.image
.write(b
'b' * 256, 256)
1141 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
1142 self
.image
.discard(128, 256)
1143 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
1145 self
.image
.create_snap('snap1')
1146 self
.image
.discard(0, 1 << IMG_ORDER
)
1147 self
.image
.create_snap('snap2')
1148 self
.image
.set_snap('snap2')
1149 check_diff(self
.image
, 0, IMG_SIZE
, 'snap1', [(0, 512, False)])
1150 self
.image
.remove_snap('snap1')
1151 self
.image
.remove_snap('snap2')
1153 def test_aio_read(self
):
1154 # this is a list so that the local cb() can modify it
1159 # test1: success case
1160 comp
= self
.image
.aio_read(0, 20, cb
)
1161 comp
.wait_for_complete_and_cb()
1162 eq(retval
[0], b
'\0' * 20)
1163 eq(comp
.get_return_value(), 20)
1164 eq(sys
.getrefcount(comp
), 2)
1168 comp
= self
.image
.aio_read(IMG_SIZE
, 20, cb
)
1169 comp
.wait_for_complete_and_cb()
1171 assert(comp
.get_return_value() < 0)
1172 eq(sys
.getrefcount(comp
), 2)
1174 def test_aio_write(self
):
1177 retval
[0] = comp
.get_return_value()
1179 data
= rand_data(256)
1180 comp
= self
.image
.aio_write(data
, 256, cb
)
1181 comp
.wait_for_complete_and_cb()
1183 eq(comp
.get_return_value(), 0)
1184 eq(sys
.getrefcount(comp
), 2)
1185 eq(self
.image
.read(256, 256), data
)
1187 def test_aio_discard(self
):
1190 retval
[0] = comp
.get_return_value()
1192 data
= rand_data(256)
1193 self
.image
.write(data
, 0)
1194 comp
= self
.image
.aio_discard(0, 256, cb
)
1195 comp
.wait_for_complete_and_cb()
1197 eq(comp
.get_return_value(), 0)
1198 eq(sys
.getrefcount(comp
), 2)
1199 eq(self
.image
.read(256, 256), b
'\0' * 256)
1201 def test_aio_write_zeroes(self
):
1204 retval
[0] = comp
.get_return_value()
1206 data
= rand_data(256)
1207 self
.image
.write(data
, 0)
1208 comp
= self
.image
.aio_write_zeroes(0, 256, cb
)
1209 comp
.wait_for_complete_and_cb()
1211 eq(comp
.get_return_value(), 0)
1212 eq(sys
.getrefcount(comp
), 2)
1213 eq(self
.image
.read(256, 256), b
'\0' * 256)
1215 def test_aio_flush(self
):
1218 retval
[0] = comp
.get_return_value()
1220 comp
= self
.image
.aio_flush(cb
)
1221 comp
.wait_for_complete_and_cb()
1223 eq(sys
.getrefcount(comp
), 2)
1225 def test_metadata(self
):
1226 metadata
= list(self
.image
.metadata_list())
1227 eq(len(metadata
), 0)
1228 assert_raises(KeyError, self
.image
.metadata_get
, "key1")
1229 self
.image
.metadata_set("key1", "value1")
1230 self
.image
.metadata_set("key2", "value2")
1231 value
= self
.image
.metadata_get("key1")
1233 value
= self
.image
.metadata_get("key2")
1235 metadata
= list(self
.image
.metadata_list())
1236 eq(len(metadata
), 2)
1237 self
.image
.metadata_remove("key1")
1238 metadata
= list(self
.image
.metadata_list())
1239 eq(len(metadata
), 1)
1240 eq(metadata
[0], ("key2", "value2"))
1241 self
.image
.metadata_remove("key2")
1242 assert_raises(KeyError, self
.image
.metadata_remove
, "key2")
1243 metadata
= list(self
.image
.metadata_list())
1244 eq(len(metadata
), 0)
1248 self
.image
.metadata_set("key" + str(i
), "X" * 1025)
1249 metadata
= list(self
.image
.metadata_list())
1250 eq(len(metadata
), N
)
1252 self
.image
.metadata_remove("key" + str(i
))
1253 metadata
= list(self
.image
.metadata_list())
1254 eq(len(metadata
), N
- i
- 1)
1256 def test_watchers_list(self
):
1257 watchers
= list(self
.image
.watchers_list())
1258 # The image is open (in r/w mode) from setup, so expect there to be one
1260 eq(len(watchers
), 1)
1262 def test_config_list(self
):
1263 with
Image(ioctx
, image_name
) as image
:
1264 for option
in image
.config_list():
1265 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1267 image
.metadata_set("conf_rbd_cache", "true")
1269 for option
in image
.config_list():
1270 if option
['name'] == "rbd_cache":
1271 eq(option
['source'], RBD_CONFIG_SOURCE_IMAGE
)
1273 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1275 image
.metadata_remove("conf_rbd_cache")
1277 for option
in image
.config_list():
1278 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1280 def test_image_config_set_and_get_and_remove(self
):
1281 with
Image(ioctx
, image_name
) as image
:
1282 for option
in image
.config_list():
1283 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1285 image
.config_set("rbd_request_timed_out_seconds", "100")
1286 modify_value
= image
.config_get("rbd_request_timed_out_seconds")
1287 eq(modify_value
, '100')
1289 image
.config_remove("rbd_request_timed_out_seconds")
1291 for option
in image
.config_list():
1292 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1294 def test_sparsify(self
):
1295 assert_raises(InvalidArgument
, self
.image
.sparsify
, 16)
1296 self
.image
.sparsify(4096)
1298 class TestImageId(object):
1303 self
.image
= Image(ioctx
, image_name
)
1304 self
.image2
= Image(ioctx
, None, None, False, self
.image
.id())
1313 def test_read(self
):
1314 data
= self
.image2
.read(0, 20)
1315 eq(data
, b
'\0' * 20)
1317 def test_write(self
):
1318 data
= rand_data(256)
1319 self
.image2
.write(data
, 0)
1321 def test_resize(self
):
1322 new_size
= IMG_SIZE
* 2
1323 self
.image2
.resize(new_size
)
1324 info
= self
.image2
.stat()
1325 check_stat(info
, new_size
, IMG_ORDER
)
1327 def check_diff(image
, offset
, length
, from_snapshot
, expected
):
1329 def cb(offset
, length
, exists
):
1330 extents
.append((offset
, length
, exists
))
1331 image
.diff_iterate(0, IMG_SIZE
, None, cb
)
1332 eq(extents
, expected
)
1334 class TestClone(object):
1336 @require_features([RBD_FEATURE_LAYERING
])
1342 self
.image
= Image(ioctx
, image_name
)
1343 data
= rand_data(256)
1344 self
.image
.write(data
, IMG_SIZE
// 2)
1345 self
.image
.create_snap('snap1')
1347 self
.image
.protect_snap('snap1')
1348 self
.clone_name
= get_temp_image_name()
1349 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
1351 self
.clone
= Image(ioctx
, self
.clone_name
)
1356 self
.rbd
.remove(ioctx
, self
.clone_name
)
1357 self
.image
.unprotect_snap('snap1')
1358 self
.image
.remove_snap('snap1')
1362 def _test_with_params(self
, features
=None, order
=None, stripe_unit
=None,
1364 self
.image
.create_snap('snap2')
1365 self
.image
.protect_snap('snap2')
1366 clone_name2
= get_temp_image_name()
1367 if features
is None:
1368 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
)
1370 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1372 elif stripe_unit
is None:
1373 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1375 elif stripe_count
is None:
1376 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1377 features
, order
, stripe_unit
)
1379 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1380 features
, order
, stripe_unit
, stripe_count
)
1381 self
.rbd
.remove(ioctx
, clone_name2
)
1382 self
.image
.unprotect_snap('snap2')
1383 self
.image
.remove_snap('snap2')
1385 def test_with_params(self
):
1386 self
._test
_with
_params
()
1388 def test_with_params2(self
):
1390 self
._test
_with
_params
(features
, self
.image
.stat()['order'])
1392 @require_features([RBD_FEATURE_STRIPINGV2
])
1393 def test_with_params3(self
):
1395 self
._test
_with
_params
(features
, self
.image
.stat()['order'],
1396 self
.image
.stripe_unit(),
1397 self
.image
.stripe_count())
1399 def test_unprotected(self
):
1400 self
.image
.create_snap('snap2')
1402 clone_name2
= get_temp_image_name()
1403 rados
.conf_set("rbd_default_clone_format", "1")
1404 assert_raises(InvalidArgument
, self
.rbd
.clone
, ioctx
, image_name
,
1405 'snap2', ioctx
, clone_name2
, features
)
1406 rados
.conf_set("rbd_default_clone_format", "auto")
1407 self
.image
.remove_snap('snap2')
1409 def test_unprotect_with_children(self
):
1411 # can't remove a snapshot that has dependent clones
1412 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
1414 # validate parent info of clone created by TestClone.setUp
1415 (pool
, image
, snap
) = self
.clone
.parent_info()
1417 eq(image
, image_name
)
1419 eq(self
.image
.id(), self
.clone
.parent_id())
1421 # create a new pool...
1422 pool_name2
= get_temp_pool_name()
1423 rados
.create_pool(pool_name2
)
1424 other_ioctx
= rados
.open_ioctx(pool_name2
)
1425 other_ioctx
.application_enable('rbd')
1427 # ...with a clone of the same parent
1428 other_clone_name
= get_temp_image_name()
1429 rados
.conf_set("rbd_default_clone_format", "1")
1430 self
.rbd
.clone(ioctx
, image_name
, 'snap1', other_ioctx
,
1431 other_clone_name
, features
)
1432 rados
.conf_set("rbd_default_clone_format", "auto")
1433 self
.other_clone
= Image(other_ioctx
, other_clone_name
)
1434 # validate its parent info
1435 (pool
, image
, snap
) = self
.other_clone
.parent_info()
1437 eq(image
, image_name
)
1439 eq(self
.image
.id(), self
.other_clone
.parent_id())
1441 # can't unprotect snap with children
1442 assert_raises(ImageBusy
, self
.image
.unprotect_snap
, 'snap1')
1444 # 2 children, check that cannot remove the parent snap
1445 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
1447 # close and remove other pool's clone
1448 self
.other_clone
.close()
1449 self
.rbd
.remove(other_ioctx
, other_clone_name
)
1451 # check that we cannot yet remove the parent snap
1452 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
1455 rados
.delete_pool(pool_name2
)
1457 # unprotect, remove parent snap happen in cleanup, and should succeed
1459 def test_stat(self
):
1460 image_info
= self
.image
.stat()
1461 clone_info
= self
.clone
.stat()
1462 eq(clone_info
['size'], image_info
['size'])
1463 eq(clone_info
['size'], self
.clone
.overlap())
1465 def test_resize_stat(self
):
1466 self
.clone
.resize(IMG_SIZE
// 2)
1467 image_info
= self
.image
.stat()
1468 clone_info
= self
.clone
.stat()
1469 eq(clone_info
['size'], IMG_SIZE
// 2)
1470 eq(image_info
['size'], IMG_SIZE
)
1471 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
1473 self
.clone
.resize(IMG_SIZE
* 2)
1474 image_info
= self
.image
.stat()
1475 clone_info
= self
.clone
.stat()
1476 eq(clone_info
['size'], IMG_SIZE
* 2)
1477 eq(image_info
['size'], IMG_SIZE
)
1478 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
1480 def test_resize_io(self
):
1481 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1482 self
.image
.resize(0)
1483 self
.clone
.resize(IMG_SIZE
// 2 + 128)
1484 child_data
= self
.clone
.read(IMG_SIZE
// 2, 128)
1485 eq(child_data
, parent_data
[:128])
1486 self
.clone
.resize(IMG_SIZE
)
1487 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1488 eq(child_data
, parent_data
[:128] + (b
'\0' * 128))
1489 self
.clone
.resize(IMG_SIZE
// 2 + 1)
1490 child_data
= self
.clone
.read(IMG_SIZE
// 2, 1)
1491 eq(child_data
, parent_data
[0:1])
1492 self
.clone
.resize(0)
1493 self
.clone
.resize(IMG_SIZE
)
1494 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1495 eq(child_data
, b
'\0' * 256)
1497 def test_read(self
):
1498 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1499 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1500 eq(child_data
, parent_data
)
1502 def test_write(self
):
1503 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1504 new_data
= rand_data(256)
1505 self
.clone
.write(new_data
, IMG_SIZE
// 2 + 256)
1506 child_data
= self
.clone
.read(IMG_SIZE
// 2 + 256, 256)
1507 eq(child_data
, new_data
)
1508 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1509 eq(child_data
, parent_data
)
1510 parent_data
= self
.image
.read(IMG_SIZE
// 2 + 256, 256)
1511 eq(parent_data
, b
'\0' * 256)
1513 def check_children(self
, expected
):
1514 actual
= self
.image
.list_children()
1515 # dedup for cache pools until
1516 # http://tracker.ceph.com/issues/8187 is fixed
1517 deduped
= set([(pool_name
, image
[1]) for image
in actual
])
1518 eq(deduped
, set(expected
))
1520 def check_children2(self
, expected
):
1521 actual
= [{k
:v
for k
,v
in x
.items() if k
in expected
[0]} \
1522 for x
in self
.image
.list_children2()]
1523 eq(actual
, expected
)
1525 def check_descendants(self
, expected
):
1526 eq(list(self
.image
.list_descendants()), expected
)
1528 def get_image_id(self
, ioctx
, name
):
1529 with
Image(ioctx
, name
) as image
:
1532 def test_list_children(self
):
1535 self
.image
.set_snap('snap1')
1536 self
.check_children([(pool_name
, self
.clone_name
)])
1537 self
.check_children2(
1538 [{'pool': pool_name
, 'pool_namespace': '',
1539 'image': self
.clone_name
, 'trash': False,
1540 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1541 self
.check_descendants(
1542 [{'pool': pool_name
, 'pool_namespace': '',
1543 'image': self
.clone_name
, 'trash': False,
1544 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1546 self
.rbd
.remove(ioctx
, self
.clone_name
)
1547 eq(self
.image
.list_children(), [])
1548 eq(list(self
.image
.list_children2()), [])
1549 eq(list(self
.image
.list_descendants()), [])
1551 clone_name
= get_temp_image_name() + '_'
1552 expected_children
= []
1553 expected_children2
= []
1555 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
,
1556 clone_name
+ str(i
), features
)
1557 expected_children
.append((pool_name
, clone_name
+ str(i
)))
1558 expected_children2
.append(
1559 {'pool': pool_name
, 'pool_namespace': '',
1560 'image': clone_name
+ str(i
), 'trash': False,
1561 'id': self
.get_image_id(ioctx
, clone_name
+ str(i
))})
1562 self
.check_children(expected_children
)
1563 self
.check_children2(expected_children2
)
1564 self
.check_descendants(expected_children2
)
1566 image6_id
= self
.get_image_id(ioctx
, clone_name
+ str(5))
1567 RBD().trash_move(ioctx
, clone_name
+ str(5), 0)
1568 expected_children
.remove((pool_name
, clone_name
+ str(5)))
1569 for item
in expected_children2
:
1570 for k
, v
in item
.items():
1572 item
["trash"] = True
1573 self
.check_children(expected_children
)
1574 self
.check_children2(expected_children2
)
1575 self
.check_descendants(expected_children2
)
1577 RBD().trash_restore(ioctx
, image6_id
, clone_name
+ str(5))
1578 expected_children
.append((pool_name
, clone_name
+ str(5)))
1579 for item
in expected_children2
:
1580 for k
, v
in item
.items():
1582 item
["trash"] = False
1583 self
.check_children(expected_children
)
1584 self
.check_children2(expected_children2
)
1585 self
.check_descendants(expected_children2
)
1588 self
.rbd
.remove(ioctx
, clone_name
+ str(i
))
1589 expected_children
.remove((pool_name
, clone_name
+ str(i
)))
1590 expected_children2
.pop(0)
1591 self
.check_children(expected_children
)
1592 self
.check_children2(expected_children2
)
1593 self
.check_descendants(expected_children2
)
1595 eq(self
.image
.list_children(), [])
1596 eq(list(self
.image
.list_children2()), [])
1597 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
1599 self
.check_children([(pool_name
, self
.clone_name
)])
1600 self
.check_children2(
1601 [{'pool': pool_name
, 'pool_namespace': '',
1602 'image': self
.clone_name
, 'trash': False,
1603 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1604 self
.check_descendants(
1605 [{'pool': pool_name
, 'pool_namespace': '',
1606 'image': self
.clone_name
, 'trash': False,
1607 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1608 self
.clone
= Image(ioctx
, self
.clone_name
)
1610 def test_flatten_errors(self
):
1611 # test that we can't flatten a non-clone
1612 assert_raises(InvalidArgument
, self
.image
.flatten
)
1614 # test that we can't flatten a snapshot
1615 self
.clone
.create_snap('snap2')
1616 self
.clone
.set_snap('snap2')
1617 assert_raises(ReadOnlyImage
, self
.clone
.flatten
)
1618 self
.clone
.remove_snap('snap2')
1620 def check_flatten_with_order(self
, new_order
):
1623 clone_name2
= get_temp_image_name()
1624 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1625 features
, new_order
)
1626 #with Image(ioctx, 'clone2') as clone:
1627 clone2
= Image(ioctx
, clone_name2
)
1629 eq(clone2
.overlap(), 0)
1631 self
.rbd
.remove(ioctx
, clone_name2
)
1633 # flatten after resizing to non-block size
1634 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1635 features
, new_order
)
1636 with
Image(ioctx
, clone_name2
) as clone
:
1637 clone
.resize(IMG_SIZE
// 2 - 1)
1639 eq(0, clone
.overlap())
1640 self
.rbd
.remove(ioctx
, clone_name2
)
1642 # flatten after resizing to non-block size
1643 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1644 features
, new_order
)
1645 with
Image(ioctx
, clone_name2
) as clone
:
1646 clone
.resize(IMG_SIZE
// 2 + 1)
1648 eq(clone
.overlap(), 0)
1649 self
.rbd
.remove(ioctx
, clone_name2
)
1651 def test_flatten_basic(self
):
1652 self
.check_flatten_with_order(IMG_ORDER
)
1654 def test_flatten_smaller_order(self
):
1655 self
.check_flatten_with_order(IMG_ORDER
- 2)
1657 def test_flatten_larger_order(self
):
1658 self
.check_flatten_with_order(IMG_ORDER
+ 2)
1660 def test_flatten_drops_cache(self
):
1663 clone_name2
= get_temp_image_name()
1664 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1665 features
, IMG_ORDER
)
1666 with
Image(ioctx
, clone_name2
) as clone
:
1667 with
Image(ioctx
, clone_name2
) as clone2
:
1668 # cache object non-existence
1669 data
= clone
.read(IMG_SIZE
// 2, 256)
1670 clone2_data
= clone2
.read(IMG_SIZE
// 2, 256)
1671 eq(data
, clone2_data
)
1673 assert_raises(ImageNotFound
, clone
.parent_info
)
1674 assert_raises(ImageNotFound
, clone2
.parent_info
)
1675 assert_raises(ImageNotFound
, clone
.parent_id
)
1676 assert_raises(ImageNotFound
, clone2
.parent_id
)
1677 after_flatten
= clone
.read(IMG_SIZE
// 2, 256)
1678 eq(data
, after_flatten
)
1679 after_flatten
= clone2
.read(IMG_SIZE
// 2, 256)
1680 eq(data
, after_flatten
)
1681 self
.rbd
.remove(ioctx
, clone_name2
)
1683 def test_flatten_multi_level(self
):
1684 self
.clone
.create_snap('snap2')
1685 self
.clone
.protect_snap('snap2')
1686 clone_name3
= get_temp_image_name()
1687 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1689 self
.clone
.flatten()
1690 with
Image(ioctx
, clone_name3
) as clone3
:
1692 self
.clone
.unprotect_snap('snap2')
1693 self
.clone
.remove_snap('snap2')
1694 self
.rbd
.remove(ioctx
, clone_name3
)
1696 def test_flatten_with_progress(self
):
1697 d
= {'received_callback': False}
1698 def progress_cb(current
, total
):
1699 d
['received_callback'] = True
1704 clone_name
= get_temp_image_name()
1705 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name
,
1707 with
Image(ioctx
, clone_name
) as clone
:
1708 clone
.flatten(on_progress
=progress_cb
)
1709 self
.rbd
.remove(ioctx
, clone_name
)
1710 eq(True, d
['received_callback'])
1712 def test_resize_flatten_multi_level(self
):
1713 self
.clone
.create_snap('snap2')
1714 self
.clone
.protect_snap('snap2')
1715 clone_name3
= get_temp_image_name()
1716 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1718 self
.clone
.resize(1)
1719 orig_data
= self
.image
.read(0, 256)
1720 with
Image(ioctx
, clone_name3
) as clone3
:
1721 clone3_data
= clone3
.read(0, 256)
1722 eq(orig_data
, clone3_data
)
1723 self
.clone
.flatten()
1724 with
Image(ioctx
, clone_name3
) as clone3
:
1725 clone3_data
= clone3
.read(0, 256)
1726 eq(orig_data
, clone3_data
)
1727 self
.rbd
.remove(ioctx
, clone_name3
)
1728 self
.clone
.unprotect_snap('snap2')
1729 self
.clone
.remove_snap('snap2')
1731 def test_trash_snapshot(self
):
1732 self
.image
.create_snap('snap2')
1734 clone_name
= get_temp_image_name()
1735 rados
.conf_set("rbd_default_clone_format", "2")
1736 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name
, features
)
1737 rados
.conf_set("rbd_default_clone_format", "auto")
1739 self
.image
.remove_snap('snap2')
1741 snaps
= [s
for s
in self
.image
.list_snaps() if s
['name'] != 'snap1']
1742 eq([RBD_SNAP_NAMESPACE_TYPE_TRASH
], [s
['namespace'] for s
in snaps
])
1743 eq([{'original_name' : 'snap2'}], [s
['trash'] for s
in snaps
])
1745 self
.rbd
.remove(ioctx
, clone_name
)
1746 eq([], [s
for s
in self
.image
.list_snaps() if s
['name'] != 'snap1'])
1748 class TestExclusiveLock(object):
1750 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
1753 rados2
= Rados(conffile
='')
1756 ioctx2
= rados2
.open_ioctx(pool_name
)
1766 def test_ownership(self
):
1767 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1768 image1
.write(b
'0'*256, 0)
1769 eq(image1
.is_exclusive_lock_owner(), True)
1770 eq(image2
.is_exclusive_lock_owner(), False)
1772 def test_snapshot_leadership(self
):
1773 with
Image(ioctx
, image_name
) as image
:
1774 image
.create_snap('snap')
1775 eq(image
.is_exclusive_lock_owner(), True)
1777 with
Image(ioctx
, image_name
) as image
:
1778 image
.write(b
'0'*256, 0)
1779 eq(image
.is_exclusive_lock_owner(), True)
1780 image
.set_snap('snap')
1781 eq(image
.is_exclusive_lock_owner(), False)
1782 with
Image(ioctx
, image_name
, snapshot
='snap') as image
:
1783 eq(image
.is_exclusive_lock_owner(), False)
1785 with
Image(ioctx
, image_name
) as image
:
1786 image
.remove_snap('snap')
1788 def test_read_only_leadership(self
):
1789 with
Image(ioctx
, image_name
, read_only
=True) as image
:
1790 eq(image
.is_exclusive_lock_owner(), False)
1792 def test_follower_flatten(self
):
1793 with
Image(ioctx
, image_name
) as image
:
1794 image
.create_snap('snap')
1795 image
.protect_snap('snap')
1797 RBD().clone(ioctx
, image_name
, 'snap', ioctx
, 'clone', features
)
1798 with
Image(ioctx
, 'clone') as image1
, Image(ioctx2
, 'clone') as image2
:
1799 data
= rand_data(256)
1800 image1
.write(data
, 0)
1802 assert_raises(ImageNotFound
, image1
.parent_info
)
1803 assert_raises(ImageNotFound
, image1
.parent_id
)
1807 image2
.parent_info()
1808 except ImageNotFound
:
1813 RBD().remove(ioctx
, 'clone')
1814 with
Image(ioctx
, image_name
) as image
:
1815 image
.unprotect_snap('snap')
1816 image
.remove_snap('snap')
1818 def test_follower_resize(self
):
1819 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1820 image1
.write(b
'0'*256, 0)
1821 for new_size
in [IMG_SIZE
* 2, IMG_SIZE
// 2]:
1822 image2
.resize(new_size
);
1823 eq(new_size
, image1
.size())
1825 if new_size
== image2
.size():
1828 eq(new_size
, image2
.size())
1830 def test_follower_snap_create(self
):
1831 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1832 image2
.create_snap('snap1')
1833 image1
.remove_snap('snap1')
1835 def test_follower_snap_rollback(self
):
1836 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1837 image1
.create_snap('snap')
1839 assert_raises(ReadOnlyImage
, image2
.rollback_to_snap
, 'snap')
1840 image1
.rollback_to_snap('snap')
1842 image1
.remove_snap('snap')
1844 def test_follower_discard(self
):
1846 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1847 data
= rand_data(256)
1848 image1
.write(data
, 0)
1849 image2
.discard(0, 256)
1850 eq(image1
.is_exclusive_lock_owner(), False)
1851 eq(image2
.is_exclusive_lock_owner(), True)
1852 read
= image2
.read(0, 256)
1853 if rados
.conf_get('rbd_skip_partial_discard') == 'false':
1854 eq(256 * b
'\0', read
)
1858 def test_follower_write(self
):
1859 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1860 data
= rand_data(256)
1861 image1
.write(data
, 0)
1862 image2
.write(data
, IMG_SIZE
// 2)
1863 eq(image1
.is_exclusive_lock_owner(), False)
1864 eq(image2
.is_exclusive_lock_owner(), True)
1865 for offset
in [0, IMG_SIZE
// 2]:
1866 read
= image2
.read(offset
, 256)
1868 def test_acquire_release_lock(self
):
1869 with
Image(ioctx
, image_name
) as image
:
1870 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
1871 image
.lock_release()
1873 def test_break_lock(self
):
1874 blacklist_rados
= Rados(conffile
='')
1875 blacklist_rados
.connect()
1877 blacklist_ioctx
= blacklist_rados
.open_ioctx(pool_name
)
1879 rados2
.conf_set('rbd_blacklist_on_break_lock', 'true')
1880 with
Image(ioctx2
, image_name
) as image
, \
1881 Image(blacklist_ioctx
, image_name
) as blacklist_image
:
1883 lock_owners
= list(image
.lock_get_owners())
1884 eq(0, len(lock_owners
))
1886 blacklist_image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
1887 assert_raises(ReadOnlyImage
, image
.lock_acquire
,
1888 RBD_LOCK_MODE_EXCLUSIVE
)
1889 lock_owners
= list(image
.lock_get_owners())
1890 eq(1, len(lock_owners
))
1891 eq(RBD_LOCK_MODE_EXCLUSIVE
, lock_owners
[0]['mode'])
1892 image
.lock_break(RBD_LOCK_MODE_EXCLUSIVE
,
1893 lock_owners
[0]['owner'])
1895 assert_raises(ConnectionShutdown
,
1896 blacklist_image
.is_exclusive_lock_owner
)
1898 blacklist_rados
.wait_for_latest_osdmap()
1899 data
= rand_data(256)
1900 assert_raises(ConnectionShutdown
,
1901 blacklist_image
.write
, data
, 0)
1903 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
1906 blacklist_image
.close()
1907 except ConnectionShutdown
:
1910 blacklist_ioctx
.close()
1912 blacklist_rados
.shutdown()
1914 class TestMirroring(object):
1917 def check_info(info
, global_id
, state
, primary
=None):
1918 eq(global_id
, info
['global_id'])
1919 eq(state
, info
['state'])
1920 if primary
is not None:
1921 eq(primary
, info
['primary'])
1925 self
.initial_mirror_mode
= self
.rbd
.mirror_mode_get(ioctx
)
1926 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
1928 self
.image
= Image(ioctx
, image_name
)
1933 self
.rbd
.mirror_mode_set(ioctx
, self
.initial_mirror_mode
)
1935 def test_uuid(self
):
1936 mirror_uuid
= self
.rbd
.mirror_uuid_get(ioctx
)
1939 def test_site_name(self
):
1940 site_name
= "us-west-1"
1941 self
.rbd
.mirror_site_name_set(rados
, site_name
)
1942 eq(site_name
, self
.rbd
.mirror_site_name_get(rados
))
1943 self
.rbd
.mirror_site_name_set(rados
, "")
1944 eq(rados
.get_fsid(), self
.rbd
.mirror_site_name_get(rados
))
1946 def test_mirror_peer_bootstrap(self
):
1947 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
1949 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_DISABLED
)
1950 assert_raises(InvalidArgument
, self
.rbd
.mirror_peer_bootstrap_create
,
1953 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
1954 token_b64
= self
.rbd
.mirror_peer_bootstrap_create(ioctx
)
1955 token
= base64
.b64decode(token_b64
)
1956 token_dict
= json
.loads(token
)
1957 eq(sorted(['fsid', 'client_id', 'key', 'mon_host']),
1958 sorted(list(token_dict
.keys())))
1960 # requires different cluster
1961 assert_raises(InvalidArgument
, self
.rbd
.mirror_peer_bootstrap_import
,
1962 ioctx
, RBD_MIRROR_PEER_DIRECTION_RX
, token_b64
)
1964 def test_mirror_peer(self
):
1965 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
1966 site_name
= "test_site"
1967 client_name
= "test_client"
1968 uuid
= self
.rbd
.mirror_peer_add(ioctx
, site_name
, client_name
,
1969 direction
=RBD_MIRROR_PEER_DIRECTION_RX_TX
)
1973 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX
,
1974 'site_name' : site_name
,
1975 'cluster_name' : site_name
,
1977 'client_name' : client_name
,
1979 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
1980 cluster_name
= "test_cluster1"
1981 self
.rbd
.mirror_peer_set_cluster(ioctx
, uuid
, cluster_name
)
1982 client_name
= "test_client1"
1983 self
.rbd
.mirror_peer_set_client(ioctx
, uuid
, client_name
)
1986 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX
,
1987 'site_name' : cluster_name
,
1988 'cluster_name' : cluster_name
,
1990 'client_name' : client_name
,
1992 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
1995 RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST
: 'host1',
1996 RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY
: 'abc'
1998 self
.rbd
.mirror_peer_set_attributes(ioctx
, uuid
, attribs
)
1999 eq(attribs
, self
.rbd
.mirror_peer_get_attributes(ioctx
, uuid
))
2001 self
.rbd
.mirror_peer_remove(ioctx
, uuid
)
2002 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
2004 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
2005 RBD_FEATURE_JOURNALING
])
2006 def test_mirror_image(self
):
2008 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
2009 self
.image
.mirror_image_disable(True)
2010 info
= self
.image
.mirror_image_get_info()
2011 self
.check_info(info
, '', RBD_MIRROR_IMAGE_DISABLED
, False)
2013 self
.image
.mirror_image_enable()
2014 info
= self
.image
.mirror_image_get_info()
2015 global_id
= info
['global_id']
2016 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2018 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
2021 self
.image
.mirror_image_disable(True)
2022 except InvalidArgument
:
2024 eq(True, fail
) # Fails because of mirror mode pool
2026 self
.image
.mirror_image_demote()
2027 info
= self
.image
.mirror_image_get_info()
2028 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, False)
2030 entries
= dict(self
.rbd
.mirror_image_info_list(ioctx
))
2031 info
['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL
;
2032 eq(info
, entries
[self
.image
.id()])
2034 self
.image
.mirror_image_resync()
2036 self
.image
.mirror_image_promote(True)
2037 info
= self
.image
.mirror_image_get_info()
2038 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2040 entries
= dict(self
.rbd
.mirror_image_info_list(ioctx
))
2041 info
['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL
;
2042 eq(info
, entries
[self
.image
.id()])
2046 self
.image
.mirror_image_resync()
2047 except InvalidArgument
:
2049 eq(True, fail
) # Fails because it is primary
2051 status
= self
.image
.mirror_image_get_status()
2052 eq(image_name
, status
['name'])
2053 eq(False, status
['up'])
2054 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
2055 info
= status
['info']
2056 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2058 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
2059 RBD_FEATURE_JOURNALING
])
2060 def test_mirror_image_status(self
):
2061 info
= self
.image
.mirror_image_get_info()
2062 global_id
= info
['global_id']
2063 state
= info
['state']
2064 primary
= info
['primary']
2066 status
= self
.image
.mirror_image_get_status()
2067 eq(image_name
, status
['name'])
2068 eq(False, status
['up'])
2069 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
2070 eq([], status
['remote_statuses'])
2071 info
= status
['info']
2072 self
.check_info(info
, global_id
, state
, primary
)
2074 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
2077 eq(image_name
, status
['name'])
2078 eq(False, status
['up'])
2079 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
2080 info
= status
['info']
2081 self
.check_info(info
, global_id
, state
)
2083 states
= self
.rbd
.mirror_image_status_summary(ioctx
)
2084 eq([(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, 1)], states
)
2086 assert_raises(ImageNotFound
, self
.image
.mirror_image_get_instance_id
)
2087 instance_ids
= list(self
.rbd
.mirror_image_instance_id_list(ioctx
))
2088 eq(0, len(instance_ids
))
2092 self
.rbd
.create(ioctx
, image_name
+ str(i
), IMG_SIZE
, IMG_ORDER
,
2093 old_format
=False, features
=int(features
))
2094 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
2095 eq(N
+ 1, len(images
))
2097 self
.rbd
.remove(ioctx
, image_name
+ str(i
))
2099 def test_mirror_image_create_snapshot(self
):
2100 assert_raises(InvalidArgument
, self
.image
.mirror_image_create_snapshot
)
2102 peer1_uuid
= self
.rbd
.mirror_peer_add(ioctx
, "cluster1", "client")
2103 peer2_uuid
= self
.rbd
.mirror_peer_add(ioctx
, "cluster2", "client")
2104 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
2105 self
.image
.mirror_image_disable(False)
2106 self
.image
.mirror_image_enable(RBD_MIRROR_IMAGE_MODE_SNAPSHOT
)
2107 mode
= self
.image
.mirror_image_get_mode()
2108 eq(RBD_MIRROR_IMAGE_MODE_SNAPSHOT
, mode
)
2110 snaps
= list(self
.image
.list_snaps())
2113 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2114 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2116 info
= self
.image
.mirror_image_get_info()
2117 eq(True, info
['primary'])
2119 self
.rbd
.mirror_image_info_list(ioctx
,
2120 RBD_MIRROR_IMAGE_MODE_SNAPSHOT
))
2121 info
['mode'] = RBD_MIRROR_IMAGE_MODE_SNAPSHOT
;
2122 eq(info
, entries
[self
.image
.id()])
2124 snap_id
= self
.image
.mirror_image_create_snapshot()
2126 snaps
= list(self
.image
.list_snaps())
2129 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2130 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2132 eq(snap
['id'], snap_id
)
2133 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2134 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2135 eq(sorted([peer1_uuid
, peer2_uuid
]),
2136 sorted(snap
['mirror']['mirror_peer_uuids']))
2138 eq(RBD_SNAP_NAMESPACE_TYPE_MIRROR
,
2139 self
.image
.snap_get_namespace_type(snap_id
))
2140 mirror_snap
= self
.image
.snap_get_mirror_namespace(snap_id
)
2141 eq(mirror_snap
, snap
['mirror'])
2143 self
.image
.mirror_image_demote()
2145 assert_raises(InvalidArgument
, self
.image
.mirror_image_create_snapshot
)
2147 snaps
= list(self
.image
.list_snaps())
2150 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2152 eq(snap
['id'], snap_id
)
2153 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2155 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2156 eq(RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED
, snap
['mirror']['state'])
2157 eq(sorted([peer1_uuid
, peer2_uuid
]),
2158 sorted(snap
['mirror']['mirror_peer_uuids']))
2160 self
.rbd
.mirror_peer_remove(ioctx
, peer1_uuid
)
2161 self
.rbd
.mirror_peer_remove(ioctx
, peer2_uuid
)
2162 self
.image
.mirror_image_promote(False)
2164 class TestTrash(object):
2168 rados2
= Rados(conffile
='')
2171 ioctx2
= rados2
.open_ioctx(pool_name
)
2179 def test_move(self
):
2181 with
Image(ioctx
, image_name
) as image
:
2182 image_id
= image
.id()
2184 RBD().trash_move(ioctx
, image_name
, 1000)
2185 RBD().trash_remove(ioctx
, image_id
, True)
2187 def test_purge(self
):
2189 with
Image(ioctx
, image_name
) as image
:
2190 image_name1
= image_name
2191 image_id1
= image
.id()
2194 with
Image(ioctx
, image_name
) as image
:
2195 image_name2
= image_name
2196 image_id2
= image
.id()
2198 RBD().trash_move(ioctx
, image_name1
, 0)
2199 RBD().trash_move(ioctx
, image_name2
, 1000)
2200 RBD().trash_purge(ioctx
, datetime
.now())
2202 entries
= list(RBD().trash_list(ioctx
))
2203 eq([image_id2
], [x
['id'] for x
in entries
])
2204 RBD().trash_remove(ioctx
, image_id2
, True)
2206 def test_remove_denied(self
):
2208 with
Image(ioctx
, image_name
) as image
:
2209 image_id
= image
.id()
2211 RBD().trash_move(ioctx
, image_name
, 1000)
2212 assert_raises(PermissionError
, RBD().trash_remove
, ioctx
, image_id
)
2213 RBD().trash_remove(ioctx
, image_id
, True)
2215 def test_remove(self
):
2217 with
Image(ioctx
, image_name
) as image
:
2218 image_id
= image
.id()
2220 RBD().trash_move(ioctx
, image_name
, 0)
2221 RBD().trash_remove(ioctx
, image_id
)
2223 def test_remove_with_progress(self
):
2224 d
= {'received_callback': False}
2225 def progress_cb(current
, total
):
2226 d
['received_callback'] = True
2230 with
Image(ioctx
, image_name
) as image
:
2231 image_id
= image
.id()
2233 RBD().trash_move(ioctx
, image_name
, 0)
2234 RBD().trash_remove(ioctx
, image_id
, on_progress
=progress_cb
)
2235 eq(True, d
['received_callback'])
2239 with
Image(ioctx
, image_name
) as image
:
2240 image_id
= image
.id()
2242 RBD().trash_move(ioctx
, image_name
, 1000)
2244 info
= RBD().trash_get(ioctx
, image_id
)
2245 eq(image_id
, info
['id'])
2246 eq(image_name
, info
['name'])
2247 eq('USER', info
['source'])
2248 assert(info
['deferment_end_time'] > info
['deletion_time'])
2250 RBD().trash_remove(ioctx
, image_id
, True)
2252 def test_list(self
):
2254 with
Image(ioctx
, image_name
) as image
:
2255 image_id1
= image
.id()
2256 image_name1
= image_name
2257 RBD().trash_move(ioctx
, image_name
, 1000)
2260 with
Image(ioctx
, image_name
) as image
:
2261 image_id2
= image
.id()
2262 image_name2
= image_name
2263 RBD().trash_move(ioctx
, image_name
, 1000)
2265 entries
= list(RBD().trash_list(ioctx
))
2267 if e
['id'] == image_id1
:
2268 eq(e
['name'], image_name1
)
2269 elif e
['id'] == image_id2
:
2270 eq(e
['name'], image_name2
)
2273 eq(e
['source'], 'USER')
2274 assert e
['deferment_end_time'] > e
['deletion_time']
2276 RBD().trash_remove(ioctx
, image_id1
, True)
2277 RBD().trash_remove(ioctx
, image_id2
, True)
2279 def test_restore(self
):
2281 with
Image(ioctx
, image_name
) as image
:
2282 image_id
= image
.id()
2283 RBD().trash_move(ioctx
, image_name
, 1000)
2284 RBD().trash_restore(ioctx
, image_id
, image_name
)
2287 def test_create_group():
2291 def test_rename_group():
2293 if group_name
is not None:
2295 eq(["new" + group_name
], RBD().group_list(ioctx
))
2296 RBD().group_remove(ioctx
, "new" + group_name
)
2300 def test_list_groups_empty():
2301 eq([], RBD().group_list(ioctx
))
2303 @with_setup(create_group
, remove_group
)
2304 def test_list_groups():
2305 eq([group_name
], RBD().group_list(ioctx
))
2307 @with_setup(create_group
)
2308 def test_list_groups_after_removed():
2310 eq([], RBD().group_list(ioctx
))
2312 class TestGroups(object):
2318 self
.image_names
= [image_name
]
2319 self
.image
= Image(ioctx
, image_name
)
2322 snap_name
= get_temp_snap_name()
2323 self
.group
= Group(ioctx
, group_name
)
2328 for name
in self
.image_names
:
2329 RBD().remove(ioctx
, name
)
2331 def test_group_image_add(self
):
2332 self
.group
.add_image(ioctx
, image_name
)
2334 def test_group_image_list_empty(self
):
2335 eq([], list(self
.group
.list_images()))
2337 def test_group_image_list(self
):
2338 eq([], list(self
.group
.list_images()))
2339 self
.group
.add_image(ioctx
, image_name
)
2340 eq([image_name
], [img
['name'] for img
in self
.group
.list_images()])
2342 def test_group_image_list_move_to_trash(self
):
2343 eq([], list(self
.group
.list_images()))
2344 with
Image(ioctx
, image_name
) as image
:
2345 image_id
= image
.id()
2346 self
.group
.add_image(ioctx
, image_name
)
2347 eq([image_name
], [img
['name'] for img
in self
.group
.list_images()])
2348 RBD().trash_move(ioctx
, image_name
, 0)
2349 eq([], list(self
.group
.list_images()))
2350 RBD().trash_restore(ioctx
, image_id
, image_name
)
2352 def test_group_image_many_images(self
):
2353 eq([], list(self
.group
.list_images()))
2354 self
.group
.add_image(ioctx
, image_name
)
2356 for x
in range(0, 20):
2358 self
.image_names
.append(image_name
)
2359 self
.group
.add_image(ioctx
, image_name
)
2361 self
.image_names
.sort()
2362 answer
= [img
['name'] for img
in self
.group
.list_images()]
2364 eq(self
.image_names
, answer
)
2366 def test_group_image_remove(self
):
2367 eq([], list(self
.group
.list_images()))
2368 self
.group
.add_image(ioctx
, image_name
)
2369 with
Image(ioctx
, image_name
) as image
:
2370 eq(RBD_OPERATION_FEATURE_GROUP
,
2371 image
.op_features() & RBD_OPERATION_FEATURE_GROUP
)
2372 group
= image
.group()
2373 eq(group_name
, group
['name'])
2375 eq([image_name
], [img
['name'] for img
in self
.group
.list_images()])
2376 self
.group
.remove_image(ioctx
, image_name
)
2377 eq([], list(self
.group
.list_images()))
2378 with
Image(ioctx
, image_name
) as image
:
2379 eq(0, image
.op_features() & RBD_OPERATION_FEATURE_GROUP
)
2381 def test_group_snap(self
):
2383 eq([], list(self
.group
.list_snaps()))
2384 self
.group
.create_snap(snap_name
)
2385 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2387 for snap
in self
.image
.list_snaps():
2388 eq(rbd
.RBD_SNAP_NAMESPACE_TYPE_GROUP
, snap
['namespace'])
2389 info
= snap
['group']
2390 eq(group_name
, info
['group_name'])
2391 eq(snap_name
, info
['group_snap_name'])
2393 self
.group
.remove_snap(snap_name
)
2394 eq([], list(self
.group
.list_snaps()))
2396 def test_group_snap_list_many(self
):
2398 eq([], list(self
.group
.list_snaps()))
2400 for x
in range(0, 20):
2401 snap_names
.append(snap_name
)
2402 self
.group
.create_snap(snap_name
)
2403 snap_name
= get_temp_snap_name()
2406 answer
= [snap
['name'] for snap
in self
.group
.list_snaps()]
2408 eq(snap_names
, answer
)
2410 def test_group_snap_namespace(self
):
2412 eq([], list(self
.group
.list_snaps()))
2413 self
.group
.add_image(ioctx
, image_name
)
2414 self
.group
.create_snap(snap_name
)
2415 eq(1, len([snap
['name'] for snap
in self
.image
.list_snaps()]))
2416 self
.group
.remove_image(ioctx
, image_name
)
2417 self
.group
.remove_snap(snap_name
)
2418 eq([], list(self
.group
.list_snaps()))
2420 def test_group_snap_rename(self
):
2422 new_snap_name
= "new" + snap_name
2424 eq([], list(self
.group
.list_snaps()))
2425 self
.group
.create_snap(snap_name
)
2426 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2427 self
.group
.rename_snap(snap_name
, new_snap_name
)
2428 eq([new_snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2429 self
.group
.remove_snap(new_snap_name
)
2430 eq([], list(self
.group
.list_snaps()))
2432 def test_group_snap_rollback(self
):
2433 eq([], list(self
.group
.list_images()))
2434 self
.group
.add_image(ioctx
, image_name
)
2435 with
Image(ioctx
, image_name
) as image
:
2436 image
.write(b
'\0' * 256, 0)
2437 read
= image
.read(0, 256)
2438 eq(read
, b
'\0' * 256)
2441 eq([], list(self
.group
.list_snaps()))
2442 self
.group
.create_snap(snap_name
)
2443 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2445 with
Image(ioctx
, image_name
) as image
:
2446 data
= rand_data(256)
2447 image
.write(data
, 0)
2448 read
= image
.read(0, 256)
2451 self
.group
.rollback_to_snap(snap_name
)
2452 with
Image(ioctx
, image_name
) as image
:
2453 read
= image
.read(0, 256)
2454 eq(read
, b
'\0' * 256)
2456 self
.group
.remove_image(ioctx
, image_name
)
2457 eq([], list(self
.group
.list_images()))
2458 self
.group
.remove_snap(snap_name
)
2459 eq([], list(self
.group
.list_snaps()))
2461 @with_setup(create_image
, remove_image
)
2464 image_name2
= get_temp_image_name()
2466 class TestMigration(object):
2468 def test_migration(self
):
2470 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2471 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2474 status
= RBD().migration_status(ioctx
, image_name
)
2475 eq(image_name
, status
['source_image_name'])
2476 eq(image_name
, status
['dest_image_name'])
2477 eq(RBD_IMAGE_MIGRATION_STATE_PREPARED
, status
['state'])
2479 RBD().migration_execute(ioctx
, image_name
)
2480 RBD().migration_commit(ioctx
, image_name
)
2483 def test_migration_with_progress(self
):
2484 d
= {'received_callback': False}
2485 def progress_cb(current
, total
):
2486 d
['received_callback'] = True
2490 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2491 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2493 RBD().migration_execute(ioctx
, image_name
, on_progress
=progress_cb
)
2494 eq(True, d
['received_callback'])
2495 d
['received_callback'] = False
2497 RBD().migration_commit(ioctx
, image_name
, on_progress
=progress_cb
)
2498 eq(True, d
['received_callback'])
2501 def test_migrate_abort(self
):
2503 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2504 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2506 RBD().migration_abort(ioctx
, image_name
)
2509 def test_migrate_abort_with_progress(self
):
2510 d
= {'received_callback': False}
2511 def progress_cb(current
, total
):
2512 d
['received_callback'] = True
2516 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2517 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2519 RBD().migration_abort(ioctx
, image_name
, on_progress
=progress_cb
)
2520 eq(True, d
['received_callback'])