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
)
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
)
605 data
= self
.image
.read(0, 20)
608 def test_read_with_fadvise_flags(self
):
609 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED
)
611 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM
)
614 def test_large_write(self
):
615 data
= rand_data(IMG_SIZE
)
616 self
.image
.write(data
, 0)
618 def test_large_read(self
):
619 data
= self
.image
.read(0, IMG_SIZE
)
620 eq(data
, b
'\0' * IMG_SIZE
)
622 def test_write_read(self
):
623 data
= rand_data(256)
625 self
.image
.write(data
, offset
)
626 read
= self
.image
.read(offset
, 256)
629 def test_read_bad_offset(self
):
630 assert_raises(InvalidArgument
, self
.image
.read
, IMG_SIZE
+ 1, IMG_SIZE
)
632 def test_resize(self
):
633 new_size
= IMG_SIZE
* 2
634 self
.image
.resize(new_size
)
635 info
= self
.image
.stat()
636 check_stat(info
, new_size
, IMG_ORDER
)
638 def test_resize_allow_shrink_False(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
)
643 assert_raises(InvalidArgument
, self
.image
.resize
, IMG_SIZE
, False)
646 eq(IMG_SIZE
, self
.image
.size())
647 self
.image
.create_snap('snap1')
648 new_size
= IMG_SIZE
* 2
649 self
.image
.resize(new_size
)
650 eq(new_size
, self
.image
.size())
651 self
.image
.create_snap('snap2')
652 self
.image
.set_snap('snap2')
653 eq(new_size
, self
.image
.size())
654 self
.image
.set_snap('snap1')
655 eq(IMG_SIZE
, self
.image
.size())
656 self
.image
.set_snap(None)
657 eq(new_size
, self
.image
.size())
658 self
.image
.remove_snap('snap1')
659 self
.image
.remove_snap('snap2')
661 def test_resize_down(self
):
662 new_size
= IMG_SIZE
// 2
663 data
= rand_data(256)
664 self
.image
.write(data
, IMG_SIZE
// 2);
665 self
.image
.resize(new_size
)
666 self
.image
.resize(IMG_SIZE
)
667 read
= self
.image
.read(IMG_SIZE
// 2, 256)
668 eq(b
'\0' * 256, read
)
670 def test_resize_bytes(self
):
671 new_size
= IMG_SIZE
// 2 - 5
672 data
= rand_data(256)
673 self
.image
.write(data
, IMG_SIZE
// 2 - 10);
674 self
.image
.resize(new_size
)
675 self
.image
.resize(IMG_SIZE
)
676 read
= self
.image
.read(IMG_SIZE
// 2 - 10, 5)
678 read
= self
.image
.read(IMG_SIZE
// 2 - 5, 251)
679 eq(b
'\0' * 251, read
)
681 def _test_copy(self
, features
=None, order
=None, stripe_unit
=None,
684 data
= rand_data(256)
685 self
.image
.write(data
, 256)
686 image_name
= get_temp_image_name()
688 self
.image
.copy(ioctx
, image_name
)
690 self
.image
.copy(ioctx
, image_name
, features
)
691 elif stripe_unit
is None:
692 self
.image
.copy(ioctx
, image_name
, features
, order
)
693 elif stripe_count
is None:
694 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
)
696 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
,
698 assert_raises(ImageExists
, self
.image
.copy
, ioctx
, image_name
)
699 copy
= Image(ioctx
, image_name
)
700 copy_data
= copy
.read(256, 256)
702 self
.rbd
.remove(ioctx
, image_name
)
708 def test_copy2(self
):
709 self
._test
_copy
(self
.image
.features(), self
.image
.stat()['order'])
711 @require_features([RBD_FEATURE_STRIPINGV2
])
712 def test_copy3(self
):
714 self
._test
_copy
(features
, self
.image
.stat()['order'],
715 self
.image
.stripe_unit(), self
.image
.stripe_count())
717 def test_deep_copy(self
):
720 self
.image
.write(b
'a' * 256, 0)
721 self
.image
.create_snap('snap1')
722 self
.image
.write(b
'b' * 256, 0)
723 dst_name
= get_temp_image_name()
724 self
.image
.deep_copy(ioctx
, dst_name
, features
=features
,
725 order
=self
.image
.stat()['order'],
726 stripe_unit
=self
.image
.stripe_unit(),
727 stripe_count
=self
.image
.stripe_count(),
729 self
.image
.remove_snap('snap1')
730 with
Image(ioctx
, dst_name
, 'snap1') as copy
:
731 copy_data
= copy
.read(0, 256)
732 eq(b
'a' * 256, copy_data
)
733 with
Image(ioctx
, dst_name
) as copy
:
734 copy_data
= copy
.read(0, 256)
735 eq(b
'b' * 256, copy_data
)
736 copy
.remove_snap('snap1')
737 self
.rbd
.remove(ioctx
, dst_name
)
739 @require_features([RBD_FEATURE_LAYERING
])
740 def test_deep_copy_clone(self
):
743 self
.image
.write(b
'a' * 256, 0)
744 self
.image
.create_snap('snap1')
745 self
.image
.write(b
'b' * 256, 0)
746 self
.image
.protect_snap('snap1')
747 clone_name
= get_temp_image_name()
748 dst_name
= get_temp_image_name()
749 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name
)
750 with
Image(ioctx
, clone_name
) as child
:
751 child
.create_snap('snap1')
752 child
.deep_copy(ioctx
, dst_name
, features
=features
,
753 order
=self
.image
.stat()['order'],
754 stripe_unit
=self
.image
.stripe_unit(),
755 stripe_count
=self
.image
.stripe_count(),
757 child
.remove_snap('snap1')
759 with
Image(ioctx
, dst_name
) as copy
:
760 copy_data
= copy
.read(0, 256)
761 eq(b
'a' * 256, copy_data
)
762 copy
.remove_snap('snap1')
763 self
.rbd
.remove(ioctx
, dst_name
)
764 self
.rbd
.remove(ioctx
, clone_name
)
765 self
.image
.unprotect_snap('snap1')
766 self
.image
.remove_snap('snap1')
768 def test_create_snap(self
):
770 self
.image
.create_snap('snap1')
771 read
= self
.image
.read(0, 256)
772 eq(read
, b
'\0' * 256)
773 data
= rand_data(256)
774 self
.image
.write(data
, 0)
775 read
= self
.image
.read(0, 256)
777 at_snapshot
= Image(ioctx
, image_name
, 'snap1')
778 snap_data
= at_snapshot
.read(0, 256)
780 eq(snap_data
, b
'\0' * 256)
781 self
.image
.remove_snap('snap1')
783 def test_create_snap_exists(self
):
784 self
.image
.create_snap('snap1')
785 assert_raises(ImageExists
, self
.image
.create_snap
, 'snap1')
786 self
.image
.remove_snap('snap1')
788 def test_list_snaps(self
):
789 eq([], list(self
.image
.list_snaps()))
790 self
.image
.create_snap('snap1')
791 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
792 self
.image
.create_snap('snap2')
793 eq(['snap1', 'snap2'], [snap
['name'] for snap
in self
.image
.list_snaps()])
794 self
.image
.remove_snap('snap1')
795 self
.image
.remove_snap('snap2')
797 def test_list_snaps_iterator_auto_close(self
):
798 self
.image
.create_snap('snap1')
799 self
.image
.list_snaps()
800 self
.image
.remove_snap('snap1')
802 def test_remove_snap(self
):
803 eq([], list(self
.image
.list_snaps()))
804 self
.image
.create_snap('snap1')
805 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
806 self
.image
.remove_snap('snap1')
807 eq([], list(self
.image
.list_snaps()))
809 def test_remove_snap_not_found(self
):
810 assert_raises(ImageNotFound
, self
.image
.remove_snap
, 'snap1')
812 @require_features([RBD_FEATURE_LAYERING
])
813 def test_remove_snap2(self
):
814 self
.image
.create_snap('snap1')
815 self
.image
.protect_snap('snap1')
816 assert(self
.image
.is_protected_snap('snap1'))
817 self
.image
.remove_snap2('snap1', RBD_SNAP_REMOVE_UNPROTECT
)
818 eq([], list(self
.image
.list_snaps()))
820 def test_remove_snap_by_id(self
):
821 eq([], list(self
.image
.list_snaps()))
822 self
.image
.create_snap('snap1')
823 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
824 for snap
in self
.image
.list_snaps():
826 self
.image
.remove_snap_by_id(snap_id
)
827 eq([], list(self
.image
.list_snaps()))
829 def test_rename_snap(self
):
830 eq([], list(self
.image
.list_snaps()))
831 self
.image
.create_snap('snap1')
832 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
833 self
.image
.rename_snap("snap1", "snap1-rename")
834 eq(['snap1-rename'], [snap
['name'] for snap
in self
.image
.list_snaps()])
835 self
.image
.remove_snap('snap1-rename')
836 eq([], list(self
.image
.list_snaps()))
838 @require_features([RBD_FEATURE_LAYERING
])
839 def test_protect_snap(self
):
840 self
.image
.create_snap('snap1')
841 assert(not self
.image
.is_protected_snap('snap1'))
842 self
.image
.protect_snap('snap1')
843 assert(self
.image
.is_protected_snap('snap1'))
844 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
845 self
.image
.unprotect_snap('snap1')
846 assert(not self
.image
.is_protected_snap('snap1'))
847 self
.image
.remove_snap('snap1')
848 assert_raises(ImageNotFound
, self
.image
.unprotect_snap
, 'snap1')
849 assert_raises(ImageNotFound
, self
.image
.is_protected_snap
, 'snap1')
851 def test_snap_exists(self
):
852 self
.image
.create_snap('snap1')
853 eq(self
.image
.snap_exists('snap1'), True)
854 self
.image
.remove_snap('snap1')
855 eq(self
.image
.snap_exists('snap1'), False)
857 def test_snap_timestamp(self
):
858 self
.image
.create_snap('snap1')
859 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
860 for snap
in self
.image
.list_snaps():
862 time
= self
.image
.get_snap_timestamp(snap_id
)
863 assert_not_equal(b
'', time
.year
)
864 assert_not_equal(0, time
.year
)
865 assert_not_equal(time
.year
, '1970')
866 self
.image
.remove_snap('snap1')
868 def test_limit_snaps(self
):
869 self
.image
.set_snap_limit(2)
870 eq(2, self
.image
.get_snap_limit())
871 self
.image
.create_snap('snap1')
872 self
.image
.create_snap('snap2')
873 assert_raises(DiskQuotaExceeded
, self
.image
.create_snap
, 'snap3')
874 self
.image
.remove_snap_limit()
875 self
.image
.create_snap('snap3')
877 self
.image
.remove_snap('snap1')
878 self
.image
.remove_snap('snap2')
879 self
.image
.remove_snap('snap3')
881 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
882 def test_remove_with_exclusive_lock(self
):
883 assert_raises(ImageBusy
, remove_image
)
885 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
886 def test_remove_with_snap(self
):
887 self
.image
.create_snap('snap1')
888 assert_raises(ImageHasSnapshots
, remove_image
)
889 self
.image
.remove_snap('snap1')
891 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
892 def test_remove_with_watcher(self
):
893 data
= rand_data(256)
894 self
.image
.write(data
, 0)
895 assert_raises(ImageBusy
, remove_image
)
896 read
= self
.image
.read(0, 256)
899 def test_rollback_to_snap(self
):
900 self
.image
.write(b
'\0' * 256, 0)
901 self
.image
.create_snap('snap1')
902 read
= self
.image
.read(0, 256)
903 eq(read
, b
'\0' * 256)
904 data
= rand_data(256)
905 self
.image
.write(data
, 0)
906 read
= self
.image
.read(0, 256)
908 self
.image
.rollback_to_snap('snap1')
909 read
= self
.image
.read(0, 256)
910 eq(read
, b
'\0' * 256)
911 self
.image
.remove_snap('snap1')
913 def test_rollback_to_snap_sparse(self
):
914 self
.image
.create_snap('snap1')
915 read
= self
.image
.read(0, 256)
916 eq(read
, b
'\0' * 256)
917 data
= rand_data(256)
918 self
.image
.write(data
, 0)
919 read
= self
.image
.read(0, 256)
921 self
.image
.rollback_to_snap('snap1')
922 read
= self
.image
.read(0, 256)
923 eq(read
, b
'\0' * 256)
924 self
.image
.remove_snap('snap1')
926 def test_rollback_with_resize(self
):
927 read
= self
.image
.read(0, 256)
928 eq(read
, b
'\0' * 256)
929 data
= rand_data(256)
930 self
.image
.write(data
, 0)
931 self
.image
.create_snap('snap1')
932 read
= self
.image
.read(0, 256)
934 new_size
= IMG_SIZE
* 2
935 self
.image
.resize(new_size
)
936 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
937 self
.image
.write(data
, new_size
- 256)
938 self
.image
.create_snap('snap2')
939 read
= self
.image
.read(new_size
- 256, 256)
941 self
.image
.rollback_to_snap('snap1')
942 check_stat(self
.image
.stat(), IMG_SIZE
, IMG_ORDER
)
943 assert_raises(InvalidArgument
, self
.image
.read
, new_size
- 256, 256)
944 self
.image
.rollback_to_snap('snap2')
945 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
946 read
= self
.image
.read(new_size
- 256, 256)
948 self
.image
.remove_snap('snap1')
949 self
.image
.remove_snap('snap2')
951 def test_set_snap(self
):
952 self
.image
.write(b
'\0' * 256, 0)
953 self
.image
.create_snap('snap1')
954 read
= self
.image
.read(0, 256)
955 eq(read
, b
'\0' * 256)
956 data
= rand_data(256)
957 self
.image
.write(data
, 0)
958 read
= self
.image
.read(0, 256)
960 self
.image
.set_snap('snap1')
961 read
= self
.image
.read(0, 256)
962 eq(read
, b
'\0' * 256)
963 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
964 self
.image
.remove_snap('snap1')
966 def test_set_no_snap(self
):
967 self
.image
.write(b
'\0' * 256, 0)
968 self
.image
.create_snap('snap1')
969 read
= self
.image
.read(0, 256)
970 eq(read
, b
'\0' * 256)
971 data
= rand_data(256)
972 self
.image
.write(data
, 0)
973 read
= self
.image
.read(0, 256)
975 self
.image
.set_snap('snap1')
976 read
= self
.image
.read(0, 256)
977 eq(read
, b
'\0' * 256)
978 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
979 self
.image
.set_snap(None)
980 read
= self
.image
.read(0, 256)
982 self
.image
.remove_snap('snap1')
984 def test_set_snap_by_id(self
):
985 self
.image
.write(b
'\0' * 256, 0)
986 self
.image
.create_snap('snap1')
987 read
= self
.image
.read(0, 256)
988 eq(read
, b
'\0' * 256)
989 data
= rand_data(256)
990 self
.image
.write(data
, 0)
991 read
= self
.image
.read(0, 256)
993 snaps
= list(self
.image
.list_snaps())
994 self
.image
.set_snap_by_id(snaps
[0]['id'])
995 read
= self
.image
.read(0, 256)
996 eq(read
, b
'\0' * 256)
997 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
998 self
.image
.set_snap_by_id(None)
999 read
= self
.image
.read(0, 256)
1001 self
.image
.remove_snap('snap1')
1003 def test_snap_get_name(self
):
1004 eq([], list(self
.image
.list_snaps()))
1005 self
.image
.create_snap('snap1')
1006 self
.image
.create_snap('snap2')
1007 self
.image
.create_snap('snap3')
1009 for snap
in self
.image
.list_snaps():
1010 expected_snap_name
= self
.image
.snap_get_name(snap
['id'])
1011 eq(expected_snap_name
, snap
['name'])
1012 self
.image
.remove_snap('snap1')
1013 self
.image
.remove_snap('snap2')
1014 self
.image
.remove_snap('snap3')
1015 eq([], list(self
.image
.list_snaps()))
1017 assert_raises(ImageNotFound
, self
.image
.snap_get_name
, 1)
1019 def test_snap_get_id(self
):
1020 eq([], list(self
.image
.list_snaps()))
1021 self
.image
.create_snap('snap1')
1022 self
.image
.create_snap('snap2')
1023 self
.image
.create_snap('snap3')
1025 for snap
in self
.image
.list_snaps():
1026 expected_snap_id
= self
.image
.snap_get_id(snap
['name'])
1027 eq(expected_snap_id
, snap
['id'])
1028 self
.image
.remove_snap('snap1')
1029 self
.image
.remove_snap('snap2')
1030 self
.image
.remove_snap('snap3')
1031 eq([], list(self
.image
.list_snaps()))
1033 assert_raises(ImageNotFound
, self
.image
.snap_get_id
, 'snap1')
1035 def test_set_snap_sparse(self
):
1036 self
.image
.create_snap('snap1')
1037 read
= self
.image
.read(0, 256)
1038 eq(read
, b
'\0' * 256)
1039 data
= rand_data(256)
1040 self
.image
.write(data
, 0)
1041 read
= self
.image
.read(0, 256)
1043 self
.image
.set_snap('snap1')
1044 read
= self
.image
.read(0, 256)
1045 eq(read
, b
'\0' * 256)
1046 assert_raises(ReadOnlyImage
, self
.image
.write
, data
, 0)
1047 self
.image
.remove_snap('snap1')
1049 def test_many_snaps(self
):
1051 for i
in range(num_snaps
):
1052 self
.image
.create_snap(str(i
))
1053 snaps
= sorted(self
.image
.list_snaps(),
1054 key
=lambda snap
: int(snap
['name']))
1055 eq(len(snaps
), num_snaps
)
1056 for i
, snap
in enumerate(snaps
):
1057 eq(snap
['size'], IMG_SIZE
)
1058 eq(snap
['name'], str(i
))
1059 for i
in range(num_snaps
):
1060 self
.image
.remove_snap(str(i
))
1062 def test_set_snap_deleted(self
):
1063 self
.image
.write(b
'\0' * 256, 0)
1064 self
.image
.create_snap('snap1')
1065 read
= self
.image
.read(0, 256)
1066 eq(read
, b
'\0' * 256)
1067 data
= rand_data(256)
1068 self
.image
.write(data
, 0)
1069 read
= self
.image
.read(0, 256)
1071 self
.image
.set_snap('snap1')
1072 self
.image
.remove_snap('snap1')
1073 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
1074 self
.image
.set_snap(None)
1075 read
= self
.image
.read(0, 256)
1078 def test_set_snap_recreated(self
):
1079 self
.image
.write(b
'\0' * 256, 0)
1080 self
.image
.create_snap('snap1')
1081 read
= self
.image
.read(0, 256)
1082 eq(read
, b
'\0' * 256)
1083 data
= rand_data(256)
1084 self
.image
.write(data
, 0)
1085 read
= self
.image
.read(0, 256)
1087 self
.image
.set_snap('snap1')
1088 self
.image
.remove_snap('snap1')
1089 self
.image
.create_snap('snap1')
1090 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
1091 self
.image
.set_snap(None)
1092 read
= self
.image
.read(0, 256)
1094 self
.image
.remove_snap('snap1')
1096 def test_lock_unlock(self
):
1097 assert_raises(ImageNotFound
, self
.image
.unlock
, '')
1098 self
.image
.lock_exclusive('')
1099 assert_raises(ImageExists
, self
.image
.lock_exclusive
, '')
1100 assert_raises(ImageBusy
, self
.image
.lock_exclusive
, 'test')
1101 assert_raises(ImageExists
, self
.image
.lock_shared
, '', '')
1102 assert_raises(ImageBusy
, self
.image
.lock_shared
, 'foo', '')
1103 self
.image
.unlock('')
1105 def test_list_lockers(self
):
1106 eq([], self
.image
.list_lockers())
1107 self
.image
.lock_exclusive('test')
1108 lockers
= self
.image
.list_lockers()
1109 eq(1, len(lockers
['lockers']))
1110 _
, cookie
, _
= lockers
['lockers'][0]
1112 eq('', lockers
['tag'])
1113 assert lockers
['exclusive']
1114 self
.image
.unlock('test')
1115 eq([], self
.image
.list_lockers())
1118 for i
in range(num_shared
):
1119 self
.image
.lock_shared(str(i
), 'tag')
1120 lockers
= self
.image
.list_lockers()
1121 eq('tag', lockers
['tag'])
1122 assert not lockers
['exclusive']
1123 eq(num_shared
, len(lockers
['lockers']))
1124 cookies
= sorted(map(lambda x
: x
[1], lockers
['lockers']))
1125 for i
in range(num_shared
):
1126 eq(str(i
), cookies
[i
])
1127 self
.image
.unlock(str(i
))
1128 eq([], self
.image
.list_lockers())
1130 def test_diff_iterate(self
):
1131 check_diff(self
.image
, 0, IMG_SIZE
, None, [])
1132 self
.image
.write(b
'a' * 256, 0)
1133 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 256, True)])
1134 self
.image
.write(b
'b' * 256, 256)
1135 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
1136 self
.image
.discard(128, 256)
1137 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
1139 self
.image
.create_snap('snap1')
1140 self
.image
.discard(0, 1 << IMG_ORDER
)
1141 self
.image
.create_snap('snap2')
1142 self
.image
.set_snap('snap2')
1143 check_diff(self
.image
, 0, IMG_SIZE
, 'snap1', [(0, 512, False)])
1144 self
.image
.remove_snap('snap1')
1145 self
.image
.remove_snap('snap2')
1147 def test_aio_read(self
):
1148 # this is a list so that the local cb() can modify it
1153 # test1: success case
1154 comp
= self
.image
.aio_read(0, 20, cb
)
1155 comp
.wait_for_complete_and_cb()
1156 eq(retval
[0], b
'\0' * 20)
1157 eq(comp
.get_return_value(), 20)
1158 eq(sys
.getrefcount(comp
), 2)
1162 comp
= self
.image
.aio_read(IMG_SIZE
, 20, cb
)
1163 comp
.wait_for_complete_and_cb()
1165 assert(comp
.get_return_value() < 0)
1166 eq(sys
.getrefcount(comp
), 2)
1168 def test_aio_write(self
):
1171 retval
[0] = comp
.get_return_value()
1173 data
= rand_data(256)
1174 comp
= self
.image
.aio_write(data
, 256, cb
)
1175 comp
.wait_for_complete_and_cb()
1177 eq(comp
.get_return_value(), 0)
1178 eq(sys
.getrefcount(comp
), 2)
1179 eq(self
.image
.read(256, 256), data
)
1181 def test_aio_discard(self
):
1184 retval
[0] = comp
.get_return_value()
1186 data
= rand_data(256)
1187 self
.image
.write(data
, 0)
1188 comp
= self
.image
.aio_discard(0, 256, cb
)
1189 comp
.wait_for_complete_and_cb()
1191 eq(comp
.get_return_value(), 0)
1192 eq(sys
.getrefcount(comp
), 2)
1193 eq(self
.image
.read(256, 256), b
'\0' * 256)
1195 def test_aio_flush(self
):
1198 retval
[0] = comp
.get_return_value()
1200 comp
= self
.image
.aio_flush(cb
)
1201 comp
.wait_for_complete_and_cb()
1203 eq(sys
.getrefcount(comp
), 2)
1205 def test_metadata(self
):
1206 metadata
= list(self
.image
.metadata_list())
1207 eq(len(metadata
), 0)
1208 assert_raises(KeyError, self
.image
.metadata_get
, "key1")
1209 self
.image
.metadata_set("key1", "value1")
1210 self
.image
.metadata_set("key2", "value2")
1211 value
= self
.image
.metadata_get("key1")
1213 value
= self
.image
.metadata_get("key2")
1215 metadata
= list(self
.image
.metadata_list())
1216 eq(len(metadata
), 2)
1217 self
.image
.metadata_remove("key1")
1218 metadata
= list(self
.image
.metadata_list())
1219 eq(len(metadata
), 1)
1220 eq(metadata
[0], ("key2", "value2"))
1221 self
.image
.metadata_remove("key2")
1222 assert_raises(KeyError, self
.image
.metadata_remove
, "key2")
1223 metadata
= list(self
.image
.metadata_list())
1224 eq(len(metadata
), 0)
1228 self
.image
.metadata_set("key" + str(i
), "X" * 1025)
1229 metadata
= list(self
.image
.metadata_list())
1230 eq(len(metadata
), N
)
1232 self
.image
.metadata_remove("key" + str(i
))
1233 metadata
= list(self
.image
.metadata_list())
1234 eq(len(metadata
), N
- i
- 1)
1236 def test_watchers_list(self
):
1237 watchers
= list(self
.image
.watchers_list())
1238 # The image is open (in r/w mode) from setup, so expect there to be one
1240 eq(len(watchers
), 1)
1242 def test_config_list(self
):
1243 with
Image(ioctx
, image_name
) as image
:
1244 for option
in image
.config_list():
1245 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1247 image
.metadata_set("conf_rbd_cache", "true")
1249 for option
in image
.config_list():
1250 if option
['name'] == "rbd_cache":
1251 eq(option
['source'], RBD_CONFIG_SOURCE_IMAGE
)
1253 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1255 image
.metadata_remove("conf_rbd_cache")
1257 for option
in image
.config_list():
1258 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1260 def test_image_config_set_and_get_and_remove(self
):
1261 with
Image(ioctx
, image_name
) as image
:
1262 for option
in image
.config_list():
1263 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1265 image
.config_set("rbd_request_timed_out_seconds", "100")
1266 modify_value
= image
.config_get("rbd_request_timed_out_seconds")
1267 eq(modify_value
, '100')
1269 image
.config_remove("rbd_request_timed_out_seconds")
1271 for option
in image
.config_list():
1272 eq(option
['source'], RBD_CONFIG_SOURCE_CONFIG
)
1274 def test_sparsify(self
):
1275 assert_raises(InvalidArgument
, self
.image
.sparsify
, 16)
1276 self
.image
.sparsify(4096)
1278 class TestImageId(object):
1283 self
.image
= Image(ioctx
, image_name
)
1284 self
.image2
= Image(ioctx
, None, None, False, self
.image
.id())
1293 def test_read(self
):
1294 data
= self
.image2
.read(0, 20)
1295 eq(data
, b
'\0' * 20)
1297 def test_write(self
):
1298 data
= rand_data(256)
1299 self
.image2
.write(data
, 0)
1301 def test_resize(self
):
1302 new_size
= IMG_SIZE
* 2
1303 self
.image2
.resize(new_size
)
1304 info
= self
.image2
.stat()
1305 check_stat(info
, new_size
, IMG_ORDER
)
1307 def check_diff(image
, offset
, length
, from_snapshot
, expected
):
1309 def cb(offset
, length
, exists
):
1310 extents
.append((offset
, length
, exists
))
1311 image
.diff_iterate(0, IMG_SIZE
, None, cb
)
1312 eq(extents
, expected
)
1314 class TestClone(object):
1316 @require_features([RBD_FEATURE_LAYERING
])
1322 self
.image
= Image(ioctx
, image_name
)
1323 data
= rand_data(256)
1324 self
.image
.write(data
, IMG_SIZE
// 2)
1325 self
.image
.create_snap('snap1')
1327 self
.image
.protect_snap('snap1')
1328 self
.clone_name
= get_temp_image_name()
1329 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
1331 self
.clone
= Image(ioctx
, self
.clone_name
)
1336 self
.rbd
.remove(ioctx
, self
.clone_name
)
1337 self
.image
.unprotect_snap('snap1')
1338 self
.image
.remove_snap('snap1')
1342 def _test_with_params(self
, features
=None, order
=None, stripe_unit
=None,
1344 self
.image
.create_snap('snap2')
1345 self
.image
.protect_snap('snap2')
1346 clone_name2
= get_temp_image_name()
1347 if features
is None:
1348 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
)
1350 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1352 elif stripe_unit
is None:
1353 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1355 elif stripe_count
is None:
1356 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1357 features
, order
, stripe_unit
)
1359 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
1360 features
, order
, stripe_unit
, stripe_count
)
1361 self
.rbd
.remove(ioctx
, clone_name2
)
1362 self
.image
.unprotect_snap('snap2')
1363 self
.image
.remove_snap('snap2')
1365 def test_with_params(self
):
1366 self
._test
_with
_params
()
1368 def test_with_params2(self
):
1370 self
._test
_with
_params
(features
, self
.image
.stat()['order'])
1372 @require_features([RBD_FEATURE_STRIPINGV2
])
1373 def test_with_params3(self
):
1375 self
._test
_with
_params
(features
, self
.image
.stat()['order'],
1376 self
.image
.stripe_unit(),
1377 self
.image
.stripe_count())
1379 def test_unprotected(self
):
1380 self
.image
.create_snap('snap2')
1382 clone_name2
= get_temp_image_name()
1383 rados
.conf_set("rbd_default_clone_format", "1")
1384 assert_raises(InvalidArgument
, self
.rbd
.clone
, ioctx
, image_name
,
1385 'snap2', ioctx
, clone_name2
, features
)
1386 rados
.conf_set("rbd_default_clone_format", "auto")
1387 self
.image
.remove_snap('snap2')
1389 def test_unprotect_with_children(self
):
1391 # can't remove a snapshot that has dependent clones
1392 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
1394 # validate parent info of clone created by TestClone.setUp
1395 (pool
, image
, snap
) = self
.clone
.parent_info()
1397 eq(image
, image_name
)
1399 eq(self
.image
.id(), self
.clone
.parent_id())
1401 # create a new pool...
1402 pool_name2
= get_temp_pool_name()
1403 rados
.create_pool(pool_name2
)
1404 other_ioctx
= rados
.open_ioctx(pool_name2
)
1405 other_ioctx
.application_enable('rbd')
1407 # ...with a clone of the same parent
1408 other_clone_name
= get_temp_image_name()
1409 rados
.conf_set("rbd_default_clone_format", "1")
1410 self
.rbd
.clone(ioctx
, image_name
, 'snap1', other_ioctx
,
1411 other_clone_name
, features
)
1412 rados
.conf_set("rbd_default_clone_format", "auto")
1413 self
.other_clone
= Image(other_ioctx
, other_clone_name
)
1414 # validate its parent info
1415 (pool
, image
, snap
) = self
.other_clone
.parent_info()
1417 eq(image
, image_name
)
1419 eq(self
.image
.id(), self
.other_clone
.parent_id())
1421 # can't unprotect snap with children
1422 assert_raises(ImageBusy
, self
.image
.unprotect_snap
, 'snap1')
1424 # 2 children, check that cannot remove the parent snap
1425 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
1427 # close and remove other pool's clone
1428 self
.other_clone
.close()
1429 self
.rbd
.remove(other_ioctx
, other_clone_name
)
1431 # check that we cannot yet remove the parent snap
1432 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
1435 rados
.delete_pool(pool_name2
)
1437 # unprotect, remove parent snap happen in cleanup, and should succeed
1439 def test_stat(self
):
1440 image_info
= self
.image
.stat()
1441 clone_info
= self
.clone
.stat()
1442 eq(clone_info
['size'], image_info
['size'])
1443 eq(clone_info
['size'], self
.clone
.overlap())
1445 def test_resize_stat(self
):
1446 self
.clone
.resize(IMG_SIZE
// 2)
1447 image_info
= self
.image
.stat()
1448 clone_info
= self
.clone
.stat()
1449 eq(clone_info
['size'], IMG_SIZE
// 2)
1450 eq(image_info
['size'], IMG_SIZE
)
1451 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
1453 self
.clone
.resize(IMG_SIZE
* 2)
1454 image_info
= self
.image
.stat()
1455 clone_info
= self
.clone
.stat()
1456 eq(clone_info
['size'], IMG_SIZE
* 2)
1457 eq(image_info
['size'], IMG_SIZE
)
1458 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
1460 def test_resize_io(self
):
1461 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1462 self
.image
.resize(0)
1463 self
.clone
.resize(IMG_SIZE
// 2 + 128)
1464 child_data
= self
.clone
.read(IMG_SIZE
// 2, 128)
1465 eq(child_data
, parent_data
[:128])
1466 self
.clone
.resize(IMG_SIZE
)
1467 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1468 eq(child_data
, parent_data
[:128] + (b
'\0' * 128))
1469 self
.clone
.resize(IMG_SIZE
// 2 + 1)
1470 child_data
= self
.clone
.read(IMG_SIZE
// 2, 1)
1471 eq(child_data
, parent_data
[0:1])
1472 self
.clone
.resize(0)
1473 self
.clone
.resize(IMG_SIZE
)
1474 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1475 eq(child_data
, b
'\0' * 256)
1477 def test_read(self
):
1478 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1479 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1480 eq(child_data
, parent_data
)
1482 def test_write(self
):
1483 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1484 new_data
= rand_data(256)
1485 self
.clone
.write(new_data
, IMG_SIZE
// 2 + 256)
1486 child_data
= self
.clone
.read(IMG_SIZE
// 2 + 256, 256)
1487 eq(child_data
, new_data
)
1488 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1489 eq(child_data
, parent_data
)
1490 parent_data
= self
.image
.read(IMG_SIZE
// 2 + 256, 256)
1491 eq(parent_data
, b
'\0' * 256)
1493 def check_children(self
, expected
):
1494 actual
= self
.image
.list_children()
1495 # dedup for cache pools until
1496 # http://tracker.ceph.com/issues/8187 is fixed
1497 deduped
= set([(pool_name
, image
[1]) for image
in actual
])
1498 eq(deduped
, set(expected
))
1500 def check_children2(self
, expected
):
1501 actual
= [{k
:v
for k
,v
in x
.items() if k
in expected
[0]} \
1502 for x
in self
.image
.list_children2()]
1503 eq(actual
, expected
)
1505 def check_descendants(self
, expected
):
1506 eq(list(self
.image
.list_descendants()), expected
)
1508 def get_image_id(self
, ioctx
, name
):
1509 with
Image(ioctx
, name
) as image
:
1512 def test_list_children(self
):
1515 self
.image
.set_snap('snap1')
1516 self
.check_children([(pool_name
, self
.clone_name
)])
1517 self
.check_children2(
1518 [{'pool': pool_name
, 'pool_namespace': '',
1519 'image': self
.clone_name
, 'trash': False,
1520 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1521 self
.check_descendants(
1522 [{'pool': pool_name
, 'pool_namespace': '',
1523 'image': self
.clone_name
, 'trash': False,
1524 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1526 self
.rbd
.remove(ioctx
, self
.clone_name
)
1527 eq(self
.image
.list_children(), [])
1528 eq(list(self
.image
.list_children2()), [])
1529 eq(list(self
.image
.list_descendants()), [])
1531 clone_name
= get_temp_image_name() + '_'
1532 expected_children
= []
1533 expected_children2
= []
1535 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
,
1536 clone_name
+ str(i
), features
)
1537 expected_children
.append((pool_name
, clone_name
+ str(i
)))
1538 expected_children2
.append(
1539 {'pool': pool_name
, 'pool_namespace': '',
1540 'image': clone_name
+ str(i
), 'trash': False,
1541 'id': self
.get_image_id(ioctx
, clone_name
+ str(i
))})
1542 self
.check_children(expected_children
)
1543 self
.check_children2(expected_children2
)
1544 self
.check_descendants(expected_children2
)
1546 image6_id
= self
.get_image_id(ioctx
, clone_name
+ str(5))
1547 RBD().trash_move(ioctx
, clone_name
+ str(5), 0)
1548 expected_children
.remove((pool_name
, clone_name
+ str(5)))
1549 for item
in expected_children2
:
1550 for k
, v
in item
.items():
1552 item
["trash"] = True
1553 self
.check_children(expected_children
)
1554 self
.check_children2(expected_children2
)
1555 self
.check_descendants(expected_children2
)
1557 RBD().trash_restore(ioctx
, image6_id
, clone_name
+ str(5))
1558 expected_children
.append((pool_name
, clone_name
+ str(5)))
1559 for item
in expected_children2
:
1560 for k
, v
in item
.items():
1562 item
["trash"] = False
1563 self
.check_children(expected_children
)
1564 self
.check_children2(expected_children2
)
1565 self
.check_descendants(expected_children2
)
1568 self
.rbd
.remove(ioctx
, clone_name
+ str(i
))
1569 expected_children
.remove((pool_name
, clone_name
+ str(i
)))
1570 expected_children2
.pop(0)
1571 self
.check_children(expected_children
)
1572 self
.check_children2(expected_children2
)
1573 self
.check_descendants(expected_children2
)
1575 eq(self
.image
.list_children(), [])
1576 eq(list(self
.image
.list_children2()), [])
1577 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
1579 self
.check_children([(pool_name
, self
.clone_name
)])
1580 self
.check_children2(
1581 [{'pool': pool_name
, 'pool_namespace': '',
1582 'image': self
.clone_name
, 'trash': False,
1583 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1584 self
.check_descendants(
1585 [{'pool': pool_name
, 'pool_namespace': '',
1586 'image': self
.clone_name
, 'trash': False,
1587 'id': self
.get_image_id(ioctx
, self
.clone_name
)}])
1588 self
.clone
= Image(ioctx
, self
.clone_name
)
1590 def test_flatten_errors(self
):
1591 # test that we can't flatten a non-clone
1592 assert_raises(InvalidArgument
, self
.image
.flatten
)
1594 # test that we can't flatten a snapshot
1595 self
.clone
.create_snap('snap2')
1596 self
.clone
.set_snap('snap2')
1597 assert_raises(ReadOnlyImage
, self
.clone
.flatten
)
1598 self
.clone
.remove_snap('snap2')
1600 def check_flatten_with_order(self
, new_order
):
1603 clone_name2
= get_temp_image_name()
1604 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1605 features
, new_order
)
1606 #with Image(ioctx, 'clone2') as clone:
1607 clone2
= Image(ioctx
, clone_name2
)
1609 eq(clone2
.overlap(), 0)
1611 self
.rbd
.remove(ioctx
, clone_name2
)
1613 # flatten after resizing to non-block size
1614 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1615 features
, new_order
)
1616 with
Image(ioctx
, clone_name2
) as clone
:
1617 clone
.resize(IMG_SIZE
// 2 - 1)
1619 eq(0, clone
.overlap())
1620 self
.rbd
.remove(ioctx
, clone_name2
)
1622 # flatten after resizing to non-block size
1623 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1624 features
, new_order
)
1625 with
Image(ioctx
, clone_name2
) as clone
:
1626 clone
.resize(IMG_SIZE
// 2 + 1)
1628 eq(clone
.overlap(), 0)
1629 self
.rbd
.remove(ioctx
, clone_name2
)
1631 def test_flatten_basic(self
):
1632 self
.check_flatten_with_order(IMG_ORDER
)
1634 def test_flatten_smaller_order(self
):
1635 self
.check_flatten_with_order(IMG_ORDER
- 2)
1637 def test_flatten_larger_order(self
):
1638 self
.check_flatten_with_order(IMG_ORDER
+ 2)
1640 def test_flatten_drops_cache(self
):
1643 clone_name2
= get_temp_image_name()
1644 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1645 features
, IMG_ORDER
)
1646 with
Image(ioctx
, clone_name2
) as clone
:
1647 with
Image(ioctx
, clone_name2
) as clone2
:
1648 # cache object non-existence
1649 data
= clone
.read(IMG_SIZE
// 2, 256)
1650 clone2_data
= clone2
.read(IMG_SIZE
// 2, 256)
1651 eq(data
, clone2_data
)
1653 assert_raises(ImageNotFound
, clone
.parent_info
)
1654 assert_raises(ImageNotFound
, clone2
.parent_info
)
1655 assert_raises(ImageNotFound
, clone
.parent_id
)
1656 assert_raises(ImageNotFound
, clone2
.parent_id
)
1657 after_flatten
= clone
.read(IMG_SIZE
// 2, 256)
1658 eq(data
, after_flatten
)
1659 after_flatten
= clone2
.read(IMG_SIZE
// 2, 256)
1660 eq(data
, after_flatten
)
1661 self
.rbd
.remove(ioctx
, clone_name2
)
1663 def test_flatten_multi_level(self
):
1664 self
.clone
.create_snap('snap2')
1665 self
.clone
.protect_snap('snap2')
1666 clone_name3
= get_temp_image_name()
1667 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1669 self
.clone
.flatten()
1670 with
Image(ioctx
, clone_name3
) as clone3
:
1672 self
.clone
.unprotect_snap('snap2')
1673 self
.clone
.remove_snap('snap2')
1674 self
.rbd
.remove(ioctx
, clone_name3
)
1676 def test_flatten_with_progress(self
):
1677 d
= {'received_callback': False}
1678 def progress_cb(current
, total
):
1679 d
['received_callback'] = True
1684 clone_name
= get_temp_image_name()
1685 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name
,
1687 with
Image(ioctx
, clone_name
) as clone
:
1688 clone
.flatten(on_progress
=progress_cb
)
1689 self
.rbd
.remove(ioctx
, clone_name
)
1690 eq(True, d
['received_callback'])
1692 def test_resize_flatten_multi_level(self
):
1693 self
.clone
.create_snap('snap2')
1694 self
.clone
.protect_snap('snap2')
1695 clone_name3
= get_temp_image_name()
1696 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1698 self
.clone
.resize(1)
1699 orig_data
= self
.image
.read(0, 256)
1700 with
Image(ioctx
, clone_name3
) as clone3
:
1701 clone3_data
= clone3
.read(0, 256)
1702 eq(orig_data
, clone3_data
)
1703 self
.clone
.flatten()
1704 with
Image(ioctx
, clone_name3
) as clone3
:
1705 clone3_data
= clone3
.read(0, 256)
1706 eq(orig_data
, clone3_data
)
1707 self
.rbd
.remove(ioctx
, clone_name3
)
1708 self
.clone
.unprotect_snap('snap2')
1709 self
.clone
.remove_snap('snap2')
1711 def test_trash_snapshot(self
):
1712 self
.image
.create_snap('snap2')
1714 clone_name
= get_temp_image_name()
1715 rados
.conf_set("rbd_default_clone_format", "2")
1716 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name
, features
)
1717 rados
.conf_set("rbd_default_clone_format", "auto")
1719 self
.image
.remove_snap('snap2')
1721 snaps
= [s
for s
in self
.image
.list_snaps() if s
['name'] != 'snap1']
1722 eq([RBD_SNAP_NAMESPACE_TYPE_TRASH
], [s
['namespace'] for s
in snaps
])
1723 eq([{'original_name' : 'snap2'}], [s
['trash'] for s
in snaps
])
1725 self
.rbd
.remove(ioctx
, clone_name
)
1726 eq([], [s
for s
in self
.image
.list_snaps() if s
['name'] != 'snap1'])
1728 class TestExclusiveLock(object):
1730 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
1733 rados2
= Rados(conffile
='')
1736 ioctx2
= rados2
.open_ioctx(pool_name
)
1746 def test_ownership(self
):
1747 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1748 image1
.write(b
'0'*256, 0)
1749 eq(image1
.is_exclusive_lock_owner(), True)
1750 eq(image2
.is_exclusive_lock_owner(), False)
1752 def test_snapshot_leadership(self
):
1753 with
Image(ioctx
, image_name
) as image
:
1754 image
.create_snap('snap')
1755 eq(image
.is_exclusive_lock_owner(), True)
1757 with
Image(ioctx
, image_name
) as image
:
1758 image
.write(b
'0'*256, 0)
1759 eq(image
.is_exclusive_lock_owner(), True)
1760 image
.set_snap('snap')
1761 eq(image
.is_exclusive_lock_owner(), False)
1762 with
Image(ioctx
, image_name
, snapshot
='snap') as image
:
1763 eq(image
.is_exclusive_lock_owner(), False)
1765 with
Image(ioctx
, image_name
) as image
:
1766 image
.remove_snap('snap')
1768 def test_read_only_leadership(self
):
1769 with
Image(ioctx
, image_name
, read_only
=True) as image
:
1770 eq(image
.is_exclusive_lock_owner(), False)
1772 def test_follower_flatten(self
):
1773 with
Image(ioctx
, image_name
) as image
:
1774 image
.create_snap('snap')
1775 image
.protect_snap('snap')
1777 RBD().clone(ioctx
, image_name
, 'snap', ioctx
, 'clone', features
)
1778 with
Image(ioctx
, 'clone') as image1
, Image(ioctx2
, 'clone') as image2
:
1779 data
= rand_data(256)
1780 image1
.write(data
, 0)
1782 assert_raises(ImageNotFound
, image1
.parent_info
)
1783 assert_raises(ImageNotFound
, image1
.parent_id
)
1787 image2
.parent_info()
1788 except ImageNotFound
:
1793 RBD().remove(ioctx
, 'clone')
1794 with
Image(ioctx
, image_name
) as image
:
1795 image
.unprotect_snap('snap')
1796 image
.remove_snap('snap')
1798 def test_follower_resize(self
):
1799 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1800 image1
.write(b
'0'*256, 0)
1801 for new_size
in [IMG_SIZE
* 2, IMG_SIZE
// 2]:
1802 image2
.resize(new_size
);
1803 eq(new_size
, image1
.size())
1805 if new_size
== image2
.size():
1808 eq(new_size
, image2
.size())
1810 def test_follower_snap_create(self
):
1811 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1812 image2
.create_snap('snap1')
1813 image1
.remove_snap('snap1')
1815 def test_follower_snap_rollback(self
):
1816 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1817 image1
.create_snap('snap')
1819 assert_raises(ReadOnlyImage
, image2
.rollback_to_snap
, 'snap')
1820 image1
.rollback_to_snap('snap')
1822 image1
.remove_snap('snap')
1824 def test_follower_discard(self
):
1826 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1827 data
= rand_data(256)
1828 image1
.write(data
, 0)
1829 image2
.discard(0, 256)
1830 eq(image1
.is_exclusive_lock_owner(), False)
1831 eq(image2
.is_exclusive_lock_owner(), True)
1832 read
= image2
.read(0, 256)
1833 if rados
.conf_get('rbd_skip_partial_discard') == 'false':
1834 eq(256 * b
'\0', read
)
1838 def test_follower_write(self
):
1839 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1840 data
= rand_data(256)
1841 image1
.write(data
, 0)
1842 image2
.write(data
, IMG_SIZE
// 2)
1843 eq(image1
.is_exclusive_lock_owner(), False)
1844 eq(image2
.is_exclusive_lock_owner(), True)
1845 for offset
in [0, IMG_SIZE
// 2]:
1846 read
= image2
.read(offset
, 256)
1848 def test_acquire_release_lock(self
):
1849 with
Image(ioctx
, image_name
) as image
:
1850 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
1851 image
.lock_release()
1853 def test_break_lock(self
):
1854 blacklist_rados
= Rados(conffile
='')
1855 blacklist_rados
.connect()
1857 blacklist_ioctx
= blacklist_rados
.open_ioctx(pool_name
)
1859 rados2
.conf_set('rbd_blacklist_on_break_lock', 'true')
1860 with
Image(ioctx2
, image_name
) as image
, \
1861 Image(blacklist_ioctx
, image_name
) as blacklist_image
:
1863 lock_owners
= list(image
.lock_get_owners())
1864 eq(0, len(lock_owners
))
1866 blacklist_image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
1867 assert_raises(ReadOnlyImage
, image
.lock_acquire
,
1868 RBD_LOCK_MODE_EXCLUSIVE
)
1869 lock_owners
= list(image
.lock_get_owners())
1870 eq(1, len(lock_owners
))
1871 eq(RBD_LOCK_MODE_EXCLUSIVE
, lock_owners
[0]['mode'])
1872 image
.lock_break(RBD_LOCK_MODE_EXCLUSIVE
,
1873 lock_owners
[0]['owner'])
1875 assert_raises(ConnectionShutdown
,
1876 blacklist_image
.is_exclusive_lock_owner
)
1878 blacklist_rados
.wait_for_latest_osdmap()
1879 data
= rand_data(256)
1880 assert_raises(ConnectionShutdown
,
1881 blacklist_image
.write
, data
, 0)
1883 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
1886 blacklist_image
.close()
1887 except ConnectionShutdown
:
1890 blacklist_ioctx
.close()
1892 blacklist_rados
.shutdown()
1894 class TestMirroring(object):
1897 def check_info(info
, global_id
, state
, primary
=None):
1898 eq(global_id
, info
['global_id'])
1899 eq(state
, info
['state'])
1900 if primary
is not None:
1901 eq(primary
, info
['primary'])
1905 self
.initial_mirror_mode
= self
.rbd
.mirror_mode_get(ioctx
)
1906 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
1908 self
.image
= Image(ioctx
, image_name
)
1913 self
.rbd
.mirror_mode_set(ioctx
, self
.initial_mirror_mode
)
1915 def test_uuid(self
):
1916 mirror_uuid
= self
.rbd
.mirror_uuid_get(ioctx
)
1919 def test_site_name(self
):
1920 site_name
= "us-west-1"
1921 self
.rbd
.mirror_site_name_set(rados
, site_name
)
1922 eq(site_name
, self
.rbd
.mirror_site_name_get(rados
))
1923 self
.rbd
.mirror_site_name_set(rados
, "")
1924 eq(rados
.get_fsid(), self
.rbd
.mirror_site_name_get(rados
))
1926 def test_mirror_peer_bootstrap(self
):
1927 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
1929 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_DISABLED
)
1930 assert_raises(InvalidArgument
, self
.rbd
.mirror_peer_bootstrap_create
,
1933 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
1934 token_b64
= self
.rbd
.mirror_peer_bootstrap_create(ioctx
)
1935 token
= base64
.b64decode(token_b64
)
1936 token_dict
= json
.loads(token
)
1937 eq(sorted(['fsid', 'client_id', 'key', 'mon_host']),
1938 sorted(list(token_dict
.keys())))
1940 # requires different cluster
1941 assert_raises(InvalidArgument
, self
.rbd
.mirror_peer_bootstrap_import
,
1942 ioctx
, RBD_MIRROR_PEER_DIRECTION_RX
, token_b64
)
1944 def test_mirror_peer(self
):
1945 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
1946 site_name
= "test_site"
1947 client_name
= "test_client"
1948 uuid
= self
.rbd
.mirror_peer_add(ioctx
, site_name
, client_name
,
1949 direction
=RBD_MIRROR_PEER_DIRECTION_RX_TX
)
1953 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX
,
1954 'site_name' : site_name
,
1955 'cluster_name' : site_name
,
1957 'client_name' : client_name
,
1959 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
1960 cluster_name
= "test_cluster1"
1961 self
.rbd
.mirror_peer_set_cluster(ioctx
, uuid
, cluster_name
)
1962 client_name
= "test_client1"
1963 self
.rbd
.mirror_peer_set_client(ioctx
, uuid
, client_name
)
1966 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX
,
1967 'site_name' : cluster_name
,
1968 'cluster_name' : cluster_name
,
1970 'client_name' : client_name
,
1972 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
1975 RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST
: 'host1',
1976 RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY
: 'abc'
1978 self
.rbd
.mirror_peer_set_attributes(ioctx
, uuid
, attribs
)
1979 eq(attribs
, self
.rbd
.mirror_peer_get_attributes(ioctx
, uuid
))
1981 self
.rbd
.mirror_peer_remove(ioctx
, uuid
)
1982 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
1984 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
1985 RBD_FEATURE_JOURNALING
])
1986 def test_mirror_image(self
):
1988 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
1989 self
.image
.mirror_image_disable(True)
1990 info
= self
.image
.mirror_image_get_info()
1991 self
.check_info(info
, '', RBD_MIRROR_IMAGE_DISABLED
, False)
1993 self
.image
.mirror_image_enable()
1994 info
= self
.image
.mirror_image_get_info()
1995 global_id
= info
['global_id']
1996 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
1998 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
2001 self
.image
.mirror_image_disable(True)
2002 except InvalidArgument
:
2004 eq(True, fail
) # Fails because of mirror mode pool
2006 self
.image
.mirror_image_demote()
2007 info
= self
.image
.mirror_image_get_info()
2008 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, False)
2010 entries
= dict(self
.rbd
.mirror_image_info_list(ioctx
))
2011 info
['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL
;
2012 eq(info
, entries
[self
.image
.id()])
2014 self
.image
.mirror_image_resync()
2016 self
.image
.mirror_image_promote(True)
2017 info
= self
.image
.mirror_image_get_info()
2018 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2020 entries
= dict(self
.rbd
.mirror_image_info_list(ioctx
))
2021 info
['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL
;
2022 eq(info
, entries
[self
.image
.id()])
2026 self
.image
.mirror_image_resync()
2027 except InvalidArgument
:
2029 eq(True, fail
) # Fails because it is primary
2031 status
= self
.image
.mirror_image_get_status()
2032 eq(image_name
, status
['name'])
2033 eq(False, status
['up'])
2034 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
2035 info
= status
['info']
2036 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
2038 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
2039 RBD_FEATURE_JOURNALING
])
2040 def test_mirror_image_status(self
):
2041 info
= self
.image
.mirror_image_get_info()
2042 global_id
= info
['global_id']
2043 state
= info
['state']
2044 primary
= info
['primary']
2046 status
= self
.image
.mirror_image_get_status()
2047 eq(image_name
, status
['name'])
2048 eq(False, status
['up'])
2049 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
2050 eq([], status
['remote_statuses'])
2051 info
= status
['info']
2052 self
.check_info(info
, global_id
, state
, primary
)
2054 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
2057 eq(image_name
, status
['name'])
2058 eq(False, status
['up'])
2059 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
2060 info
= status
['info']
2061 self
.check_info(info
, global_id
, state
)
2063 states
= self
.rbd
.mirror_image_status_summary(ioctx
)
2064 eq([(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, 1)], states
)
2066 assert_raises(ImageNotFound
, self
.image
.mirror_image_get_instance_id
)
2067 instance_ids
= list(self
.rbd
.mirror_image_instance_id_list(ioctx
))
2068 eq(0, len(instance_ids
))
2072 self
.rbd
.create(ioctx
, image_name
+ str(i
), IMG_SIZE
, IMG_ORDER
,
2073 old_format
=False, features
=int(features
))
2074 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
2075 eq(N
+ 1, len(images
))
2077 self
.rbd
.remove(ioctx
, image_name
+ str(i
))
2079 def test_mirror_image_create_snapshot(self
):
2080 assert_raises(InvalidArgument
, self
.image
.mirror_image_create_snapshot
)
2082 peer1_uuid
= self
.rbd
.mirror_peer_add(ioctx
, "cluster1", "client")
2083 peer2_uuid
= self
.rbd
.mirror_peer_add(ioctx
, "cluster2", "client")
2084 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
2085 self
.image
.mirror_image_disable(False)
2086 self
.image
.mirror_image_enable(RBD_MIRROR_IMAGE_MODE_SNAPSHOT
)
2087 mode
= self
.image
.mirror_image_get_mode()
2088 eq(RBD_MIRROR_IMAGE_MODE_SNAPSHOT
, mode
)
2090 snaps
= list(self
.image
.list_snaps())
2093 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2094 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2096 info
= self
.image
.mirror_image_get_info()
2097 eq(True, info
['primary'])
2099 self
.rbd
.mirror_image_info_list(ioctx
,
2100 RBD_MIRROR_IMAGE_MODE_SNAPSHOT
))
2101 info
['mode'] = RBD_MIRROR_IMAGE_MODE_SNAPSHOT
;
2102 eq(info
, entries
[self
.image
.id()])
2104 snap_id
= self
.image
.mirror_image_create_snapshot()
2106 snaps
= list(self
.image
.list_snaps())
2109 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2110 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2112 eq(snap
['id'], snap_id
)
2113 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2114 eq(RBD_SNAP_MIRROR_STATE_PRIMARY
, snap
['mirror']['state'])
2115 eq(sorted([peer1_uuid
, peer2_uuid
]),
2116 sorted(snap
['mirror']['mirror_peer_uuids']))
2118 eq(RBD_SNAP_NAMESPACE_TYPE_MIRROR
,
2119 self
.image
.snap_get_namespace_type(snap_id
))
2120 mirror_snap
= self
.image
.snap_get_mirror_namespace(snap_id
)
2121 eq(mirror_snap
, snap
['mirror'])
2123 self
.image
.mirror_image_demote()
2125 assert_raises(InvalidArgument
, self
.image
.mirror_image_create_snapshot
)
2127 snaps
= list(self
.image
.list_snaps())
2130 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2132 eq(snap
['id'], snap_id
)
2133 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2135 eq(snap
['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR
)
2136 eq(RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED
, snap
['mirror']['state'])
2137 eq(sorted([peer1_uuid
, peer2_uuid
]),
2138 sorted(snap
['mirror']['mirror_peer_uuids']))
2140 self
.rbd
.mirror_peer_remove(ioctx
, peer1_uuid
)
2141 self
.rbd
.mirror_peer_remove(ioctx
, peer2_uuid
)
2142 self
.image
.mirror_image_promote(False)
2144 class TestTrash(object):
2148 rados2
= Rados(conffile
='')
2151 ioctx2
= rados2
.open_ioctx(pool_name
)
2159 def test_move(self
):
2161 with
Image(ioctx
, image_name
) as image
:
2162 image_id
= image
.id()
2164 RBD().trash_move(ioctx
, image_name
, 1000)
2165 RBD().trash_remove(ioctx
, image_id
, True)
2167 def test_purge(self
):
2169 with
Image(ioctx
, image_name
) as image
:
2170 image_name1
= image_name
2171 image_id1
= image
.id()
2174 with
Image(ioctx
, image_name
) as image
:
2175 image_name2
= image_name
2176 image_id2
= image
.id()
2178 RBD().trash_move(ioctx
, image_name1
, 0)
2179 RBD().trash_move(ioctx
, image_name2
, 1000)
2180 RBD().trash_purge(ioctx
, datetime
.now())
2182 entries
= list(RBD().trash_list(ioctx
))
2183 eq([image_id2
], [x
['id'] for x
in entries
])
2184 RBD().trash_remove(ioctx
, image_id2
, True)
2186 def test_remove_denied(self
):
2188 with
Image(ioctx
, image_name
) as image
:
2189 image_id
= image
.id()
2191 RBD().trash_move(ioctx
, image_name
, 1000)
2192 assert_raises(PermissionError
, RBD().trash_remove
, ioctx
, image_id
)
2193 RBD().trash_remove(ioctx
, image_id
, True)
2195 def test_remove(self
):
2197 with
Image(ioctx
, image_name
) as image
:
2198 image_id
= image
.id()
2200 RBD().trash_move(ioctx
, image_name
, 0)
2201 RBD().trash_remove(ioctx
, image_id
)
2203 def test_remove_with_progress(self
):
2204 d
= {'received_callback': False}
2205 def progress_cb(current
, total
):
2206 d
['received_callback'] = True
2210 with
Image(ioctx
, image_name
) as image
:
2211 image_id
= image
.id()
2213 RBD().trash_move(ioctx
, image_name
, 0)
2214 RBD().trash_remove(ioctx
, image_id
, on_progress
=progress_cb
)
2215 eq(True, d
['received_callback'])
2219 with
Image(ioctx
, image_name
) as image
:
2220 image_id
= image
.id()
2222 RBD().trash_move(ioctx
, image_name
, 1000)
2224 info
= RBD().trash_get(ioctx
, image_id
)
2225 eq(image_id
, info
['id'])
2226 eq(image_name
, info
['name'])
2227 eq('USER', info
['source'])
2228 assert(info
['deferment_end_time'] > info
['deletion_time'])
2230 RBD().trash_remove(ioctx
, image_id
, True)
2232 def test_list(self
):
2234 with
Image(ioctx
, image_name
) as image
:
2235 image_id1
= image
.id()
2236 image_name1
= image_name
2237 RBD().trash_move(ioctx
, image_name
, 1000)
2240 with
Image(ioctx
, image_name
) as image
:
2241 image_id2
= image
.id()
2242 image_name2
= image_name
2243 RBD().trash_move(ioctx
, image_name
, 1000)
2245 entries
= list(RBD().trash_list(ioctx
))
2247 if e
['id'] == image_id1
:
2248 eq(e
['name'], image_name1
)
2249 elif e
['id'] == image_id2
:
2250 eq(e
['name'], image_name2
)
2253 eq(e
['source'], 'USER')
2254 assert e
['deferment_end_time'] > e
['deletion_time']
2256 RBD().trash_remove(ioctx
, image_id1
, True)
2257 RBD().trash_remove(ioctx
, image_id2
, True)
2259 def test_restore(self
):
2261 with
Image(ioctx
, image_name
) as image
:
2262 image_id
= image
.id()
2263 RBD().trash_move(ioctx
, image_name
, 1000)
2264 RBD().trash_restore(ioctx
, image_id
, image_name
)
2267 def test_create_group():
2271 def test_rename_group():
2273 if group_name
is not None:
2275 eq(["new" + group_name
], RBD().group_list(ioctx
))
2276 RBD().group_remove(ioctx
, "new" + group_name
)
2280 def test_list_groups_empty():
2281 eq([], RBD().group_list(ioctx
))
2283 @with_setup(create_group
, remove_group
)
2284 def test_list_groups():
2285 eq([group_name
], RBD().group_list(ioctx
))
2287 @with_setup(create_group
)
2288 def test_list_groups_after_removed():
2290 eq([], RBD().group_list(ioctx
))
2292 class TestGroups(object):
2298 self
.image_names
= [image_name
]
2299 self
.image
= Image(ioctx
, image_name
)
2302 snap_name
= get_temp_snap_name()
2303 self
.group
= Group(ioctx
, group_name
)
2308 for name
in self
.image_names
:
2309 RBD().remove(ioctx
, name
)
2311 def test_group_image_add(self
):
2312 self
.group
.add_image(ioctx
, image_name
)
2314 def test_group_image_list_empty(self
):
2315 eq([], list(self
.group
.list_images()))
2317 def test_group_image_list(self
):
2318 eq([], list(self
.group
.list_images()))
2319 self
.group
.add_image(ioctx
, image_name
)
2320 eq([image_name
], [img
['name'] for img
in self
.group
.list_images()])
2322 def test_group_image_list_move_to_trash(self
):
2323 eq([], list(self
.group
.list_images()))
2324 with
Image(ioctx
, image_name
) as image
:
2325 image_id
= image
.id()
2326 self
.group
.add_image(ioctx
, image_name
)
2327 eq([image_name
], [img
['name'] for img
in self
.group
.list_images()])
2328 RBD().trash_move(ioctx
, image_name
, 0)
2329 eq([], list(self
.group
.list_images()))
2330 RBD().trash_restore(ioctx
, image_id
, image_name
)
2332 def test_group_image_many_images(self
):
2333 eq([], list(self
.group
.list_images()))
2334 self
.group
.add_image(ioctx
, image_name
)
2336 for x
in range(0, 20):
2338 self
.image_names
.append(image_name
)
2339 self
.group
.add_image(ioctx
, image_name
)
2341 self
.image_names
.sort()
2342 answer
= [img
['name'] for img
in self
.group
.list_images()]
2344 eq(self
.image_names
, answer
)
2346 def test_group_image_remove(self
):
2347 eq([], list(self
.group
.list_images()))
2348 self
.group
.add_image(ioctx
, image_name
)
2349 with
Image(ioctx
, image_name
) as image
:
2350 eq(RBD_OPERATION_FEATURE_GROUP
,
2351 image
.op_features() & RBD_OPERATION_FEATURE_GROUP
)
2352 group
= image
.group()
2353 eq(group_name
, group
['name'])
2355 eq([image_name
], [img
['name'] for img
in self
.group
.list_images()])
2356 self
.group
.remove_image(ioctx
, image_name
)
2357 eq([], list(self
.group
.list_images()))
2358 with
Image(ioctx
, image_name
) as image
:
2359 eq(0, image
.op_features() & RBD_OPERATION_FEATURE_GROUP
)
2361 def test_group_snap(self
):
2363 eq([], list(self
.group
.list_snaps()))
2364 self
.group
.create_snap(snap_name
)
2365 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2367 for snap
in self
.image
.list_snaps():
2368 eq(rbd
.RBD_SNAP_NAMESPACE_TYPE_GROUP
, snap
['namespace'])
2369 info
= snap
['group']
2370 eq(group_name
, info
['group_name'])
2371 eq(snap_name
, info
['group_snap_name'])
2373 self
.group
.remove_snap(snap_name
)
2374 eq([], list(self
.group
.list_snaps()))
2376 def test_group_snap_list_many(self
):
2378 eq([], list(self
.group
.list_snaps()))
2380 for x
in range(0, 20):
2381 snap_names
.append(snap_name
)
2382 self
.group
.create_snap(snap_name
)
2383 snap_name
= get_temp_snap_name()
2386 answer
= [snap
['name'] for snap
in self
.group
.list_snaps()]
2388 eq(snap_names
, answer
)
2390 def test_group_snap_namespace(self
):
2392 eq([], list(self
.group
.list_snaps()))
2393 self
.group
.add_image(ioctx
, image_name
)
2394 self
.group
.create_snap(snap_name
)
2395 eq(1, len([snap
['name'] for snap
in self
.image
.list_snaps()]))
2396 self
.group
.remove_image(ioctx
, image_name
)
2397 self
.group
.remove_snap(snap_name
)
2398 eq([], list(self
.group
.list_snaps()))
2400 def test_group_snap_rename(self
):
2402 new_snap_name
= "new" + snap_name
2404 eq([], list(self
.group
.list_snaps()))
2405 self
.group
.create_snap(snap_name
)
2406 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2407 self
.group
.rename_snap(snap_name
, new_snap_name
)
2408 eq([new_snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2409 self
.group
.remove_snap(new_snap_name
)
2410 eq([], list(self
.group
.list_snaps()))
2412 def test_group_snap_rollback(self
):
2413 eq([], list(self
.group
.list_images()))
2414 self
.group
.add_image(ioctx
, image_name
)
2415 with
Image(ioctx
, image_name
) as image
:
2416 image
.write(b
'\0' * 256, 0)
2417 read
= image
.read(0, 256)
2418 eq(read
, b
'\0' * 256)
2421 eq([], list(self
.group
.list_snaps()))
2422 self
.group
.create_snap(snap_name
)
2423 eq([snap_name
], [snap
['name'] for snap
in self
.group
.list_snaps()])
2425 with
Image(ioctx
, image_name
) as image
:
2426 data
= rand_data(256)
2427 image
.write(data
, 0)
2428 read
= image
.read(0, 256)
2431 self
.group
.rollback_to_snap(snap_name
)
2432 with
Image(ioctx
, image_name
) as image
:
2433 read
= image
.read(0, 256)
2434 eq(read
, b
'\0' * 256)
2436 self
.group
.remove_image(ioctx
, image_name
)
2437 eq([], list(self
.group
.list_images()))
2438 self
.group
.remove_snap(snap_name
)
2439 eq([], list(self
.group
.list_snaps()))
2441 @with_setup(create_image
, remove_image
)
2444 image_name2
= get_temp_image_name()
2446 class TestMigration(object):
2448 def test_migration(self
):
2450 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2451 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2454 status
= RBD().migration_status(ioctx
, image_name
)
2455 eq(image_name
, status
['source_image_name'])
2456 eq(image_name
, status
['dest_image_name'])
2457 eq(RBD_IMAGE_MIGRATION_STATE_PREPARED
, status
['state'])
2459 RBD().migration_execute(ioctx
, image_name
)
2460 RBD().migration_commit(ioctx
, image_name
)
2463 def test_migration_with_progress(self
):
2464 d
= {'received_callback': False}
2465 def progress_cb(current
, total
):
2466 d
['received_callback'] = True
2470 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2471 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2473 RBD().migration_execute(ioctx
, image_name
, on_progress
=progress_cb
)
2474 eq(True, d
['received_callback'])
2475 d
['received_callback'] = False
2477 RBD().migration_commit(ioctx
, image_name
, on_progress
=progress_cb
)
2478 eq(True, d
['received_callback'])
2481 def test_migrate_abort(self
):
2483 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2484 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2486 RBD().migration_abort(ioctx
, image_name
)
2489 def test_migrate_abort_with_progress(self
):
2490 d
= {'received_callback': False}
2491 def progress_cb(current
, total
):
2492 d
['received_callback'] = True
2496 RBD().migration_prepare(ioctx
, image_name
, ioctx
, image_name
, features
=63,
2497 order
=23, stripe_unit
=1<<23, stripe_count
=1,
2499 RBD().migration_abort(ioctx
, image_name
, on_progress
=progress_cb
)
2500 eq(True, d
['received_callback'])