1 # vim: expandtab smarttab shiftwidth=4 softtabstop=4
8 from datetime
import datetime
9 from nose
import with_setup
, SkipTest
10 from nose
.tools
import eq_
as eq
, assert_raises
, assert_not_equal
11 from rados
import (Rados
,
12 LIBRADOS_OP_FLAG_FADVISE_DONTNEED
,
13 LIBRADOS_OP_FLAG_FADVISE_NOCACHE
,
14 LIBRADOS_OP_FLAG_FADVISE_RANDOM
)
15 from rbd
import (RBD
, Image
, ImageNotFound
, InvalidArgument
, ImageExists
,
16 ImageBusy
, ImageHasSnapshots
, ReadOnlyImage
,
17 FunctionNotSupported
, ArgumentOutOfRange
,
18 DiskQuotaExceeded
, ConnectionShutdown
, PermissionError
,
19 RBD_FEATURE_LAYERING
, RBD_FEATURE_STRIPINGV2
,
20 RBD_FEATURE_EXCLUSIVE_LOCK
, RBD_FEATURE_JOURNALING
,
21 RBD_MIRROR_MODE_DISABLED
, RBD_MIRROR_MODE_IMAGE
,
22 RBD_MIRROR_MODE_POOL
, RBD_MIRROR_IMAGE_ENABLED
,
23 RBD_MIRROR_IMAGE_DISABLED
, MIRROR_IMAGE_STATUS_STATE_UNKNOWN
,
24 RBD_LOCK_MODE_EXCLUSIVE
)
33 IMG_SIZE
= 8 << 20 # 8 MiB
34 IMG_ORDER
= 22 # 4 MiB objects
38 rados
= Rados(conffile
='')
41 pool_name
= get_temp_pool_name()
42 rados
.create_pool(pool_name
)
44 ioctx
= rados
.open_ioctx(pool_name
)
46 features
= os
.getenv("RBD_FEATURES")
47 features
= int(features
) if features
is not None else 61
49 def teardown_module():
53 rados
.delete_pool(pool_name
)
56 def get_temp_pool_name():
59 return "test-rbd-api-" + socket
.gethostname() + '-' + str(os
.getpid()) + \
62 def get_temp_image_name():
65 return "image" + str(image_idx
)
69 image_name
= get_temp_image_name()
70 if features
is not None:
71 RBD().create(ioctx
, image_name
, IMG_SIZE
, IMG_ORDER
, old_format
=False,
72 features
=int(features
))
74 RBD().create(ioctx
, image_name
, IMG_SIZE
, IMG_ORDER
, old_format
=True)
77 if image_name
is not None:
78 RBD().remove(ioctx
, image_name
)
80 def require_new_format():
82 def _require_new_format(*args
, **kwargs
):
86 return fn(*args
, **kwargs
)
87 return functools
.wraps(fn
)(_require_new_format
)
90 def require_features(required_features
):
92 def _require_features(*args
, **kwargs
):
96 for feature
in required_features
:
97 if feature
& features
!= feature
:
99 return fn(*args
, **kwargs
)
100 return functools
.wraps(fn
)(_require_features
)
103 def blacklist_features(blacklisted_features
):
105 def _blacklist_features(*args
, **kwargs
):
107 for feature
in blacklisted_features
:
108 if features
is not None and feature
& features
== feature
:
110 return fn(*args
, **kwargs
)
111 return functools
.wraps(fn
)(_blacklist_features
)
121 def check_default_params(format
, order
=None, features
=None, stripe_count
=None,
122 stripe_unit
=None, exception
=None):
126 for k
in ['rbd_default_format', 'rbd_default_order', 'rbd_default_features',
127 'rbd_default_stripe_count', 'rbd_default_stripe_unit']:
128 orig_vals
[k
] = rados
.conf_get(k
)
130 rados
.conf_set('rbd_default_format', str(format
))
131 if order
is not None:
132 rados
.conf_set('rbd_default_order', str(order
or 0))
133 if features
is not None:
134 rados
.conf_set('rbd_default_features', str(features
or 0))
135 if stripe_count
is not None:
136 rados
.conf_set('rbd_default_stripe_count', str(stripe_count
or 0))
137 if stripe_unit
is not None:
138 rados
.conf_set('rbd_default_stripe_unit', str(stripe_unit
or 0))
139 feature_data_pool
= 0
140 datapool
= rados
.conf_get('rbd_default_data_pool')
141 if not len(datapool
) == 0:
142 feature_data_pool
= 128
143 image_name
= get_temp_image_name()
144 if exception
is None:
145 RBD().create(ioctx
, image_name
, IMG_SIZE
)
147 with
Image(ioctx
, image_name
) as image
:
148 eq(format
== 1, image
.old_format())
150 expected_order
= int(rados
.conf_get('rbd_default_order'))
151 actual_order
= image
.stat()['order']
152 eq(expected_order
, actual_order
)
154 expected_features
= features
156 expected_features
= 0
157 elif expected_features
is None:
158 expected_features
= 61 | feature_data_pool
160 expected_features |
= feature_data_pool
161 eq(expected_features
, image
.features())
163 expected_stripe_count
= stripe_count
164 if not expected_stripe_count
or format
== 1 or \
165 features
& RBD_FEATURE_STRIPINGV2
== 0:
166 expected_stripe_count
= 1
167 eq(expected_stripe_count
, image
.stripe_count())
169 expected_stripe_unit
= stripe_unit
170 if not expected_stripe_unit
or format
== 1 or \
171 features
& RBD_FEATURE_STRIPINGV2
== 0:
172 expected_stripe_unit
= 1 << actual_order
173 eq(expected_stripe_unit
, image
.stripe_unit())
175 RBD().remove(ioctx
, image_name
)
177 assert_raises(exception
, RBD().create
, ioctx
, image_name
, IMG_SIZE
)
179 for k
, v
in orig_vals
.items():
182 def test_create_defaults():
183 # basic format 1 and 2
184 check_default_params(1)
185 check_default_params(2)
187 check_default_params(1, 0, exception
=ArgumentOutOfRange
)
188 check_default_params(2, 0, exception
=ArgumentOutOfRange
)
189 check_default_params(1, 11, exception
=ArgumentOutOfRange
)
190 check_default_params(2, 11, exception
=ArgumentOutOfRange
)
191 check_default_params(1, 65, exception
=ArgumentOutOfRange
)
192 check_default_params(2, 65, exception
=ArgumentOutOfRange
)
193 # striping and features are ignored for format 1
194 check_default_params(1, 20, 0, 1, 1)
195 check_default_params(1, 20, 3, 1, 1)
196 check_default_params(1, 20, 0, 0, 0)
197 # striping is ignored if stripingv2 is not set
198 check_default_params(2, 20, 0, 1, 1 << 20)
199 check_default_params(2, 20, RBD_FEATURE_LAYERING
, 1, 1 << 20)
200 check_default_params(2, 20, 0, 0, 0)
201 # striping with stripingv2 is fine
202 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 1, 1 << 16)
203 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 10, 1 << 20)
204 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 10, 1 << 16)
205 check_default_params(2, 20, 0, 0, 0)
206 # make sure invalid combinations of stripe unit and order are still invalid
207 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 10, 1 << 50, exception
=InvalidArgument
)
208 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 10, 100, exception
=InvalidArgument
)
209 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 0, 1, exception
=InvalidArgument
)
210 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 1, 0, exception
=InvalidArgument
)
211 # 0 stripe unit and count are still ignored
212 check_default_params(2, 22, 0, 0, 0)
214 def test_context_manager():
215 with
Rados(conffile
='') as cluster
:
216 with cluster
.open_ioctx(pool_name
) as ioctx
:
217 image_name
= get_temp_image_name()
218 RBD().create(ioctx
, image_name
, IMG_SIZE
)
219 with
Image(ioctx
, image_name
) as image
:
220 data
= rand_data(256)
222 read
= image
.read(0, 256)
223 RBD().remove(ioctx
, image_name
)
226 def test_open_read_only():
227 with
Rados(conffile
='') as cluster
:
228 with cluster
.open_ioctx(pool_name
) as ioctx
:
229 image_name
= get_temp_image_name()
230 RBD().create(ioctx
, image_name
, IMG_SIZE
)
231 data
= rand_data(256)
232 with
Image(ioctx
, image_name
) as image
:
234 image
.create_snap('snap')
235 with
Image(ioctx
, image_name
, read_only
=True) as image
:
236 read
= image
.read(0, 256)
238 assert_raises(ReadOnlyImage
, image
.write
, data
, 0)
239 assert_raises(ReadOnlyImage
, image
.create_snap
, 'test')
240 assert_raises(ReadOnlyImage
, image
.remove_snap
, 'snap')
241 assert_raises(ReadOnlyImage
, image
.rollback_to_snap
, 'snap')
242 assert_raises(ReadOnlyImage
, image
.protect_snap
, 'snap')
243 assert_raises(ReadOnlyImage
, image
.unprotect_snap
, 'snap')
244 assert_raises(ReadOnlyImage
, image
.unprotect_snap
, 'snap')
245 assert_raises(ReadOnlyImage
, image
.flatten
)
246 with
Image(ioctx
, image_name
) as image
:
247 image
.remove_snap('snap')
248 RBD().remove(ioctx
, image_name
)
253 image_name
= get_temp_image_name()
254 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
+ 'dne')
255 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
, 'snap')
257 def test_open_readonly_dne():
259 image_name
= get_temp_image_name()
260 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
+ 'dne',
262 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
, 'snap',
265 def test_remove_dne():
266 assert_raises(ImageNotFound
, remove_image
)
268 def test_list_empty():
269 eq([], RBD().list(ioctx
))
271 @with_setup(create_image
, remove_image
)
273 eq([image_name
], RBD().list(ioctx
))
275 @with_setup(create_image
, remove_image
)
278 image_name2
= get_temp_image_name()
279 rbd
.rename(ioctx
, image_name
, image_name2
)
280 eq([image_name2
], rbd
.list(ioctx
))
281 rbd
.rename(ioctx
, image_name2
, image_name
)
282 eq([image_name
], rbd
.list(ioctx
))
285 return os
.urandom(size
)
287 def check_stat(info
, size
, order
):
288 assert 'block_name_prefix' in info
289 eq(info
['size'], size
)
290 eq(info
['order'], order
)
291 eq(info
['num_objs'], size
// (1 << order
))
292 eq(info
['obj_size'], 1 << order
)
294 class TestImage(object):
299 self
.image
= Image(ioctx
, image_name
)
306 @require_new_format()
307 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
308 def test_update_features(self
):
309 features
= self
.image
.features()
310 self
.image
.update_features(RBD_FEATURE_EXCLUSIVE_LOCK
, True)
311 eq(features | RBD_FEATURE_EXCLUSIVE_LOCK
, self
.image
.features())
313 @require_features([RBD_FEATURE_STRIPINGV2
])
314 def test_create_with_params(self
):
316 image_name
= get_temp_image_name()
318 stripe_unit
= 1 << 20
320 self
.rbd
.create(ioctx
, image_name
, IMG_SIZE
, order
,
321 False, features
, stripe_unit
, stripe_count
)
322 image
= Image(ioctx
, image_name
)
324 check_stat(info
, IMG_SIZE
, order
)
325 eq(image
.features(), features
)
326 eq(image
.stripe_unit(), stripe_unit
)
327 eq(image
.stripe_count(), stripe_count
)
329 RBD().remove(ioctx
, image_name
)
331 @require_new_format()
333 assert_not_equal(b
'', self
.image
.id())
335 def test_block_name_prefix(self
):
336 assert_not_equal(b
'', self
.image
.block_name_prefix())
338 def test_create_timestamp(self
):
339 timestamp
= self
.image
.create_timestamp()
340 assert_not_equal(0, timestamp
.year
)
341 assert_not_equal(1970, timestamp
.year
)
343 def test_invalidate_cache(self
):
344 self
.image
.write(b
'abc', 0)
345 eq(b
'abc', self
.image
.read(0, 3))
346 self
.image
.invalidate_cache()
347 eq(b
'abc', self
.image
.read(0, 3))
350 info
= self
.image
.stat()
351 check_stat(info
, IMG_SIZE
, IMG_ORDER
)
353 def test_flags(self
):
354 flags
= self
.image
.flags()
357 def test_image_auto_close(self
):
358 image
= Image(ioctx
, image_name
)
360 def test_write(self
):
361 data
= rand_data(256)
362 self
.image
.write(data
, 0)
364 def test_write_with_fadvise_flags(self
):
365 data
= rand_data(256)
366 self
.image
.write(data
, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED
)
367 self
.image
.write(data
, 0, LIBRADOS_OP_FLAG_FADVISE_NOCACHE
)
370 data
= self
.image
.read(0, 20)
373 def test_read_with_fadvise_flags(self
):
374 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED
)
376 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM
)
379 def test_large_write(self
):
380 data
= rand_data(IMG_SIZE
)
381 self
.image
.write(data
, 0)
383 def test_large_read(self
):
384 data
= self
.image
.read(0, IMG_SIZE
)
385 eq(data
, b
'\0' * IMG_SIZE
)
387 def test_write_read(self
):
388 data
= rand_data(256)
390 self
.image
.write(data
, offset
)
391 read
= self
.image
.read(offset
, 256)
394 def test_read_bad_offset(self
):
395 assert_raises(InvalidArgument
, self
.image
.read
, IMG_SIZE
+ 1, IMG_SIZE
)
397 def test_resize(self
):
398 new_size
= IMG_SIZE
* 2
399 self
.image
.resize(new_size
)
400 info
= self
.image
.stat()
401 check_stat(info
, new_size
, IMG_ORDER
)
404 eq(IMG_SIZE
, self
.image
.size())
405 self
.image
.create_snap('snap1')
406 new_size
= IMG_SIZE
* 2
407 self
.image
.resize(new_size
)
408 eq(new_size
, self
.image
.size())
409 self
.image
.create_snap('snap2')
410 self
.image
.set_snap('snap2')
411 eq(new_size
, self
.image
.size())
412 self
.image
.set_snap('snap1')
413 eq(IMG_SIZE
, self
.image
.size())
414 self
.image
.set_snap(None)
415 eq(new_size
, self
.image
.size())
416 self
.image
.remove_snap('snap1')
417 self
.image
.remove_snap('snap2')
419 def test_resize_down(self
):
420 new_size
= IMG_SIZE
// 2
421 data
= rand_data(256)
422 self
.image
.write(data
, IMG_SIZE
// 2);
423 self
.image
.resize(new_size
)
424 self
.image
.resize(IMG_SIZE
)
425 read
= self
.image
.read(IMG_SIZE
// 2, 256)
426 eq(b
'\0' * 256, read
)
428 def test_resize_bytes(self
):
429 new_size
= IMG_SIZE
// 2 - 5
430 data
= rand_data(256)
431 self
.image
.write(data
, IMG_SIZE
// 2 - 10);
432 self
.image
.resize(new_size
)
433 self
.image
.resize(IMG_SIZE
)
434 read
= self
.image
.read(IMG_SIZE
// 2 - 10, 5)
436 read
= self
.image
.read(IMG_SIZE
// 2 - 5, 251)
437 eq(b
'\0' * 251, read
)
439 def _test_copy(self
, features
=None, order
=None, stripe_unit
=None,
442 data
= rand_data(256)
443 self
.image
.write(data
, 256)
444 image_name
= get_temp_image_name()
446 self
.image
.copy(ioctx
, image_name
)
448 self
.image
.copy(ioctx
, image_name
, features
)
449 elif stripe_unit
is None:
450 self
.image
.copy(ioctx
, image_name
, features
, order
)
451 elif stripe_count
is None:
452 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
)
454 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
,
456 assert_raises(ImageExists
, self
.image
.copy
, ioctx
, image_name
)
457 copy
= Image(ioctx
, image_name
)
458 copy_data
= copy
.read(256, 256)
460 self
.rbd
.remove(ioctx
, image_name
)
466 def test_copy2(self
):
467 self
._test
_copy
(self
.image
.features(), self
.image
.stat()['order'])
469 @require_features([RBD_FEATURE_STRIPINGV2
])
470 def test_copy3(self
):
472 self
._test
_copy
(features
, self
.image
.stat()['order'],
473 self
.image
.stripe_unit(), self
.image
.stripe_count())
475 def test_create_snap(self
):
477 self
.image
.create_snap('snap1')
478 read
= self
.image
.read(0, 256)
479 eq(read
, b
'\0' * 256)
480 data
= rand_data(256)
481 self
.image
.write(data
, 0)
482 read
= self
.image
.read(0, 256)
484 at_snapshot
= Image(ioctx
, image_name
, 'snap1')
485 snap_data
= at_snapshot
.read(0, 256)
487 eq(snap_data
, b
'\0' * 256)
488 self
.image
.remove_snap('snap1')
490 def test_list_snaps(self
):
491 eq([], list(self
.image
.list_snaps()))
492 self
.image
.create_snap('snap1')
493 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
494 self
.image
.create_snap('snap2')
495 eq(['snap1', 'snap2'], [snap
['name'] for snap
in self
.image
.list_snaps()])
496 self
.image
.remove_snap('snap1')
497 self
.image
.remove_snap('snap2')
499 def test_list_snaps_iterator_auto_close(self
):
500 self
.image
.create_snap('snap1')
501 self
.image
.list_snaps()
502 self
.image
.remove_snap('snap1')
504 def test_remove_snap(self
):
505 eq([], list(self
.image
.list_snaps()))
506 self
.image
.create_snap('snap1')
507 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
508 self
.image
.remove_snap('snap1')
509 eq([], list(self
.image
.list_snaps()))
511 def test_rename_snap(self
):
512 eq([], list(self
.image
.list_snaps()))
513 self
.image
.create_snap('snap1')
514 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
515 self
.image
.rename_snap("snap1", "snap1-rename")
516 eq(['snap1-rename'], [snap
['name'] for snap
in self
.image
.list_snaps()])
517 self
.image
.remove_snap('snap1-rename')
518 eq([], list(self
.image
.list_snaps()))
520 @require_features([RBD_FEATURE_LAYERING
])
521 def test_protect_snap(self
):
522 self
.image
.create_snap('snap1')
523 assert(not self
.image
.is_protected_snap('snap1'))
524 self
.image
.protect_snap('snap1')
525 assert(self
.image
.is_protected_snap('snap1'))
526 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
527 self
.image
.unprotect_snap('snap1')
528 assert(not self
.image
.is_protected_snap('snap1'))
529 self
.image
.remove_snap('snap1')
530 assert_raises(ImageNotFound
, self
.image
.unprotect_snap
, 'snap1')
531 assert_raises(ImageNotFound
, self
.image
.is_protected_snap
, 'snap1')
533 def test_snap_timestamp(self
):
534 self
.image
.create_snap('snap1')
535 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
536 for snap
in self
.image
.list_snaps():
538 time
= self
.image
.get_snap_timestamp(snap_id
)
539 assert_not_equal(b
'', time
.year
)
540 assert_not_equal(0, time
.year
)
541 assert_not_equal(time
.year
, '1970')
542 self
.image
.remove_snap('snap1')
544 def test_limit_snaps(self
):
545 self
.image
.set_snap_limit(2)
546 eq(2, self
.image
.get_snap_limit())
547 self
.image
.create_snap('snap1')
548 self
.image
.create_snap('snap2')
549 assert_raises(DiskQuotaExceeded
, self
.image
.create_snap
, 'snap3')
550 self
.image
.remove_snap_limit()
551 self
.image
.create_snap('snap3')
553 self
.image
.remove_snap('snap1')
554 self
.image
.remove_snap('snap2')
555 self
.image
.remove_snap('snap3')
557 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
558 def test_remove_with_exclusive_lock(self
):
559 assert_raises(ImageBusy
, remove_image
)
561 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
562 def test_remove_with_snap(self
):
563 self
.image
.create_snap('snap1')
564 assert_raises(ImageHasSnapshots
, remove_image
)
565 self
.image
.remove_snap('snap1')
567 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
568 def test_remove_with_watcher(self
):
569 data
= rand_data(256)
570 self
.image
.write(data
, 0)
571 assert_raises(ImageBusy
, remove_image
)
572 read
= self
.image
.read(0, 256)
575 def test_rollback_to_snap(self
):
576 self
.image
.write(b
'\0' * 256, 0)
577 self
.image
.create_snap('snap1')
578 read
= self
.image
.read(0, 256)
579 eq(read
, b
'\0' * 256)
580 data
= rand_data(256)
581 self
.image
.write(data
, 0)
582 read
= self
.image
.read(0, 256)
584 self
.image
.rollback_to_snap('snap1')
585 read
= self
.image
.read(0, 256)
586 eq(read
, b
'\0' * 256)
587 self
.image
.remove_snap('snap1')
589 def test_rollback_to_snap_sparse(self
):
590 self
.image
.create_snap('snap1')
591 read
= self
.image
.read(0, 256)
592 eq(read
, b
'\0' * 256)
593 data
= rand_data(256)
594 self
.image
.write(data
, 0)
595 read
= self
.image
.read(0, 256)
597 self
.image
.rollback_to_snap('snap1')
598 read
= self
.image
.read(0, 256)
599 eq(read
, b
'\0' * 256)
600 self
.image
.remove_snap('snap1')
602 def test_rollback_with_resize(self
):
603 read
= self
.image
.read(0, 256)
604 eq(read
, b
'\0' * 256)
605 data
= rand_data(256)
606 self
.image
.write(data
, 0)
607 self
.image
.create_snap('snap1')
608 read
= self
.image
.read(0, 256)
610 new_size
= IMG_SIZE
* 2
611 self
.image
.resize(new_size
)
612 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
613 self
.image
.write(data
, new_size
- 256)
614 self
.image
.create_snap('snap2')
615 read
= self
.image
.read(new_size
- 256, 256)
617 self
.image
.rollback_to_snap('snap1')
618 check_stat(self
.image
.stat(), IMG_SIZE
, IMG_ORDER
)
619 assert_raises(InvalidArgument
, self
.image
.read
, new_size
- 256, 256)
620 self
.image
.rollback_to_snap('snap2')
621 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
622 read
= self
.image
.read(new_size
- 256, 256)
624 self
.image
.remove_snap('snap1')
625 self
.image
.remove_snap('snap2')
627 def test_set_snap(self
):
628 self
.image
.write(b
'\0' * 256, 0)
629 self
.image
.create_snap('snap1')
630 read
= self
.image
.read(0, 256)
631 eq(read
, b
'\0' * 256)
632 data
= rand_data(256)
633 self
.image
.write(data
, 0)
634 read
= self
.image
.read(0, 256)
636 self
.image
.set_snap('snap1')
637 read
= self
.image
.read(0, 256)
638 eq(read
, b
'\0' * 256)
639 self
.image
.remove_snap('snap1')
641 def test_set_no_snap(self
):
642 self
.image
.write(b
'\0' * 256, 0)
643 self
.image
.create_snap('snap1')
644 read
= self
.image
.read(0, 256)
645 eq(read
, b
'\0' * 256)
646 data
= rand_data(256)
647 self
.image
.write(data
, 0)
648 read
= self
.image
.read(0, 256)
650 self
.image
.set_snap('snap1')
651 read
= self
.image
.read(0, 256)
652 eq(read
, b
'\0' * 256)
653 self
.image
.set_snap(None)
654 read
= self
.image
.read(0, 256)
656 self
.image
.remove_snap('snap1')
658 def test_set_snap_sparse(self
):
659 self
.image
.create_snap('snap1')
660 read
= self
.image
.read(0, 256)
661 eq(read
, b
'\0' * 256)
662 data
= rand_data(256)
663 self
.image
.write(data
, 0)
664 read
= self
.image
.read(0, 256)
666 self
.image
.set_snap('snap1')
667 read
= self
.image
.read(0, 256)
668 eq(read
, b
'\0' * 256)
669 self
.image
.remove_snap('snap1')
671 def test_many_snaps(self
):
673 for i
in range(num_snaps
):
674 self
.image
.create_snap(str(i
))
675 snaps
= sorted(self
.image
.list_snaps(),
676 key
=lambda snap
: int(snap
['name']))
677 eq(len(snaps
), num_snaps
)
678 for i
, snap
in enumerate(snaps
):
679 eq(snap
['size'], IMG_SIZE
)
680 eq(snap
['name'], str(i
))
681 for i
in range(num_snaps
):
682 self
.image
.remove_snap(str(i
))
684 def test_set_snap_deleted(self
):
685 self
.image
.write(b
'\0' * 256, 0)
686 self
.image
.create_snap('snap1')
687 read
= self
.image
.read(0, 256)
688 eq(read
, b
'\0' * 256)
689 data
= rand_data(256)
690 self
.image
.write(data
, 0)
691 read
= self
.image
.read(0, 256)
693 self
.image
.set_snap('snap1')
694 self
.image
.remove_snap('snap1')
695 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
696 self
.image
.set_snap(None)
697 read
= self
.image
.read(0, 256)
700 def test_set_snap_recreated(self
):
701 self
.image
.write(b
'\0' * 256, 0)
702 self
.image
.create_snap('snap1')
703 read
= self
.image
.read(0, 256)
704 eq(read
, b
'\0' * 256)
705 data
= rand_data(256)
706 self
.image
.write(data
, 0)
707 read
= self
.image
.read(0, 256)
709 self
.image
.set_snap('snap1')
710 self
.image
.remove_snap('snap1')
711 self
.image
.create_snap('snap1')
712 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
713 self
.image
.set_snap(None)
714 read
= self
.image
.read(0, 256)
716 self
.image
.remove_snap('snap1')
718 def test_lock_unlock(self
):
719 assert_raises(ImageNotFound
, self
.image
.unlock
, '')
720 self
.image
.lock_exclusive('')
721 assert_raises(ImageExists
, self
.image
.lock_exclusive
, '')
722 assert_raises(ImageBusy
, self
.image
.lock_exclusive
, 'test')
723 assert_raises(ImageExists
, self
.image
.lock_shared
, '', '')
724 assert_raises(ImageBusy
, self
.image
.lock_shared
, 'foo', '')
725 self
.image
.unlock('')
727 def test_list_lockers(self
):
728 eq([], self
.image
.list_lockers())
729 self
.image
.lock_exclusive('test')
730 lockers
= self
.image
.list_lockers()
731 eq(1, len(lockers
['lockers']))
732 _
, cookie
, _
= lockers
['lockers'][0]
734 eq('', lockers
['tag'])
735 assert lockers
['exclusive']
736 self
.image
.unlock('test')
737 eq([], self
.image
.list_lockers())
740 for i
in range(num_shared
):
741 self
.image
.lock_shared(str(i
), 'tag')
742 lockers
= self
.image
.list_lockers()
743 eq('tag', lockers
['tag'])
744 assert not lockers
['exclusive']
745 eq(num_shared
, len(lockers
['lockers']))
746 cookies
= sorted(map(lambda x
: x
[1], lockers
['lockers']))
747 for i
in range(num_shared
):
748 eq(str(i
), cookies
[i
])
749 self
.image
.unlock(str(i
))
750 eq([], self
.image
.list_lockers())
752 def test_diff_iterate(self
):
753 check_diff(self
.image
, 0, IMG_SIZE
, None, [])
754 self
.image
.write(b
'a' * 256, 0)
755 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 256, True)])
756 self
.image
.write(b
'b' * 256, 256)
757 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
758 self
.image
.discard(128, 256)
759 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
761 self
.image
.create_snap('snap1')
762 self
.image
.discard(0, 1 << IMG_ORDER
)
763 self
.image
.create_snap('snap2')
764 self
.image
.set_snap('snap2')
765 check_diff(self
.image
, 0, IMG_SIZE
, 'snap1', [(0, 512, False)])
766 self
.image
.remove_snap('snap1')
767 self
.image
.remove_snap('snap2')
769 def test_aio_read(self
):
770 # this is a list so that the local cb() can modify it
775 # test1: success case
776 comp
= self
.image
.aio_read(0, 20, cb
)
777 comp
.wait_for_complete_and_cb()
778 eq(retval
[0], b
'\0' * 20)
779 eq(comp
.get_return_value(), 20)
780 eq(sys
.getrefcount(comp
), 2)
784 comp
= self
.image
.aio_read(IMG_SIZE
, 20, cb
)
785 comp
.wait_for_complete_and_cb()
787 assert(comp
.get_return_value() < 0)
788 eq(sys
.getrefcount(comp
), 2)
790 def test_aio_write(self
):
793 retval
[0] = comp
.get_return_value()
795 data
= rand_data(256)
796 comp
= self
.image
.aio_write(data
, 256, cb
)
797 comp
.wait_for_complete_and_cb()
799 eq(comp
.get_return_value(), 0)
800 eq(sys
.getrefcount(comp
), 2)
801 eq(self
.image
.read(256, 256), data
)
803 def test_aio_discard(self
):
806 retval
[0] = comp
.get_return_value()
808 data
= rand_data(256)
809 self
.image
.write(data
, 0)
810 comp
= self
.image
.aio_discard(0, 256, cb
)
811 comp
.wait_for_complete_and_cb()
813 eq(comp
.get_return_value(), 0)
814 eq(sys
.getrefcount(comp
), 2)
815 eq(self
.image
.read(256, 256), b
'\0' * 256)
817 def test_aio_flush(self
):
820 retval
[0] = comp
.get_return_value()
822 comp
= self
.image
.aio_flush(cb
)
823 comp
.wait_for_complete_and_cb()
825 eq(sys
.getrefcount(comp
), 2)
827 def test_metadata(self
):
828 metadata
= list(self
.image
.metadata_list())
830 self
.image
.metadata_set("key1", "value1")
831 self
.image
.metadata_set("key2", "value2")
832 value
= self
.image
.metadata_get("key1")
834 value
= self
.image
.metadata_get("key2")
836 metadata
= list(self
.image
.metadata_list())
838 self
.image
.metadata_remove("key1")
839 metadata
= list(self
.image
.metadata_list())
841 eq(metadata
[0], ("key2", "value2"))
842 self
.image
.metadata_remove("key2")
843 metadata
= list(self
.image
.metadata_list())
848 self
.image
.metadata_set("key" + str(i
), "X" * 1025)
849 metadata
= list(self
.image
.metadata_list())
852 self
.image
.metadata_remove("key" + str(i
))
853 metadata
= list(self
.image
.metadata_list())
854 eq(len(metadata
), N
- i
- 1)
856 def check_diff(image
, offset
, length
, from_snapshot
, expected
):
858 def cb(offset
, length
, exists
):
859 extents
.append((offset
, length
, exists
))
860 image
.diff_iterate(0, IMG_SIZE
, None, cb
)
861 eq(extents
, expected
)
863 class TestClone(object):
865 @require_features([RBD_FEATURE_LAYERING
])
871 self
.image
= Image(ioctx
, image_name
)
872 data
= rand_data(256)
873 self
.image
.write(data
, IMG_SIZE
// 2)
874 self
.image
.create_snap('snap1')
876 self
.image
.protect_snap('snap1')
877 self
.clone_name
= get_temp_image_name()
878 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
880 self
.clone
= Image(ioctx
, self
.clone_name
)
885 self
.rbd
.remove(ioctx
, self
.clone_name
)
886 self
.image
.unprotect_snap('snap1')
887 self
.image
.remove_snap('snap1')
891 def _test_with_params(self
, features
=None, order
=None, stripe_unit
=None,
893 self
.image
.create_snap('snap2')
894 self
.image
.protect_snap('snap2')
895 clone_name2
= get_temp_image_name()
897 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
)
899 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
901 elif stripe_unit
is None:
902 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
904 elif stripe_count
is None:
905 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
906 features
, order
, stripe_unit
)
908 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
909 features
, order
, stripe_unit
, stripe_count
)
910 self
.rbd
.remove(ioctx
, clone_name2
)
911 self
.image
.unprotect_snap('snap2')
912 self
.image
.remove_snap('snap2')
914 def test_with_params(self
):
915 self
._test
_with
_params
()
917 def test_with_params2(self
):
919 self
._test
_with
_params
(features
, self
.image
.stat()['order'])
921 @require_features([RBD_FEATURE_STRIPINGV2
])
922 def test_with_params3(self
):
924 self
._test
_with
_params
(features
, self
.image
.stat()['order'],
925 self
.image
.stripe_unit(),
926 self
.image
.stripe_count())
928 def test_unprotected(self
):
929 self
.image
.create_snap('snap2')
931 clone_name2
= get_temp_image_name()
932 assert_raises(InvalidArgument
, self
.rbd
.clone
, ioctx
, image_name
,
933 'snap2', ioctx
, clone_name2
, features
)
934 self
.image
.remove_snap('snap2')
936 def test_unprotect_with_children(self
):
938 # can't remove a snapshot that has dependent clones
939 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
941 # validate parent info of clone created by TestClone.setUp
942 (pool
, image
, snap
) = self
.clone
.parent_info()
944 eq(image
, image_name
)
946 eq(self
.image
.id(), self
.clone
.parent_id())
948 # create a new pool...
949 pool_name2
= get_temp_pool_name()
950 rados
.create_pool(pool_name2
)
951 other_ioctx
= rados
.open_ioctx(pool_name2
)
953 # ...with a clone of the same parent
954 other_clone_name
= get_temp_image_name()
955 self
.rbd
.clone(ioctx
, image_name
, 'snap1', other_ioctx
,
956 other_clone_name
, features
)
957 self
.other_clone
= Image(other_ioctx
, other_clone_name
)
958 # validate its parent info
959 (pool
, image
, snap
) = self
.other_clone
.parent_info()
961 eq(image
, image_name
)
963 eq(self
.image
.id(), self
.other_clone
.parent_id())
965 # can't unprotect snap with children
966 assert_raises(ImageBusy
, self
.image
.unprotect_snap
, 'snap1')
968 # 2 children, check that cannot remove the parent snap
969 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
971 # close and remove other pool's clone
972 self
.other_clone
.close()
973 self
.rbd
.remove(other_ioctx
, other_clone_name
)
975 # check that we cannot yet remove the parent snap
976 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
979 rados
.delete_pool(pool_name2
)
981 # unprotect, remove parent snap happen in cleanup, and should succeed
984 image_info
= self
.image
.stat()
985 clone_info
= self
.clone
.stat()
986 eq(clone_info
['size'], image_info
['size'])
987 eq(clone_info
['size'], self
.clone
.overlap())
989 def test_resize_stat(self
):
990 self
.clone
.resize(IMG_SIZE
// 2)
991 image_info
= self
.image
.stat()
992 clone_info
= self
.clone
.stat()
993 eq(clone_info
['size'], IMG_SIZE
// 2)
994 eq(image_info
['size'], IMG_SIZE
)
995 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
997 self
.clone
.resize(IMG_SIZE
* 2)
998 image_info
= self
.image
.stat()
999 clone_info
= self
.clone
.stat()
1000 eq(clone_info
['size'], IMG_SIZE
* 2)
1001 eq(image_info
['size'], IMG_SIZE
)
1002 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
1004 def test_resize_io(self
):
1005 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1006 self
.image
.resize(0)
1007 self
.clone
.resize(IMG_SIZE
// 2 + 128)
1008 child_data
= self
.clone
.read(IMG_SIZE
// 2, 128)
1009 eq(child_data
, parent_data
[:128])
1010 self
.clone
.resize(IMG_SIZE
)
1011 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1012 eq(child_data
, parent_data
[:128] + (b
'\0' * 128))
1013 self
.clone
.resize(IMG_SIZE
// 2 + 1)
1014 child_data
= self
.clone
.read(IMG_SIZE
// 2, 1)
1015 eq(child_data
, parent_data
[0:1])
1016 self
.clone
.resize(0)
1017 self
.clone
.resize(IMG_SIZE
)
1018 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1019 eq(child_data
, b
'\0' * 256)
1021 def test_read(self
):
1022 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1023 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1024 eq(child_data
, parent_data
)
1026 def test_write(self
):
1027 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1028 new_data
= rand_data(256)
1029 self
.clone
.write(new_data
, IMG_SIZE
// 2 + 256)
1030 child_data
= self
.clone
.read(IMG_SIZE
// 2 + 256, 256)
1031 eq(child_data
, new_data
)
1032 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1033 eq(child_data
, parent_data
)
1034 parent_data
= self
.image
.read(IMG_SIZE
// 2 + 256, 256)
1035 eq(parent_data
, b
'\0' * 256)
1037 def check_children(self
, expected
):
1038 actual
= self
.image
.list_children()
1039 # dedup for cache pools until
1040 # http://tracker.ceph.com/issues/8187 is fixed
1041 deduped
= set([(pool_name
, image
[1]) for image
in actual
])
1042 eq(deduped
, set(expected
))
1044 def test_list_children(self
):
1047 self
.image
.set_snap('snap1')
1048 self
.check_children([(pool_name
, self
.clone_name
)])
1050 self
.rbd
.remove(ioctx
, self
.clone_name
)
1051 eq(self
.image
.list_children(), [])
1053 clone_name
= get_temp_image_name() + '_'
1054 expected_children
= []
1056 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
,
1057 clone_name
+ str(i
), features
)
1058 expected_children
.append((pool_name
, clone_name
+ str(i
)))
1059 self
.check_children(expected_children
)
1062 self
.rbd
.remove(ioctx
, clone_name
+ str(i
))
1063 expected_children
.pop(0)
1064 self
.check_children(expected_children
)
1066 eq(self
.image
.list_children(), [])
1067 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
1069 self
.check_children([(pool_name
, self
.clone_name
)])
1070 self
.clone
= Image(ioctx
, self
.clone_name
)
1072 def test_flatten_errors(self
):
1073 # test that we can't flatten a non-clone
1074 assert_raises(InvalidArgument
, self
.image
.flatten
)
1076 # test that we can't flatten a snapshot
1077 self
.clone
.create_snap('snap2')
1078 self
.clone
.set_snap('snap2')
1079 assert_raises(ReadOnlyImage
, self
.clone
.flatten
)
1080 self
.clone
.remove_snap('snap2')
1082 def check_flatten_with_order(self
, new_order
):
1085 clone_name2
= get_temp_image_name()
1086 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1087 features
, new_order
)
1088 #with Image(ioctx, 'clone2') as clone:
1089 clone2
= Image(ioctx
, clone_name2
)
1091 eq(clone2
.overlap(), 0)
1093 self
.rbd
.remove(ioctx
, clone_name2
)
1095 # flatten after resizing to non-block size
1096 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1097 features
, new_order
)
1098 with
Image(ioctx
, clone_name2
) as clone
:
1099 clone
.resize(IMG_SIZE
// 2 - 1)
1101 eq(0, clone
.overlap())
1102 self
.rbd
.remove(ioctx
, clone_name2
)
1104 # flatten after resizing to non-block size
1105 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1106 features
, new_order
)
1107 with
Image(ioctx
, clone_name2
) as clone
:
1108 clone
.resize(IMG_SIZE
// 2 + 1)
1110 eq(clone
.overlap(), 0)
1111 self
.rbd
.remove(ioctx
, clone_name2
)
1113 def test_flatten_basic(self
):
1114 self
.check_flatten_with_order(IMG_ORDER
)
1116 def test_flatten_smaller_order(self
):
1117 self
.check_flatten_with_order(IMG_ORDER
- 2)
1119 def test_flatten_larger_order(self
):
1120 self
.check_flatten_with_order(IMG_ORDER
+ 2)
1122 def test_flatten_drops_cache(self
):
1125 clone_name2
= get_temp_image_name()
1126 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1127 features
, IMG_ORDER
)
1128 with
Image(ioctx
, clone_name2
) as clone
:
1129 with
Image(ioctx
, clone_name2
) as clone2
:
1130 # cache object non-existence
1131 data
= clone
.read(IMG_SIZE
// 2, 256)
1132 clone2_data
= clone2
.read(IMG_SIZE
// 2, 256)
1133 eq(data
, clone2_data
)
1135 assert_raises(ImageNotFound
, clone
.parent_info
)
1136 assert_raises(ImageNotFound
, clone2
.parent_info
)
1137 assert_raises(ImageNotFound
, clone
.parent_id
)
1138 assert_raises(ImageNotFound
, clone2
.parent_id
)
1139 after_flatten
= clone
.read(IMG_SIZE
// 2, 256)
1140 eq(data
, after_flatten
)
1141 after_flatten
= clone2
.read(IMG_SIZE
// 2, 256)
1142 eq(data
, after_flatten
)
1143 self
.rbd
.remove(ioctx
, clone_name2
)
1145 def test_flatten_multi_level(self
):
1146 self
.clone
.create_snap('snap2')
1147 self
.clone
.protect_snap('snap2')
1148 clone_name3
= get_temp_image_name()
1149 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1151 self
.clone
.flatten()
1152 with
Image(ioctx
, clone_name3
) as clone3
:
1154 self
.clone
.unprotect_snap('snap2')
1155 self
.clone
.remove_snap('snap2')
1156 self
.rbd
.remove(ioctx
, clone_name3
)
1158 def test_resize_flatten_multi_level(self
):
1159 self
.clone
.create_snap('snap2')
1160 self
.clone
.protect_snap('snap2')
1161 clone_name3
= get_temp_image_name()
1162 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1164 self
.clone
.resize(1)
1165 orig_data
= self
.image
.read(0, 256)
1166 with
Image(ioctx
, clone_name3
) as clone3
:
1167 clone3_data
= clone3
.read(0, 256)
1168 eq(orig_data
, clone3_data
)
1169 self
.clone
.flatten()
1170 with
Image(ioctx
, clone_name3
) as clone3
:
1171 clone3_data
= clone3
.read(0, 256)
1172 eq(orig_data
, clone3_data
)
1173 self
.rbd
.remove(ioctx
, clone_name3
)
1174 self
.clone
.unprotect_snap('snap2')
1175 self
.clone
.remove_snap('snap2')
1177 class TestExclusiveLock(object):
1179 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
1182 rados2
= Rados(conffile
='')
1185 ioctx2
= rados2
.open_ioctx(pool_name
)
1195 def test_ownership(self
):
1196 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1197 image1
.write(b
'0'*256, 0)
1198 eq(image1
.is_exclusive_lock_owner(), True)
1199 eq(image2
.is_exclusive_lock_owner(), False)
1201 def test_snapshot_leadership(self
):
1202 with
Image(ioctx
, image_name
) as image
:
1203 image
.create_snap('snap')
1204 eq(image
.is_exclusive_lock_owner(), True)
1206 with
Image(ioctx
, image_name
) as image
:
1207 image
.write(b
'0'*256, 0)
1208 eq(image
.is_exclusive_lock_owner(), True)
1209 image
.set_snap('snap')
1210 eq(image
.is_exclusive_lock_owner(), False)
1211 with
Image(ioctx
, image_name
, snapshot
='snap') as image
:
1212 eq(image
.is_exclusive_lock_owner(), False)
1214 with
Image(ioctx
, image_name
) as image
:
1215 image
.remove_snap('snap')
1217 def test_read_only_leadership(self
):
1218 with
Image(ioctx
, image_name
, read_only
=True) as image
:
1219 eq(image
.is_exclusive_lock_owner(), False)
1221 def test_follower_flatten(self
):
1222 with
Image(ioctx
, image_name
) as image
:
1223 image
.create_snap('snap')
1224 image
.protect_snap('snap')
1226 RBD().clone(ioctx
, image_name
, 'snap', ioctx
, 'clone', features
)
1227 with
Image(ioctx
, 'clone') as image1
, Image(ioctx2
, 'clone') as image2
:
1228 data
= rand_data(256)
1229 image1
.write(data
, 0)
1231 assert_raises(ImageNotFound
, image1
.parent_info
)
1232 assert_raises(ImageNotFound
, image1
.parent_id
)
1236 image2
.parent_info()
1237 except ImageNotFound
:
1242 RBD().remove(ioctx
, 'clone')
1243 with
Image(ioctx
, image_name
) as image
:
1244 image
.unprotect_snap('snap')
1245 image
.remove_snap('snap')
1247 def test_follower_resize(self
):
1248 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1249 image1
.write(b
'0'*256, 0)
1250 for new_size
in [IMG_SIZE
* 2, IMG_SIZE
// 2]:
1251 image2
.resize(new_size
);
1252 eq(new_size
, image1
.size())
1254 if new_size
== image2
.size():
1257 eq(new_size
, image2
.size())
1259 def test_follower_snap_create(self
):
1260 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1261 image2
.create_snap('snap1')
1262 image1
.remove_snap('snap1')
1264 def test_follower_snap_rollback(self
):
1265 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1266 image1
.create_snap('snap')
1268 assert_raises(ReadOnlyImage
, image2
.rollback_to_snap
, 'snap')
1269 image1
.rollback_to_snap('snap')
1271 image1
.remove_snap('snap')
1273 def test_follower_discard(self
):
1275 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1276 data
= rand_data(256)
1277 image1
.write(data
, 0)
1278 image2
.discard(0, 256)
1279 eq(image1
.is_exclusive_lock_owner(), False)
1280 eq(image2
.is_exclusive_lock_owner(), True)
1281 read
= image2
.read(0, 256)
1282 if rados
.conf_get('rbd_skip_partial_discard') == 'false':
1283 eq(256 * b
'\0', read
)
1287 def test_follower_write(self
):
1288 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1289 data
= rand_data(256)
1290 image1
.write(data
, 0)
1291 image2
.write(data
, IMG_SIZE
// 2)
1292 eq(image1
.is_exclusive_lock_owner(), False)
1293 eq(image2
.is_exclusive_lock_owner(), True)
1294 for offset
in [0, IMG_SIZE
// 2]:
1295 read
= image2
.read(offset
, 256)
1297 def test_acquire_release_lock(self
):
1298 with
Image(ioctx
, image_name
) as image
:
1299 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
1300 image
.lock_release()
1302 def test_break_lock(self
):
1303 blacklist_rados
= Rados(conffile
='')
1304 blacklist_rados
.connect()
1306 blacklist_ioctx
= blacklist_rados
.open_ioctx(pool_name
)
1308 rados2
.conf_set('rbd_blacklist_on_break_lock', 'true')
1309 with
Image(ioctx2
, image_name
) as image
, \
1310 Image(blacklist_ioctx
, image_name
) as blacklist_image
:
1311 blacklist_image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
1312 assert_raises(ReadOnlyImage
, image
.lock_acquire
,
1313 RBD_LOCK_MODE_EXCLUSIVE
)
1315 lock_owners
= list(image
.lock_get_owners())
1316 eq(1, len(lock_owners
))
1317 eq(RBD_LOCK_MODE_EXCLUSIVE
, lock_owners
[0]['mode'])
1318 image
.lock_break(RBD_LOCK_MODE_EXCLUSIVE
,
1319 lock_owners
[0]['owner'])
1321 assert_raises(ConnectionShutdown
,
1322 blacklist_image
.is_exclusive_lock_owner
)
1324 blacklist_rados
.wait_for_latest_osdmap()
1325 data
= rand_data(256)
1326 assert_raises(ConnectionShutdown
,
1327 blacklist_image
.write
, data
, 0)
1329 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
1332 blacklist_image
.close()
1333 except ConnectionShutdown
:
1336 blacklist_ioctx
.close()
1338 blacklist_rados
.shutdown()
1340 class TestMirroring(object):
1343 def check_info(info
, global_id
, state
, primary
=None):
1344 eq(global_id
, info
['global_id'])
1345 eq(state
, info
['state'])
1346 if primary
is not None:
1347 eq(primary
, info
['primary'])
1351 self
.initial_mirror_mode
= self
.rbd
.mirror_mode_get(ioctx
)
1352 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
1354 self
.image
= Image(ioctx
, image_name
)
1359 self
.rbd
.mirror_mode_set(ioctx
, self
.initial_mirror_mode
)
1362 def test_mirror_peer(self
):
1363 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
1364 cluster_name
= "test_cluster"
1365 client_name
= "test_client"
1366 uuid
= self
.rbd
.mirror_peer_add(ioctx
, cluster_name
, client_name
)
1370 'cluster_name' : cluster_name
,
1371 'client_name' : client_name
,
1373 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
1374 cluster_name
= "test_cluster1"
1375 self
.rbd
.mirror_peer_set_cluster(ioctx
, uuid
, cluster_name
)
1376 client_name
= "test_client1"
1377 self
.rbd
.mirror_peer_set_client(ioctx
, uuid
, client_name
)
1380 'cluster_name' : cluster_name
,
1381 'client_name' : client_name
,
1383 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
1384 self
.rbd
.mirror_peer_remove(ioctx
, uuid
)
1385 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
1387 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
1388 RBD_FEATURE_JOURNALING
])
1389 def test_mirror_image(self
):
1391 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
1392 self
.image
.mirror_image_disable(True)
1393 info
= self
.image
.mirror_image_get_info()
1394 self
.check_info(info
, '', RBD_MIRROR_IMAGE_DISABLED
, False)
1396 self
.image
.mirror_image_enable()
1397 info
= self
.image
.mirror_image_get_info()
1398 global_id
= info
['global_id']
1399 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
1401 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
1404 self
.image
.mirror_image_disable(True)
1405 except InvalidArgument
:
1407 eq(True, fail
) # Fails because of mirror mode pool
1409 self
.image
.mirror_image_demote()
1410 info
= self
.image
.mirror_image_get_info()
1411 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, False)
1413 self
.image
.mirror_image_resync()
1415 self
.image
.mirror_image_promote(True)
1416 info
= self
.image
.mirror_image_get_info()
1417 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
1421 self
.image
.mirror_image_resync()
1422 except InvalidArgument
:
1424 eq(True, fail
) # Fails because it is primary
1426 status
= self
.image
.mirror_image_get_status()
1427 eq(image_name
, status
['name'])
1428 eq(False, status
['up'])
1429 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
1430 info
= status
['info']
1431 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
1433 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
1434 RBD_FEATURE_JOURNALING
])
1435 def test_mirror_image_status(self
):
1436 info
= self
.image
.mirror_image_get_info()
1437 global_id
= info
['global_id']
1438 state
= info
['state']
1439 primary
= info
['primary']
1441 status
= self
.image
.mirror_image_get_status()
1442 eq(image_name
, status
['name'])
1443 eq(False, status
['up'])
1444 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
1445 info
= status
['info']
1446 self
.check_info(info
, global_id
, state
, primary
)
1448 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
1451 eq(image_name
, status
['name'])
1452 eq(False, status
['up'])
1453 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
1454 info
= status
['info']
1455 self
.check_info(info
, global_id
, state
)
1457 states
= self
.rbd
.mirror_image_status_summary(ioctx
)
1458 eq([(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, 1)], states
)
1462 self
.rbd
.create(ioctx
, image_name
+ str(i
), IMG_SIZE
, IMG_ORDER
,
1463 old_format
=False, features
=int(features
))
1464 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
1465 eq(N
+ 1, len(images
))
1467 self
.rbd
.remove(ioctx
, image_name
+ str(i
))
1470 class TestTrash(object):
1474 rados2
= Rados(conffile
='')
1477 ioctx2
= rados2
.open_ioctx(pool_name
)
1485 def test_move(self
):
1487 with
Image(ioctx
, image_name
) as image
:
1488 image_id
= image
.id()
1490 RBD().trash_move(ioctx
, image_name
, 1000)
1491 RBD().trash_remove(ioctx
, image_id
, True)
1493 def test_remove_denied(self
):
1495 with
Image(ioctx
, image_name
) as image
:
1496 image_id
= image
.id()
1498 RBD().trash_move(ioctx
, image_name
, 1000)
1499 assert_raises(PermissionError
, RBD().trash_remove
, ioctx
, image_id
)
1501 def test_remove(self
):
1503 with
Image(ioctx
, image_name
) as image
:
1504 image_id
= image
.id()
1506 RBD().trash_move(ioctx
, image_name
, 0)
1507 RBD().trash_remove(ioctx
, image_id
)
1511 with
Image(ioctx
, image_name
) as image
:
1512 image_id
= image
.id()
1514 RBD().trash_move(ioctx
, image_name
, 1000)
1516 info
= RBD().trash_get(ioctx
, image_id
)
1517 eq(image_id
, info
['id'])
1518 eq(image_name
, info
['name'])
1519 eq('USER', info
['source'])
1520 assert(info
['deferment_end_time'] > info
['deletion_time'])
1522 RBD().trash_remove(ioctx
, image_id
, True)
1524 def test_list(self
):
1526 with
Image(ioctx
, image_name
) as image
:
1527 image_id1
= image
.id()
1528 image_name1
= image_name
1529 RBD().trash_move(ioctx
, image_name
, 1000)
1532 with
Image(ioctx
, image_name
) as image
:
1533 image_id2
= image
.id()
1534 image_name2
= image_name
1535 RBD().trash_move(ioctx
, image_name
, 1000)
1537 entries
= list(RBD().trash_list(ioctx
))
1539 if e
['id'] == image_id1
:
1540 eq(e
['name'], image_name1
)
1541 elif e
['id'] == image_id2
:
1542 eq(e
['name'], image_name2
)
1545 eq(e
['source'], 'USER')
1546 assert e
['deferment_end_time'] > e
['deletion_time']
1548 RBD().trash_remove(ioctx
, image_id1
, True)
1549 RBD().trash_remove(ioctx
, image_id2
, True)
1551 def test_restore(self
):
1553 with
Image(ioctx
, image_name
) as image
:
1554 image_id
= image
.id()
1555 RBD().trash_move(ioctx
, image_name
, 1000)
1556 RBD().trash_restore(ioctx
, image_id
, image_name
)