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
)
45 ioctx
.application_enable('rbd')
47 features
= os
.getenv("RBD_FEATURES")
48 features
= int(features
) if features
is not None else 61
50 def teardown_module():
54 rados
.delete_pool(pool_name
)
57 def get_temp_pool_name():
60 return "test-rbd-api-" + socket
.gethostname() + '-' + str(os
.getpid()) + \
63 def get_temp_image_name():
66 return "image" + str(image_idx
)
70 image_name
= get_temp_image_name()
71 if features
is not None:
72 RBD().create(ioctx
, image_name
, IMG_SIZE
, IMG_ORDER
, old_format
=False,
73 features
=int(features
))
75 RBD().create(ioctx
, image_name
, IMG_SIZE
, IMG_ORDER
, old_format
=True)
78 if image_name
is not None:
79 RBD().remove(ioctx
, image_name
)
81 def require_new_format():
83 def _require_new_format(*args
, **kwargs
):
87 return fn(*args
, **kwargs
)
88 return functools
.wraps(fn
)(_require_new_format
)
91 def require_features(required_features
):
93 def _require_features(*args
, **kwargs
):
97 for feature
in required_features
:
98 if feature
& features
!= feature
:
100 return fn(*args
, **kwargs
)
101 return functools
.wraps(fn
)(_require_features
)
104 def blacklist_features(blacklisted_features
):
106 def _blacklist_features(*args
, **kwargs
):
108 for feature
in blacklisted_features
:
109 if features
is not None and feature
& features
== feature
:
111 return fn(*args
, **kwargs
)
112 return functools
.wraps(fn
)(_blacklist_features
)
122 def check_default_params(format
, order
=None, features
=None, stripe_count
=None,
123 stripe_unit
=None, exception
=None):
127 for k
in ['rbd_default_format', 'rbd_default_order', 'rbd_default_features',
128 'rbd_default_stripe_count', 'rbd_default_stripe_unit']:
129 orig_vals
[k
] = rados
.conf_get(k
)
131 rados
.conf_set('rbd_default_format', str(format
))
132 if order
is not None:
133 rados
.conf_set('rbd_default_order', str(order
or 0))
134 if features
is not None:
135 rados
.conf_set('rbd_default_features', str(features
or 0))
136 if stripe_count
is not None:
137 rados
.conf_set('rbd_default_stripe_count', str(stripe_count
or 0))
138 if stripe_unit
is not None:
139 rados
.conf_set('rbd_default_stripe_unit', str(stripe_unit
or 0))
140 feature_data_pool
= 0
141 datapool
= rados
.conf_get('rbd_default_data_pool')
142 if not len(datapool
) == 0:
143 feature_data_pool
= 128
144 image_name
= get_temp_image_name()
145 if exception
is None:
146 RBD().create(ioctx
, image_name
, IMG_SIZE
)
148 with
Image(ioctx
, image_name
) as image
:
149 eq(format
== 1, image
.old_format())
151 expected_order
= int(rados
.conf_get('rbd_default_order'))
152 actual_order
= image
.stat()['order']
153 eq(expected_order
, actual_order
)
155 expected_features
= features
157 expected_features
= 0
158 elif expected_features
is None:
159 expected_features
= 61 | feature_data_pool
161 expected_features |
= feature_data_pool
162 eq(expected_features
, image
.features())
164 expected_stripe_count
= stripe_count
165 if not expected_stripe_count
or format
== 1 or \
166 features
& RBD_FEATURE_STRIPINGV2
== 0:
167 expected_stripe_count
= 1
168 eq(expected_stripe_count
, image
.stripe_count())
170 expected_stripe_unit
= stripe_unit
171 if not expected_stripe_unit
or format
== 1 or \
172 features
& RBD_FEATURE_STRIPINGV2
== 0:
173 expected_stripe_unit
= 1 << actual_order
174 eq(expected_stripe_unit
, image
.stripe_unit())
176 RBD().remove(ioctx
, image_name
)
178 assert_raises(exception
, RBD().create
, ioctx
, image_name
, IMG_SIZE
)
180 for k
, v
in orig_vals
.items():
183 def test_create_defaults():
184 # basic format 1 and 2
185 check_default_params(1)
186 check_default_params(2)
188 check_default_params(1, 0, exception
=ArgumentOutOfRange
)
189 check_default_params(2, 0, exception
=ArgumentOutOfRange
)
190 check_default_params(1, 11, exception
=ArgumentOutOfRange
)
191 check_default_params(2, 11, exception
=ArgumentOutOfRange
)
192 check_default_params(1, 65, exception
=ArgumentOutOfRange
)
193 check_default_params(2, 65, exception
=ArgumentOutOfRange
)
194 # striping and features are ignored for format 1
195 check_default_params(1, 20, 0, 1, 1)
196 check_default_params(1, 20, 3, 1, 1)
197 check_default_params(1, 20, 0, 0, 0)
198 # striping is ignored if stripingv2 is not set
199 check_default_params(2, 20, 0, 1, 1 << 20)
200 check_default_params(2, 20, RBD_FEATURE_LAYERING
, 1, 1 << 20)
201 check_default_params(2, 20, 0, 0, 0)
202 # striping with stripingv2 is fine
203 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 1, 1 << 16)
204 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 10, 1 << 20)
205 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 10, 1 << 16)
206 check_default_params(2, 20, 0, 0, 0)
207 # make sure invalid combinations of stripe unit and order are still invalid
208 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 10, 1 << 50, exception
=InvalidArgument
)
209 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 10, 100, exception
=InvalidArgument
)
210 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 0, 1, exception
=InvalidArgument
)
211 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 1, 0, exception
=InvalidArgument
)
212 # 0 stripe unit and count are still ignored
213 check_default_params(2, 22, 0, 0, 0)
215 def test_context_manager():
216 with
Rados(conffile
='') as cluster
:
217 with cluster
.open_ioctx(pool_name
) as ioctx
:
218 image_name
= get_temp_image_name()
219 RBD().create(ioctx
, image_name
, IMG_SIZE
)
220 with
Image(ioctx
, image_name
) as image
:
221 data
= rand_data(256)
223 read
= image
.read(0, 256)
224 RBD().remove(ioctx
, image_name
)
227 def test_open_read_only():
228 with
Rados(conffile
='') as cluster
:
229 with cluster
.open_ioctx(pool_name
) as ioctx
:
230 image_name
= get_temp_image_name()
231 RBD().create(ioctx
, image_name
, IMG_SIZE
)
232 data
= rand_data(256)
233 with
Image(ioctx
, image_name
) as image
:
235 image
.create_snap('snap')
236 with
Image(ioctx
, image_name
, read_only
=True) as image
:
237 read
= image
.read(0, 256)
239 assert_raises(ReadOnlyImage
, image
.write
, data
, 0)
240 assert_raises(ReadOnlyImage
, image
.create_snap
, 'test')
241 assert_raises(ReadOnlyImage
, image
.remove_snap
, 'snap')
242 assert_raises(ReadOnlyImage
, image
.rollback_to_snap
, 'snap')
243 assert_raises(ReadOnlyImage
, image
.protect_snap
, 'snap')
244 assert_raises(ReadOnlyImage
, image
.unprotect_snap
, 'snap')
245 assert_raises(ReadOnlyImage
, image
.unprotect_snap
, 'snap')
246 assert_raises(ReadOnlyImage
, image
.flatten
)
247 with
Image(ioctx
, image_name
) as image
:
248 image
.remove_snap('snap')
249 RBD().remove(ioctx
, image_name
)
254 image_name
= get_temp_image_name()
255 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
+ 'dne')
256 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
, 'snap')
258 def test_open_readonly_dne():
260 image_name
= get_temp_image_name()
261 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
+ 'dne',
263 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
, 'snap',
266 def test_remove_dne():
267 assert_raises(ImageNotFound
, remove_image
)
269 def test_list_empty():
270 eq([], RBD().list(ioctx
))
272 @with_setup(create_image
, remove_image
)
274 eq([image_name
], RBD().list(ioctx
))
276 @with_setup(create_image
, remove_image
)
279 image_name2
= get_temp_image_name()
280 rbd
.rename(ioctx
, image_name
, image_name2
)
281 eq([image_name2
], rbd
.list(ioctx
))
282 rbd
.rename(ioctx
, image_name2
, image_name
)
283 eq([image_name
], rbd
.list(ioctx
))
286 return os
.urandom(size
)
288 def check_stat(info
, size
, order
):
289 assert 'block_name_prefix' in info
290 eq(info
['size'], size
)
291 eq(info
['order'], order
)
292 eq(info
['num_objs'], size
// (1 << order
))
293 eq(info
['obj_size'], 1 << order
)
295 class TestImage(object):
300 self
.image
= Image(ioctx
, image_name
)
307 @require_new_format()
308 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
309 def test_update_features(self
):
310 features
= self
.image
.features()
311 self
.image
.update_features(RBD_FEATURE_EXCLUSIVE_LOCK
, True)
312 eq(features | RBD_FEATURE_EXCLUSIVE_LOCK
, self
.image
.features())
314 @require_features([RBD_FEATURE_STRIPINGV2
])
315 def test_create_with_params(self
):
317 image_name
= get_temp_image_name()
319 stripe_unit
= 1 << 20
321 self
.rbd
.create(ioctx
, image_name
, IMG_SIZE
, order
,
322 False, features
, stripe_unit
, stripe_count
)
323 image
= Image(ioctx
, image_name
)
325 check_stat(info
, IMG_SIZE
, order
)
326 eq(image
.features(), features
)
327 eq(image
.stripe_unit(), stripe_unit
)
328 eq(image
.stripe_count(), stripe_count
)
330 RBD().remove(ioctx
, image_name
)
332 @require_new_format()
334 assert_not_equal(b
'', self
.image
.id())
336 def test_block_name_prefix(self
):
337 assert_not_equal(b
'', self
.image
.block_name_prefix())
339 def test_create_timestamp(self
):
340 timestamp
= self
.image
.create_timestamp()
341 assert_not_equal(0, timestamp
.year
)
342 assert_not_equal(1970, timestamp
.year
)
344 def test_invalidate_cache(self
):
345 self
.image
.write(b
'abc', 0)
346 eq(b
'abc', self
.image
.read(0, 3))
347 self
.image
.invalidate_cache()
348 eq(b
'abc', self
.image
.read(0, 3))
351 info
= self
.image
.stat()
352 check_stat(info
, IMG_SIZE
, IMG_ORDER
)
354 def test_flags(self
):
355 flags
= self
.image
.flags()
358 def test_image_auto_close(self
):
359 image
= Image(ioctx
, image_name
)
361 def test_write(self
):
362 data
= rand_data(256)
363 self
.image
.write(data
, 0)
365 def test_write_with_fadvise_flags(self
):
366 data
= rand_data(256)
367 self
.image
.write(data
, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED
)
368 self
.image
.write(data
, 0, LIBRADOS_OP_FLAG_FADVISE_NOCACHE
)
371 data
= self
.image
.read(0, 20)
374 def test_read_with_fadvise_flags(self
):
375 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED
)
377 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM
)
380 def test_large_write(self
):
381 data
= rand_data(IMG_SIZE
)
382 self
.image
.write(data
, 0)
384 def test_large_read(self
):
385 data
= self
.image
.read(0, IMG_SIZE
)
386 eq(data
, b
'\0' * IMG_SIZE
)
388 def test_write_read(self
):
389 data
= rand_data(256)
391 self
.image
.write(data
, offset
)
392 read
= self
.image
.read(offset
, 256)
395 def test_read_bad_offset(self
):
396 assert_raises(InvalidArgument
, self
.image
.read
, IMG_SIZE
+ 1, IMG_SIZE
)
398 def test_resize(self
):
399 new_size
= IMG_SIZE
* 2
400 self
.image
.resize(new_size
)
401 info
= self
.image
.stat()
402 check_stat(info
, new_size
, IMG_ORDER
)
405 eq(IMG_SIZE
, self
.image
.size())
406 self
.image
.create_snap('snap1')
407 new_size
= IMG_SIZE
* 2
408 self
.image
.resize(new_size
)
409 eq(new_size
, self
.image
.size())
410 self
.image
.create_snap('snap2')
411 self
.image
.set_snap('snap2')
412 eq(new_size
, self
.image
.size())
413 self
.image
.set_snap('snap1')
414 eq(IMG_SIZE
, self
.image
.size())
415 self
.image
.set_snap(None)
416 eq(new_size
, self
.image
.size())
417 self
.image
.remove_snap('snap1')
418 self
.image
.remove_snap('snap2')
420 def test_resize_down(self
):
421 new_size
= IMG_SIZE
// 2
422 data
= rand_data(256)
423 self
.image
.write(data
, IMG_SIZE
// 2);
424 self
.image
.resize(new_size
)
425 self
.image
.resize(IMG_SIZE
)
426 read
= self
.image
.read(IMG_SIZE
// 2, 256)
427 eq(b
'\0' * 256, read
)
429 def test_resize_bytes(self
):
430 new_size
= IMG_SIZE
// 2 - 5
431 data
= rand_data(256)
432 self
.image
.write(data
, IMG_SIZE
// 2 - 10);
433 self
.image
.resize(new_size
)
434 self
.image
.resize(IMG_SIZE
)
435 read
= self
.image
.read(IMG_SIZE
// 2 - 10, 5)
437 read
= self
.image
.read(IMG_SIZE
// 2 - 5, 251)
438 eq(b
'\0' * 251, read
)
440 def _test_copy(self
, features
=None, order
=None, stripe_unit
=None,
443 data
= rand_data(256)
444 self
.image
.write(data
, 256)
445 image_name
= get_temp_image_name()
447 self
.image
.copy(ioctx
, image_name
)
449 self
.image
.copy(ioctx
, image_name
, features
)
450 elif stripe_unit
is None:
451 self
.image
.copy(ioctx
, image_name
, features
, order
)
452 elif stripe_count
is None:
453 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
)
455 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
,
457 assert_raises(ImageExists
, self
.image
.copy
, ioctx
, image_name
)
458 copy
= Image(ioctx
, image_name
)
459 copy_data
= copy
.read(256, 256)
461 self
.rbd
.remove(ioctx
, image_name
)
467 def test_copy2(self
):
468 self
._test
_copy
(self
.image
.features(), self
.image
.stat()['order'])
470 @require_features([RBD_FEATURE_STRIPINGV2
])
471 def test_copy3(self
):
473 self
._test
_copy
(features
, self
.image
.stat()['order'],
474 self
.image
.stripe_unit(), self
.image
.stripe_count())
476 def test_create_snap(self
):
478 self
.image
.create_snap('snap1')
479 read
= self
.image
.read(0, 256)
480 eq(read
, b
'\0' * 256)
481 data
= rand_data(256)
482 self
.image
.write(data
, 0)
483 read
= self
.image
.read(0, 256)
485 at_snapshot
= Image(ioctx
, image_name
, 'snap1')
486 snap_data
= at_snapshot
.read(0, 256)
488 eq(snap_data
, b
'\0' * 256)
489 self
.image
.remove_snap('snap1')
491 def test_list_snaps(self
):
492 eq([], list(self
.image
.list_snaps()))
493 self
.image
.create_snap('snap1')
494 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
495 self
.image
.create_snap('snap2')
496 eq(['snap1', 'snap2'], [snap
['name'] for snap
in self
.image
.list_snaps()])
497 self
.image
.remove_snap('snap1')
498 self
.image
.remove_snap('snap2')
500 def test_list_snaps_iterator_auto_close(self
):
501 self
.image
.create_snap('snap1')
502 self
.image
.list_snaps()
503 self
.image
.remove_snap('snap1')
505 def test_remove_snap(self
):
506 eq([], list(self
.image
.list_snaps()))
507 self
.image
.create_snap('snap1')
508 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
509 self
.image
.remove_snap('snap1')
510 eq([], list(self
.image
.list_snaps()))
512 def test_rename_snap(self
):
513 eq([], list(self
.image
.list_snaps()))
514 self
.image
.create_snap('snap1')
515 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
516 self
.image
.rename_snap("snap1", "snap1-rename")
517 eq(['snap1-rename'], [snap
['name'] for snap
in self
.image
.list_snaps()])
518 self
.image
.remove_snap('snap1-rename')
519 eq([], list(self
.image
.list_snaps()))
521 @require_features([RBD_FEATURE_LAYERING
])
522 def test_protect_snap(self
):
523 self
.image
.create_snap('snap1')
524 assert(not self
.image
.is_protected_snap('snap1'))
525 self
.image
.protect_snap('snap1')
526 assert(self
.image
.is_protected_snap('snap1'))
527 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
528 self
.image
.unprotect_snap('snap1')
529 assert(not self
.image
.is_protected_snap('snap1'))
530 self
.image
.remove_snap('snap1')
531 assert_raises(ImageNotFound
, self
.image
.unprotect_snap
, 'snap1')
532 assert_raises(ImageNotFound
, self
.image
.is_protected_snap
, 'snap1')
534 def test_snap_timestamp(self
):
535 self
.image
.create_snap('snap1')
536 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
537 for snap
in self
.image
.list_snaps():
539 time
= self
.image
.get_snap_timestamp(snap_id
)
540 assert_not_equal(b
'', time
.year
)
541 assert_not_equal(0, time
.year
)
542 assert_not_equal(time
.year
, '1970')
543 self
.image
.remove_snap('snap1')
545 def test_limit_snaps(self
):
546 self
.image
.set_snap_limit(2)
547 eq(2, self
.image
.get_snap_limit())
548 self
.image
.create_snap('snap1')
549 self
.image
.create_snap('snap2')
550 assert_raises(DiskQuotaExceeded
, self
.image
.create_snap
, 'snap3')
551 self
.image
.remove_snap_limit()
552 self
.image
.create_snap('snap3')
554 self
.image
.remove_snap('snap1')
555 self
.image
.remove_snap('snap2')
556 self
.image
.remove_snap('snap3')
558 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
559 def test_remove_with_exclusive_lock(self
):
560 assert_raises(ImageBusy
, remove_image
)
562 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
563 def test_remove_with_snap(self
):
564 self
.image
.create_snap('snap1')
565 assert_raises(ImageHasSnapshots
, remove_image
)
566 self
.image
.remove_snap('snap1')
568 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
569 def test_remove_with_watcher(self
):
570 data
= rand_data(256)
571 self
.image
.write(data
, 0)
572 assert_raises(ImageBusy
, remove_image
)
573 read
= self
.image
.read(0, 256)
576 def test_rollback_to_snap(self
):
577 self
.image
.write(b
'\0' * 256, 0)
578 self
.image
.create_snap('snap1')
579 read
= self
.image
.read(0, 256)
580 eq(read
, b
'\0' * 256)
581 data
= rand_data(256)
582 self
.image
.write(data
, 0)
583 read
= self
.image
.read(0, 256)
585 self
.image
.rollback_to_snap('snap1')
586 read
= self
.image
.read(0, 256)
587 eq(read
, b
'\0' * 256)
588 self
.image
.remove_snap('snap1')
590 def test_rollback_to_snap_sparse(self
):
591 self
.image
.create_snap('snap1')
592 read
= self
.image
.read(0, 256)
593 eq(read
, b
'\0' * 256)
594 data
= rand_data(256)
595 self
.image
.write(data
, 0)
596 read
= self
.image
.read(0, 256)
598 self
.image
.rollback_to_snap('snap1')
599 read
= self
.image
.read(0, 256)
600 eq(read
, b
'\0' * 256)
601 self
.image
.remove_snap('snap1')
603 def test_rollback_with_resize(self
):
604 read
= self
.image
.read(0, 256)
605 eq(read
, b
'\0' * 256)
606 data
= rand_data(256)
607 self
.image
.write(data
, 0)
608 self
.image
.create_snap('snap1')
609 read
= self
.image
.read(0, 256)
611 new_size
= IMG_SIZE
* 2
612 self
.image
.resize(new_size
)
613 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
614 self
.image
.write(data
, new_size
- 256)
615 self
.image
.create_snap('snap2')
616 read
= self
.image
.read(new_size
- 256, 256)
618 self
.image
.rollback_to_snap('snap1')
619 check_stat(self
.image
.stat(), IMG_SIZE
, IMG_ORDER
)
620 assert_raises(InvalidArgument
, self
.image
.read
, new_size
- 256, 256)
621 self
.image
.rollback_to_snap('snap2')
622 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
623 read
= self
.image
.read(new_size
- 256, 256)
625 self
.image
.remove_snap('snap1')
626 self
.image
.remove_snap('snap2')
628 def test_set_snap(self
):
629 self
.image
.write(b
'\0' * 256, 0)
630 self
.image
.create_snap('snap1')
631 read
= self
.image
.read(0, 256)
632 eq(read
, b
'\0' * 256)
633 data
= rand_data(256)
634 self
.image
.write(data
, 0)
635 read
= self
.image
.read(0, 256)
637 self
.image
.set_snap('snap1')
638 read
= self
.image
.read(0, 256)
639 eq(read
, b
'\0' * 256)
640 self
.image
.remove_snap('snap1')
642 def test_set_no_snap(self
):
643 self
.image
.write(b
'\0' * 256, 0)
644 self
.image
.create_snap('snap1')
645 read
= self
.image
.read(0, 256)
646 eq(read
, b
'\0' * 256)
647 data
= rand_data(256)
648 self
.image
.write(data
, 0)
649 read
= self
.image
.read(0, 256)
651 self
.image
.set_snap('snap1')
652 read
= self
.image
.read(0, 256)
653 eq(read
, b
'\0' * 256)
654 self
.image
.set_snap(None)
655 read
= self
.image
.read(0, 256)
657 self
.image
.remove_snap('snap1')
659 def test_set_snap_sparse(self
):
660 self
.image
.create_snap('snap1')
661 read
= self
.image
.read(0, 256)
662 eq(read
, b
'\0' * 256)
663 data
= rand_data(256)
664 self
.image
.write(data
, 0)
665 read
= self
.image
.read(0, 256)
667 self
.image
.set_snap('snap1')
668 read
= self
.image
.read(0, 256)
669 eq(read
, b
'\0' * 256)
670 self
.image
.remove_snap('snap1')
672 def test_many_snaps(self
):
674 for i
in range(num_snaps
):
675 self
.image
.create_snap(str(i
))
676 snaps
= sorted(self
.image
.list_snaps(),
677 key
=lambda snap
: int(snap
['name']))
678 eq(len(snaps
), num_snaps
)
679 for i
, snap
in enumerate(snaps
):
680 eq(snap
['size'], IMG_SIZE
)
681 eq(snap
['name'], str(i
))
682 for i
in range(num_snaps
):
683 self
.image
.remove_snap(str(i
))
685 def test_set_snap_deleted(self
):
686 self
.image
.write(b
'\0' * 256, 0)
687 self
.image
.create_snap('snap1')
688 read
= self
.image
.read(0, 256)
689 eq(read
, b
'\0' * 256)
690 data
= rand_data(256)
691 self
.image
.write(data
, 0)
692 read
= self
.image
.read(0, 256)
694 self
.image
.set_snap('snap1')
695 self
.image
.remove_snap('snap1')
696 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
697 self
.image
.set_snap(None)
698 read
= self
.image
.read(0, 256)
701 def test_set_snap_recreated(self
):
702 self
.image
.write(b
'\0' * 256, 0)
703 self
.image
.create_snap('snap1')
704 read
= self
.image
.read(0, 256)
705 eq(read
, b
'\0' * 256)
706 data
= rand_data(256)
707 self
.image
.write(data
, 0)
708 read
= self
.image
.read(0, 256)
710 self
.image
.set_snap('snap1')
711 self
.image
.remove_snap('snap1')
712 self
.image
.create_snap('snap1')
713 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
714 self
.image
.set_snap(None)
715 read
= self
.image
.read(0, 256)
717 self
.image
.remove_snap('snap1')
719 def test_lock_unlock(self
):
720 assert_raises(ImageNotFound
, self
.image
.unlock
, '')
721 self
.image
.lock_exclusive('')
722 assert_raises(ImageExists
, self
.image
.lock_exclusive
, '')
723 assert_raises(ImageBusy
, self
.image
.lock_exclusive
, 'test')
724 assert_raises(ImageExists
, self
.image
.lock_shared
, '', '')
725 assert_raises(ImageBusy
, self
.image
.lock_shared
, 'foo', '')
726 self
.image
.unlock('')
728 def test_list_lockers(self
):
729 eq([], self
.image
.list_lockers())
730 self
.image
.lock_exclusive('test')
731 lockers
= self
.image
.list_lockers()
732 eq(1, len(lockers
['lockers']))
733 _
, cookie
, _
= lockers
['lockers'][0]
735 eq('', lockers
['tag'])
736 assert lockers
['exclusive']
737 self
.image
.unlock('test')
738 eq([], self
.image
.list_lockers())
741 for i
in range(num_shared
):
742 self
.image
.lock_shared(str(i
), 'tag')
743 lockers
= self
.image
.list_lockers()
744 eq('tag', lockers
['tag'])
745 assert not lockers
['exclusive']
746 eq(num_shared
, len(lockers
['lockers']))
747 cookies
= sorted(map(lambda x
: x
[1], lockers
['lockers']))
748 for i
in range(num_shared
):
749 eq(str(i
), cookies
[i
])
750 self
.image
.unlock(str(i
))
751 eq([], self
.image
.list_lockers())
753 def test_diff_iterate(self
):
754 check_diff(self
.image
, 0, IMG_SIZE
, None, [])
755 self
.image
.write(b
'a' * 256, 0)
756 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 256, True)])
757 self
.image
.write(b
'b' * 256, 256)
758 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
759 self
.image
.discard(128, 256)
760 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
762 self
.image
.create_snap('snap1')
763 self
.image
.discard(0, 1 << IMG_ORDER
)
764 self
.image
.create_snap('snap2')
765 self
.image
.set_snap('snap2')
766 check_diff(self
.image
, 0, IMG_SIZE
, 'snap1', [(0, 512, False)])
767 self
.image
.remove_snap('snap1')
768 self
.image
.remove_snap('snap2')
770 def test_aio_read(self
):
771 # this is a list so that the local cb() can modify it
776 # test1: success case
777 comp
= self
.image
.aio_read(0, 20, cb
)
778 comp
.wait_for_complete_and_cb()
779 eq(retval
[0], b
'\0' * 20)
780 eq(comp
.get_return_value(), 20)
781 eq(sys
.getrefcount(comp
), 2)
785 comp
= self
.image
.aio_read(IMG_SIZE
, 20, cb
)
786 comp
.wait_for_complete_and_cb()
788 assert(comp
.get_return_value() < 0)
789 eq(sys
.getrefcount(comp
), 2)
791 def test_aio_write(self
):
794 retval
[0] = comp
.get_return_value()
796 data
= rand_data(256)
797 comp
= self
.image
.aio_write(data
, 256, cb
)
798 comp
.wait_for_complete_and_cb()
800 eq(comp
.get_return_value(), 0)
801 eq(sys
.getrefcount(comp
), 2)
802 eq(self
.image
.read(256, 256), data
)
804 def test_aio_discard(self
):
807 retval
[0] = comp
.get_return_value()
809 data
= rand_data(256)
810 self
.image
.write(data
, 0)
811 comp
= self
.image
.aio_discard(0, 256, cb
)
812 comp
.wait_for_complete_and_cb()
814 eq(comp
.get_return_value(), 0)
815 eq(sys
.getrefcount(comp
), 2)
816 eq(self
.image
.read(256, 256), b
'\0' * 256)
818 def test_aio_flush(self
):
821 retval
[0] = comp
.get_return_value()
823 comp
= self
.image
.aio_flush(cb
)
824 comp
.wait_for_complete_and_cb()
826 eq(sys
.getrefcount(comp
), 2)
828 def test_metadata(self
):
829 metadata
= list(self
.image
.metadata_list())
831 assert_raises(KeyError, self
.image
.metadata_get
, "key1")
832 self
.image
.metadata_set("key1", "value1")
833 self
.image
.metadata_set("key2", "value2")
834 value
= self
.image
.metadata_get("key1")
836 value
= self
.image
.metadata_get("key2")
838 metadata
= list(self
.image
.metadata_list())
840 self
.image
.metadata_remove("key1")
841 metadata
= list(self
.image
.metadata_list())
843 eq(metadata
[0], ("key2", "value2"))
844 self
.image
.metadata_remove("key2")
845 assert_raises(KeyError, self
.image
.metadata_remove
, "key2")
846 metadata
= list(self
.image
.metadata_list())
851 self
.image
.metadata_set("key" + str(i
), "X" * 1025)
852 metadata
= list(self
.image
.metadata_list())
855 self
.image
.metadata_remove("key" + str(i
))
856 metadata
= list(self
.image
.metadata_list())
857 eq(len(metadata
), N
- i
- 1)
859 def check_diff(image
, offset
, length
, from_snapshot
, expected
):
861 def cb(offset
, length
, exists
):
862 extents
.append((offset
, length
, exists
))
863 image
.diff_iterate(0, IMG_SIZE
, None, cb
)
864 eq(extents
, expected
)
866 class TestClone(object):
868 @require_features([RBD_FEATURE_LAYERING
])
874 self
.image
= Image(ioctx
, image_name
)
875 data
= rand_data(256)
876 self
.image
.write(data
, IMG_SIZE
// 2)
877 self
.image
.create_snap('snap1')
879 self
.image
.protect_snap('snap1')
880 self
.clone_name
= get_temp_image_name()
881 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
883 self
.clone
= Image(ioctx
, self
.clone_name
)
888 self
.rbd
.remove(ioctx
, self
.clone_name
)
889 self
.image
.unprotect_snap('snap1')
890 self
.image
.remove_snap('snap1')
894 def _test_with_params(self
, features
=None, order
=None, stripe_unit
=None,
896 self
.image
.create_snap('snap2')
897 self
.image
.protect_snap('snap2')
898 clone_name2
= get_temp_image_name()
900 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
)
902 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
904 elif stripe_unit
is None:
905 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
907 elif stripe_count
is None:
908 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
909 features
, order
, stripe_unit
)
911 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
912 features
, order
, stripe_unit
, stripe_count
)
913 self
.rbd
.remove(ioctx
, clone_name2
)
914 self
.image
.unprotect_snap('snap2')
915 self
.image
.remove_snap('snap2')
917 def test_with_params(self
):
918 self
._test
_with
_params
()
920 def test_with_params2(self
):
922 self
._test
_with
_params
(features
, self
.image
.stat()['order'])
924 @require_features([RBD_FEATURE_STRIPINGV2
])
925 def test_with_params3(self
):
927 self
._test
_with
_params
(features
, self
.image
.stat()['order'],
928 self
.image
.stripe_unit(),
929 self
.image
.stripe_count())
931 def test_unprotected(self
):
932 self
.image
.create_snap('snap2')
934 clone_name2
= get_temp_image_name()
935 assert_raises(InvalidArgument
, self
.rbd
.clone
, ioctx
, image_name
,
936 'snap2', ioctx
, clone_name2
, features
)
937 self
.image
.remove_snap('snap2')
939 def test_unprotect_with_children(self
):
941 # can't remove a snapshot that has dependent clones
942 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
944 # validate parent info of clone created by TestClone.setUp
945 (pool
, image
, snap
) = self
.clone
.parent_info()
947 eq(image
, image_name
)
949 eq(self
.image
.id(), self
.clone
.parent_id())
951 # create a new pool...
952 pool_name2
= get_temp_pool_name()
953 rados
.create_pool(pool_name2
)
954 other_ioctx
= rados
.open_ioctx(pool_name2
)
955 other_ioctx
.application_enable('rbd')
957 # ...with a clone of the same parent
958 other_clone_name
= get_temp_image_name()
959 self
.rbd
.clone(ioctx
, image_name
, 'snap1', other_ioctx
,
960 other_clone_name
, features
)
961 self
.other_clone
= Image(other_ioctx
, other_clone_name
)
962 # validate its parent info
963 (pool
, image
, snap
) = self
.other_clone
.parent_info()
965 eq(image
, image_name
)
967 eq(self
.image
.id(), self
.other_clone
.parent_id())
969 # can't unprotect snap with children
970 assert_raises(ImageBusy
, self
.image
.unprotect_snap
, 'snap1')
972 # 2 children, check that cannot remove the parent snap
973 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
975 # close and remove other pool's clone
976 self
.other_clone
.close()
977 self
.rbd
.remove(other_ioctx
, other_clone_name
)
979 # check that we cannot yet remove the parent snap
980 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
983 rados
.delete_pool(pool_name2
)
985 # unprotect, remove parent snap happen in cleanup, and should succeed
988 image_info
= self
.image
.stat()
989 clone_info
= self
.clone
.stat()
990 eq(clone_info
['size'], image_info
['size'])
991 eq(clone_info
['size'], self
.clone
.overlap())
993 def test_resize_stat(self
):
994 self
.clone
.resize(IMG_SIZE
// 2)
995 image_info
= self
.image
.stat()
996 clone_info
= self
.clone
.stat()
997 eq(clone_info
['size'], IMG_SIZE
// 2)
998 eq(image_info
['size'], IMG_SIZE
)
999 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
1001 self
.clone
.resize(IMG_SIZE
* 2)
1002 image_info
= self
.image
.stat()
1003 clone_info
= self
.clone
.stat()
1004 eq(clone_info
['size'], IMG_SIZE
* 2)
1005 eq(image_info
['size'], IMG_SIZE
)
1006 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
1008 def test_resize_io(self
):
1009 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1010 self
.image
.resize(0)
1011 self
.clone
.resize(IMG_SIZE
// 2 + 128)
1012 child_data
= self
.clone
.read(IMG_SIZE
// 2, 128)
1013 eq(child_data
, parent_data
[:128])
1014 self
.clone
.resize(IMG_SIZE
)
1015 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1016 eq(child_data
, parent_data
[:128] + (b
'\0' * 128))
1017 self
.clone
.resize(IMG_SIZE
// 2 + 1)
1018 child_data
= self
.clone
.read(IMG_SIZE
// 2, 1)
1019 eq(child_data
, parent_data
[0:1])
1020 self
.clone
.resize(0)
1021 self
.clone
.resize(IMG_SIZE
)
1022 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1023 eq(child_data
, b
'\0' * 256)
1025 def test_read(self
):
1026 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1027 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1028 eq(child_data
, parent_data
)
1030 def test_write(self
):
1031 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1032 new_data
= rand_data(256)
1033 self
.clone
.write(new_data
, IMG_SIZE
// 2 + 256)
1034 child_data
= self
.clone
.read(IMG_SIZE
// 2 + 256, 256)
1035 eq(child_data
, new_data
)
1036 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1037 eq(child_data
, parent_data
)
1038 parent_data
= self
.image
.read(IMG_SIZE
// 2 + 256, 256)
1039 eq(parent_data
, b
'\0' * 256)
1041 def check_children(self
, expected
):
1042 actual
= self
.image
.list_children()
1043 # dedup for cache pools until
1044 # http://tracker.ceph.com/issues/8187 is fixed
1045 deduped
= set([(pool_name
, image
[1]) for image
in actual
])
1046 eq(deduped
, set(expected
))
1048 def test_list_children(self
):
1051 self
.image
.set_snap('snap1')
1052 self
.check_children([(pool_name
, self
.clone_name
)])
1054 self
.rbd
.remove(ioctx
, self
.clone_name
)
1055 eq(self
.image
.list_children(), [])
1057 clone_name
= get_temp_image_name() + '_'
1058 expected_children
= []
1060 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
,
1061 clone_name
+ str(i
), features
)
1062 expected_children
.append((pool_name
, clone_name
+ str(i
)))
1063 self
.check_children(expected_children
)
1066 self
.rbd
.remove(ioctx
, clone_name
+ str(i
))
1067 expected_children
.pop(0)
1068 self
.check_children(expected_children
)
1070 eq(self
.image
.list_children(), [])
1071 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
1073 self
.check_children([(pool_name
, self
.clone_name
)])
1074 self
.clone
= Image(ioctx
, self
.clone_name
)
1076 def test_flatten_errors(self
):
1077 # test that we can't flatten a non-clone
1078 assert_raises(InvalidArgument
, self
.image
.flatten
)
1080 # test that we can't flatten a snapshot
1081 self
.clone
.create_snap('snap2')
1082 self
.clone
.set_snap('snap2')
1083 assert_raises(ReadOnlyImage
, self
.clone
.flatten
)
1084 self
.clone
.remove_snap('snap2')
1086 def check_flatten_with_order(self
, new_order
):
1089 clone_name2
= get_temp_image_name()
1090 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1091 features
, new_order
)
1092 #with Image(ioctx, 'clone2') as clone:
1093 clone2
= Image(ioctx
, clone_name2
)
1095 eq(clone2
.overlap(), 0)
1097 self
.rbd
.remove(ioctx
, clone_name2
)
1099 # flatten after resizing to non-block size
1100 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1101 features
, new_order
)
1102 with
Image(ioctx
, clone_name2
) as clone
:
1103 clone
.resize(IMG_SIZE
// 2 - 1)
1105 eq(0, clone
.overlap())
1106 self
.rbd
.remove(ioctx
, clone_name2
)
1108 # flatten after resizing to non-block size
1109 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1110 features
, new_order
)
1111 with
Image(ioctx
, clone_name2
) as clone
:
1112 clone
.resize(IMG_SIZE
// 2 + 1)
1114 eq(clone
.overlap(), 0)
1115 self
.rbd
.remove(ioctx
, clone_name2
)
1117 def test_flatten_basic(self
):
1118 self
.check_flatten_with_order(IMG_ORDER
)
1120 def test_flatten_smaller_order(self
):
1121 self
.check_flatten_with_order(IMG_ORDER
- 2)
1123 def test_flatten_larger_order(self
):
1124 self
.check_flatten_with_order(IMG_ORDER
+ 2)
1126 def test_flatten_drops_cache(self
):
1129 clone_name2
= get_temp_image_name()
1130 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1131 features
, IMG_ORDER
)
1132 with
Image(ioctx
, clone_name2
) as clone
:
1133 with
Image(ioctx
, clone_name2
) as clone2
:
1134 # cache object non-existence
1135 data
= clone
.read(IMG_SIZE
// 2, 256)
1136 clone2_data
= clone2
.read(IMG_SIZE
// 2, 256)
1137 eq(data
, clone2_data
)
1139 assert_raises(ImageNotFound
, clone
.parent_info
)
1140 assert_raises(ImageNotFound
, clone2
.parent_info
)
1141 assert_raises(ImageNotFound
, clone
.parent_id
)
1142 assert_raises(ImageNotFound
, clone2
.parent_id
)
1143 after_flatten
= clone
.read(IMG_SIZE
// 2, 256)
1144 eq(data
, after_flatten
)
1145 after_flatten
= clone2
.read(IMG_SIZE
// 2, 256)
1146 eq(data
, after_flatten
)
1147 self
.rbd
.remove(ioctx
, clone_name2
)
1149 def test_flatten_multi_level(self
):
1150 self
.clone
.create_snap('snap2')
1151 self
.clone
.protect_snap('snap2')
1152 clone_name3
= get_temp_image_name()
1153 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1155 self
.clone
.flatten()
1156 with
Image(ioctx
, clone_name3
) as clone3
:
1158 self
.clone
.unprotect_snap('snap2')
1159 self
.clone
.remove_snap('snap2')
1160 self
.rbd
.remove(ioctx
, clone_name3
)
1162 def test_resize_flatten_multi_level(self
):
1163 self
.clone
.create_snap('snap2')
1164 self
.clone
.protect_snap('snap2')
1165 clone_name3
= get_temp_image_name()
1166 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1168 self
.clone
.resize(1)
1169 orig_data
= self
.image
.read(0, 256)
1170 with
Image(ioctx
, clone_name3
) as clone3
:
1171 clone3_data
= clone3
.read(0, 256)
1172 eq(orig_data
, clone3_data
)
1173 self
.clone
.flatten()
1174 with
Image(ioctx
, clone_name3
) as clone3
:
1175 clone3_data
= clone3
.read(0, 256)
1176 eq(orig_data
, clone3_data
)
1177 self
.rbd
.remove(ioctx
, clone_name3
)
1178 self
.clone
.unprotect_snap('snap2')
1179 self
.clone
.remove_snap('snap2')
1181 class TestExclusiveLock(object):
1183 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
1186 rados2
= Rados(conffile
='')
1189 ioctx2
= rados2
.open_ioctx(pool_name
)
1199 def test_ownership(self
):
1200 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1201 image1
.write(b
'0'*256, 0)
1202 eq(image1
.is_exclusive_lock_owner(), True)
1203 eq(image2
.is_exclusive_lock_owner(), False)
1205 def test_snapshot_leadership(self
):
1206 with
Image(ioctx
, image_name
) as image
:
1207 image
.create_snap('snap')
1208 eq(image
.is_exclusive_lock_owner(), True)
1210 with
Image(ioctx
, image_name
) as image
:
1211 image
.write(b
'0'*256, 0)
1212 eq(image
.is_exclusive_lock_owner(), True)
1213 image
.set_snap('snap')
1214 eq(image
.is_exclusive_lock_owner(), False)
1215 with
Image(ioctx
, image_name
, snapshot
='snap') as image
:
1216 eq(image
.is_exclusive_lock_owner(), False)
1218 with
Image(ioctx
, image_name
) as image
:
1219 image
.remove_snap('snap')
1221 def test_read_only_leadership(self
):
1222 with
Image(ioctx
, image_name
, read_only
=True) as image
:
1223 eq(image
.is_exclusive_lock_owner(), False)
1225 def test_follower_flatten(self
):
1226 with
Image(ioctx
, image_name
) as image
:
1227 image
.create_snap('snap')
1228 image
.protect_snap('snap')
1230 RBD().clone(ioctx
, image_name
, 'snap', ioctx
, 'clone', features
)
1231 with
Image(ioctx
, 'clone') as image1
, Image(ioctx2
, 'clone') as image2
:
1232 data
= rand_data(256)
1233 image1
.write(data
, 0)
1235 assert_raises(ImageNotFound
, image1
.parent_info
)
1236 assert_raises(ImageNotFound
, image1
.parent_id
)
1240 image2
.parent_info()
1241 except ImageNotFound
:
1246 RBD().remove(ioctx
, 'clone')
1247 with
Image(ioctx
, image_name
) as image
:
1248 image
.unprotect_snap('snap')
1249 image
.remove_snap('snap')
1251 def test_follower_resize(self
):
1252 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1253 image1
.write(b
'0'*256, 0)
1254 for new_size
in [IMG_SIZE
* 2, IMG_SIZE
// 2]:
1255 image2
.resize(new_size
);
1256 eq(new_size
, image1
.size())
1258 if new_size
== image2
.size():
1261 eq(new_size
, image2
.size())
1263 def test_follower_snap_create(self
):
1264 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1265 image2
.create_snap('snap1')
1266 image1
.remove_snap('snap1')
1268 def test_follower_snap_rollback(self
):
1269 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1270 image1
.create_snap('snap')
1272 assert_raises(ReadOnlyImage
, image2
.rollback_to_snap
, 'snap')
1273 image1
.rollback_to_snap('snap')
1275 image1
.remove_snap('snap')
1277 def test_follower_discard(self
):
1279 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1280 data
= rand_data(256)
1281 image1
.write(data
, 0)
1282 image2
.discard(0, 256)
1283 eq(image1
.is_exclusive_lock_owner(), False)
1284 eq(image2
.is_exclusive_lock_owner(), True)
1285 read
= image2
.read(0, 256)
1286 if rados
.conf_get('rbd_skip_partial_discard') == 'false':
1287 eq(256 * b
'\0', read
)
1291 def test_follower_write(self
):
1292 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1293 data
= rand_data(256)
1294 image1
.write(data
, 0)
1295 image2
.write(data
, IMG_SIZE
// 2)
1296 eq(image1
.is_exclusive_lock_owner(), False)
1297 eq(image2
.is_exclusive_lock_owner(), True)
1298 for offset
in [0, IMG_SIZE
// 2]:
1299 read
= image2
.read(offset
, 256)
1301 def test_acquire_release_lock(self
):
1302 with
Image(ioctx
, image_name
) as image
:
1303 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
1304 image
.lock_release()
1306 def test_break_lock(self
):
1307 blacklist_rados
= Rados(conffile
='')
1308 blacklist_rados
.connect()
1310 blacklist_ioctx
= blacklist_rados
.open_ioctx(pool_name
)
1312 rados2
.conf_set('rbd_blacklist_on_break_lock', 'true')
1313 with
Image(ioctx2
, image_name
) as image
, \
1314 Image(blacklist_ioctx
, image_name
) as blacklist_image
:
1315 blacklist_image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
1316 assert_raises(ReadOnlyImage
, image
.lock_acquire
,
1317 RBD_LOCK_MODE_EXCLUSIVE
)
1319 lock_owners
= list(image
.lock_get_owners())
1320 eq(1, len(lock_owners
))
1321 eq(RBD_LOCK_MODE_EXCLUSIVE
, lock_owners
[0]['mode'])
1322 image
.lock_break(RBD_LOCK_MODE_EXCLUSIVE
,
1323 lock_owners
[0]['owner'])
1325 assert_raises(ConnectionShutdown
,
1326 blacklist_image
.is_exclusive_lock_owner
)
1328 blacklist_rados
.wait_for_latest_osdmap()
1329 data
= rand_data(256)
1330 assert_raises(ConnectionShutdown
,
1331 blacklist_image
.write
, data
, 0)
1333 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
1336 blacklist_image
.close()
1337 except ConnectionShutdown
:
1340 blacklist_ioctx
.close()
1342 blacklist_rados
.shutdown()
1344 class TestMirroring(object):
1347 def check_info(info
, global_id
, state
, primary
=None):
1348 eq(global_id
, info
['global_id'])
1349 eq(state
, info
['state'])
1350 if primary
is not None:
1351 eq(primary
, info
['primary'])
1355 self
.initial_mirror_mode
= self
.rbd
.mirror_mode_get(ioctx
)
1356 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
1358 self
.image
= Image(ioctx
, image_name
)
1363 self
.rbd
.mirror_mode_set(ioctx
, self
.initial_mirror_mode
)
1366 def test_mirror_peer(self
):
1367 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
1368 cluster_name
= "test_cluster"
1369 client_name
= "test_client"
1370 uuid
= self
.rbd
.mirror_peer_add(ioctx
, cluster_name
, client_name
)
1374 'cluster_name' : cluster_name
,
1375 'client_name' : client_name
,
1377 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
1378 cluster_name
= "test_cluster1"
1379 self
.rbd
.mirror_peer_set_cluster(ioctx
, uuid
, cluster_name
)
1380 client_name
= "test_client1"
1381 self
.rbd
.mirror_peer_set_client(ioctx
, uuid
, client_name
)
1384 'cluster_name' : cluster_name
,
1385 'client_name' : client_name
,
1387 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
1388 self
.rbd
.mirror_peer_remove(ioctx
, uuid
)
1389 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
1391 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
1392 RBD_FEATURE_JOURNALING
])
1393 def test_mirror_image(self
):
1395 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
1396 self
.image
.mirror_image_disable(True)
1397 info
= self
.image
.mirror_image_get_info()
1398 self
.check_info(info
, '', RBD_MIRROR_IMAGE_DISABLED
, False)
1400 self
.image
.mirror_image_enable()
1401 info
= self
.image
.mirror_image_get_info()
1402 global_id
= info
['global_id']
1403 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
1405 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
1408 self
.image
.mirror_image_disable(True)
1409 except InvalidArgument
:
1411 eq(True, fail
) # Fails because of mirror mode pool
1413 self
.image
.mirror_image_demote()
1414 info
= self
.image
.mirror_image_get_info()
1415 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, False)
1417 self
.image
.mirror_image_resync()
1419 self
.image
.mirror_image_promote(True)
1420 info
= self
.image
.mirror_image_get_info()
1421 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
1425 self
.image
.mirror_image_resync()
1426 except InvalidArgument
:
1428 eq(True, fail
) # Fails because it is primary
1430 status
= self
.image
.mirror_image_get_status()
1431 eq(image_name
, status
['name'])
1432 eq(False, status
['up'])
1433 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
1434 info
= status
['info']
1435 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
1437 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
1438 RBD_FEATURE_JOURNALING
])
1439 def test_mirror_image_status(self
):
1440 info
= self
.image
.mirror_image_get_info()
1441 global_id
= info
['global_id']
1442 state
= info
['state']
1443 primary
= info
['primary']
1445 status
= self
.image
.mirror_image_get_status()
1446 eq(image_name
, status
['name'])
1447 eq(False, status
['up'])
1448 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
1449 info
= status
['info']
1450 self
.check_info(info
, global_id
, state
, primary
)
1452 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
1455 eq(image_name
, status
['name'])
1456 eq(False, status
['up'])
1457 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
1458 info
= status
['info']
1459 self
.check_info(info
, global_id
, state
)
1461 states
= self
.rbd
.mirror_image_status_summary(ioctx
)
1462 eq([(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, 1)], states
)
1466 self
.rbd
.create(ioctx
, image_name
+ str(i
), IMG_SIZE
, IMG_ORDER
,
1467 old_format
=False, features
=int(features
))
1468 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
1469 eq(N
+ 1, len(images
))
1471 self
.rbd
.remove(ioctx
, image_name
+ str(i
))
1474 class TestTrash(object):
1478 rados2
= Rados(conffile
='')
1481 ioctx2
= rados2
.open_ioctx(pool_name
)
1489 def test_move(self
):
1491 with
Image(ioctx
, image_name
) as image
:
1492 image_id
= image
.id()
1494 RBD().trash_move(ioctx
, image_name
, 1000)
1495 RBD().trash_remove(ioctx
, image_id
, True)
1497 def test_remove_denied(self
):
1499 with
Image(ioctx
, image_name
) as image
:
1500 image_id
= image
.id()
1502 RBD().trash_move(ioctx
, image_name
, 1000)
1503 assert_raises(PermissionError
, RBD().trash_remove
, ioctx
, image_id
)
1505 def test_remove(self
):
1507 with
Image(ioctx
, image_name
) as image
:
1508 image_id
= image
.id()
1510 RBD().trash_move(ioctx
, image_name
, 0)
1511 RBD().trash_remove(ioctx
, image_id
)
1515 with
Image(ioctx
, image_name
) as image
:
1516 image_id
= image
.id()
1518 RBD().trash_move(ioctx
, image_name
, 1000)
1520 info
= RBD().trash_get(ioctx
, image_id
)
1521 eq(image_id
, info
['id'])
1522 eq(image_name
, info
['name'])
1523 eq('USER', info
['source'])
1524 assert(info
['deferment_end_time'] > info
['deletion_time'])
1526 RBD().trash_remove(ioctx
, image_id
, True)
1528 def test_list(self
):
1530 with
Image(ioctx
, image_name
) as image
:
1531 image_id1
= image
.id()
1532 image_name1
= image_name
1533 RBD().trash_move(ioctx
, image_name
, 1000)
1536 with
Image(ioctx
, image_name
) as image
:
1537 image_id2
= image
.id()
1538 image_name2
= image_name
1539 RBD().trash_move(ioctx
, image_name
, 1000)
1541 entries
= list(RBD().trash_list(ioctx
))
1543 if e
['id'] == image_id1
:
1544 eq(e
['name'], image_name1
)
1545 elif e
['id'] == image_id2
:
1546 eq(e
['name'], image_name2
)
1549 eq(e
['source'], 'USER')
1550 assert e
['deferment_end_time'] > e
['deletion_time']
1552 RBD().trash_remove(ioctx
, image_id1
, True)
1553 RBD().trash_remove(ioctx
, image_id2
, True)
1555 def test_restore(self
):
1557 with
Image(ioctx
, image_name
) as image
:
1558 image_id
= image
.id()
1559 RBD().trash_move(ioctx
, image_name
, 1000)
1560 RBD().trash_restore(ioctx
, image_id
, image_name
)