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
36 os
.environ
["RBD_FORCE_ALLOW_V1"] = "1"
40 rados
= Rados(conffile
='')
43 pool_name
= get_temp_pool_name()
44 rados
.create_pool(pool_name
)
46 ioctx
= rados
.open_ioctx(pool_name
)
47 ioctx
.application_enable('rbd')
49 features
= os
.getenv("RBD_FEATURES")
50 features
= int(features
) if features
is not None else 61
52 def teardown_module():
56 rados
.delete_pool(pool_name
)
59 def get_temp_pool_name():
62 return "test-rbd-api-" + socket
.gethostname() + '-' + str(os
.getpid()) + \
65 def get_temp_image_name():
68 return "image" + str(image_idx
)
72 image_name
= get_temp_image_name()
73 if features
is not None:
74 RBD().create(ioctx
, image_name
, IMG_SIZE
, IMG_ORDER
, old_format
=False,
75 features
=int(features
))
77 RBD().create(ioctx
, image_name
, IMG_SIZE
, IMG_ORDER
, old_format
=True)
80 if image_name
is not None:
81 RBD().remove(ioctx
, image_name
)
83 def require_new_format():
85 def _require_new_format(*args
, **kwargs
):
89 return fn(*args
, **kwargs
)
90 return functools
.wraps(fn
)(_require_new_format
)
93 def require_features(required_features
):
95 def _require_features(*args
, **kwargs
):
99 for feature
in required_features
:
100 if feature
& features
!= feature
:
102 return fn(*args
, **kwargs
)
103 return functools
.wraps(fn
)(_require_features
)
106 def blacklist_features(blacklisted_features
):
108 def _blacklist_features(*args
, **kwargs
):
110 for feature
in blacklisted_features
:
111 if features
is not None and feature
& features
== feature
:
113 return fn(*args
, **kwargs
)
114 return functools
.wraps(fn
)(_blacklist_features
)
124 def check_default_params(format
, order
=None, features
=None, stripe_count
=None,
125 stripe_unit
=None, exception
=None):
129 for k
in ['rbd_default_format', 'rbd_default_order', 'rbd_default_features',
130 'rbd_default_stripe_count', 'rbd_default_stripe_unit']:
131 orig_vals
[k
] = rados
.conf_get(k
)
133 rados
.conf_set('rbd_default_format', str(format
))
134 if order
is not None:
135 rados
.conf_set('rbd_default_order', str(order
or 0))
136 if features
is not None:
137 rados
.conf_set('rbd_default_features', str(features
or 0))
138 if stripe_count
is not None:
139 rados
.conf_set('rbd_default_stripe_count', str(stripe_count
or 0))
140 if stripe_unit
is not None:
141 rados
.conf_set('rbd_default_stripe_unit', str(stripe_unit
or 0))
142 feature_data_pool
= 0
143 datapool
= rados
.conf_get('rbd_default_data_pool')
144 if not len(datapool
) == 0:
145 feature_data_pool
= 128
146 image_name
= get_temp_image_name()
147 if exception
is None:
148 RBD().create(ioctx
, image_name
, IMG_SIZE
)
150 with
Image(ioctx
, image_name
) as image
:
151 eq(format
== 1, image
.old_format())
153 expected_order
= int(rados
.conf_get('rbd_default_order'))
154 actual_order
= image
.stat()['order']
155 eq(expected_order
, actual_order
)
157 expected_features
= features
159 expected_features
= 0
160 elif expected_features
is None:
161 expected_features
= 61 | feature_data_pool
163 expected_features |
= feature_data_pool
164 eq(expected_features
, image
.features())
166 expected_stripe_count
= stripe_count
167 if not expected_stripe_count
or format
== 1 or \
168 features
& RBD_FEATURE_STRIPINGV2
== 0:
169 expected_stripe_count
= 1
170 eq(expected_stripe_count
, image
.stripe_count())
172 expected_stripe_unit
= stripe_unit
173 if not expected_stripe_unit
or format
== 1 or \
174 features
& RBD_FEATURE_STRIPINGV2
== 0:
175 expected_stripe_unit
= 1 << actual_order
176 eq(expected_stripe_unit
, image
.stripe_unit())
178 RBD().remove(ioctx
, image_name
)
180 assert_raises(exception
, RBD().create
, ioctx
, image_name
, IMG_SIZE
)
182 for k
, v
in orig_vals
.items():
185 def test_create_defaults():
186 # basic format 1 and 2
187 check_default_params(1)
188 check_default_params(2)
190 check_default_params(1, 0, exception
=ArgumentOutOfRange
)
191 check_default_params(2, 0, exception
=ArgumentOutOfRange
)
192 check_default_params(1, 11, exception
=ArgumentOutOfRange
)
193 check_default_params(2, 11, exception
=ArgumentOutOfRange
)
194 check_default_params(1, 65, exception
=ArgumentOutOfRange
)
195 check_default_params(2, 65, exception
=ArgumentOutOfRange
)
196 # striping and features are ignored for format 1
197 check_default_params(1, 20, 0, 1, 1)
198 check_default_params(1, 20, 3, 1, 1)
199 check_default_params(1, 20, 0, 0, 0)
200 # striping is ignored if stripingv2 is not set
201 check_default_params(2, 20, 0, 1, 1 << 20)
202 check_default_params(2, 20, RBD_FEATURE_LAYERING
, 1, 1 << 20)
203 check_default_params(2, 20, 0, 0, 0)
204 # striping with stripingv2 is fine
205 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 1, 1 << 16)
206 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 10, 1 << 20)
207 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2
, 10, 1 << 16)
208 check_default_params(2, 20, 0, 0, 0)
209 # make sure invalid combinations of stripe unit and order are still invalid
210 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 10, 1 << 50, exception
=InvalidArgument
)
211 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 10, 100, exception
=InvalidArgument
)
212 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 0, 1, exception
=InvalidArgument
)
213 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2
, 1, 0, exception
=InvalidArgument
)
214 # 0 stripe unit and count are still ignored
215 check_default_params(2, 22, 0, 0, 0)
217 def test_context_manager():
218 with
Rados(conffile
='') as cluster
:
219 with cluster
.open_ioctx(pool_name
) as ioctx
:
220 image_name
= get_temp_image_name()
221 RBD().create(ioctx
, image_name
, IMG_SIZE
)
222 with
Image(ioctx
, image_name
) as image
:
223 data
= rand_data(256)
225 read
= image
.read(0, 256)
226 RBD().remove(ioctx
, image_name
)
229 def test_open_read_only():
230 with
Rados(conffile
='') as cluster
:
231 with cluster
.open_ioctx(pool_name
) as ioctx
:
232 image_name
= get_temp_image_name()
233 RBD().create(ioctx
, image_name
, IMG_SIZE
)
234 data
= rand_data(256)
235 with
Image(ioctx
, image_name
) as image
:
237 image
.create_snap('snap')
238 with
Image(ioctx
, image_name
, read_only
=True) as image
:
239 read
= image
.read(0, 256)
241 assert_raises(ReadOnlyImage
, image
.write
, data
, 0)
242 assert_raises(ReadOnlyImage
, image
.create_snap
, 'test')
243 assert_raises(ReadOnlyImage
, image
.remove_snap
, 'snap')
244 assert_raises(ReadOnlyImage
, image
.rollback_to_snap
, 'snap')
245 assert_raises(ReadOnlyImage
, image
.protect_snap
, 'snap')
246 assert_raises(ReadOnlyImage
, image
.unprotect_snap
, 'snap')
247 assert_raises(ReadOnlyImage
, image
.unprotect_snap
, 'snap')
248 assert_raises(ReadOnlyImage
, image
.flatten
)
249 with
Image(ioctx
, image_name
) as image
:
250 image
.remove_snap('snap')
251 RBD().remove(ioctx
, image_name
)
256 image_name
= get_temp_image_name()
257 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
+ 'dne')
258 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
, 'snap')
260 def test_open_readonly_dne():
262 image_name
= get_temp_image_name()
263 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
+ 'dne',
265 assert_raises(ImageNotFound
, Image
, ioctx
, image_name
, 'snap',
268 def test_remove_dne():
269 assert_raises(ImageNotFound
, remove_image
)
271 def test_list_empty():
272 eq([], RBD().list(ioctx
))
274 @with_setup(create_image
, remove_image
)
276 eq([image_name
], RBD().list(ioctx
))
278 @with_setup(create_image
, remove_image
)
281 image_name2
= get_temp_image_name()
282 rbd
.rename(ioctx
, image_name
, image_name2
)
283 eq([image_name2
], rbd
.list(ioctx
))
284 rbd
.rename(ioctx
, image_name2
, image_name
)
285 eq([image_name
], rbd
.list(ioctx
))
288 return os
.urandom(size
)
290 def check_stat(info
, size
, order
):
291 assert 'block_name_prefix' in info
292 eq(info
['size'], size
)
293 eq(info
['order'], order
)
294 eq(info
['num_objs'], size
// (1 << order
))
295 eq(info
['obj_size'], 1 << order
)
297 class TestImage(object):
302 self
.image
= Image(ioctx
, image_name
)
309 @require_new_format()
310 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
311 def test_update_features(self
):
312 features
= self
.image
.features()
313 self
.image
.update_features(RBD_FEATURE_EXCLUSIVE_LOCK
, True)
314 eq(features | RBD_FEATURE_EXCLUSIVE_LOCK
, self
.image
.features())
316 @require_features([RBD_FEATURE_STRIPINGV2
])
317 def test_create_with_params(self
):
319 image_name
= get_temp_image_name()
321 stripe_unit
= 1 << 20
323 self
.rbd
.create(ioctx
, image_name
, IMG_SIZE
, order
,
324 False, features
, stripe_unit
, stripe_count
)
325 image
= Image(ioctx
, image_name
)
327 check_stat(info
, IMG_SIZE
, order
)
328 eq(image
.features(), features
)
329 eq(image
.stripe_unit(), stripe_unit
)
330 eq(image
.stripe_count(), stripe_count
)
332 RBD().remove(ioctx
, image_name
)
334 @require_new_format()
336 assert_not_equal(b
'', self
.image
.id())
338 def test_block_name_prefix(self
):
339 assert_not_equal(b
'', self
.image
.block_name_prefix())
341 def test_create_timestamp(self
):
342 timestamp
= self
.image
.create_timestamp()
343 assert_not_equal(0, timestamp
.year
)
344 assert_not_equal(1970, timestamp
.year
)
346 def test_invalidate_cache(self
):
347 self
.image
.write(b
'abc', 0)
348 eq(b
'abc', self
.image
.read(0, 3))
349 self
.image
.invalidate_cache()
350 eq(b
'abc', self
.image
.read(0, 3))
353 info
= self
.image
.stat()
354 check_stat(info
, IMG_SIZE
, IMG_ORDER
)
356 def test_flags(self
):
357 flags
= self
.image
.flags()
360 def test_image_auto_close(self
):
361 image
= Image(ioctx
, image_name
)
363 def test_write(self
):
364 data
= rand_data(256)
365 self
.image
.write(data
, 0)
367 def test_write_with_fadvise_flags(self
):
368 data
= rand_data(256)
369 self
.image
.write(data
, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED
)
370 self
.image
.write(data
, 0, LIBRADOS_OP_FLAG_FADVISE_NOCACHE
)
373 data
= self
.image
.read(0, 20)
376 def test_read_with_fadvise_flags(self
):
377 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED
)
379 data
= self
.image
.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM
)
382 def test_large_write(self
):
383 data
= rand_data(IMG_SIZE
)
384 self
.image
.write(data
, 0)
386 def test_large_read(self
):
387 data
= self
.image
.read(0, IMG_SIZE
)
388 eq(data
, b
'\0' * IMG_SIZE
)
390 def test_write_read(self
):
391 data
= rand_data(256)
393 self
.image
.write(data
, offset
)
394 read
= self
.image
.read(offset
, 256)
397 def test_read_bad_offset(self
):
398 assert_raises(InvalidArgument
, self
.image
.read
, IMG_SIZE
+ 1, IMG_SIZE
)
400 def test_resize(self
):
401 new_size
= IMG_SIZE
* 2
402 self
.image
.resize(new_size
)
403 info
= self
.image
.stat()
404 check_stat(info
, new_size
, IMG_ORDER
)
407 eq(IMG_SIZE
, self
.image
.size())
408 self
.image
.create_snap('snap1')
409 new_size
= IMG_SIZE
* 2
410 self
.image
.resize(new_size
)
411 eq(new_size
, self
.image
.size())
412 self
.image
.create_snap('snap2')
413 self
.image
.set_snap('snap2')
414 eq(new_size
, self
.image
.size())
415 self
.image
.set_snap('snap1')
416 eq(IMG_SIZE
, self
.image
.size())
417 self
.image
.set_snap(None)
418 eq(new_size
, self
.image
.size())
419 self
.image
.remove_snap('snap1')
420 self
.image
.remove_snap('snap2')
422 def test_resize_down(self
):
423 new_size
= IMG_SIZE
// 2
424 data
= rand_data(256)
425 self
.image
.write(data
, IMG_SIZE
// 2);
426 self
.image
.resize(new_size
)
427 self
.image
.resize(IMG_SIZE
)
428 read
= self
.image
.read(IMG_SIZE
// 2, 256)
429 eq(b
'\0' * 256, read
)
431 def test_resize_bytes(self
):
432 new_size
= IMG_SIZE
// 2 - 5
433 data
= rand_data(256)
434 self
.image
.write(data
, IMG_SIZE
// 2 - 10);
435 self
.image
.resize(new_size
)
436 self
.image
.resize(IMG_SIZE
)
437 read
= self
.image
.read(IMG_SIZE
// 2 - 10, 5)
439 read
= self
.image
.read(IMG_SIZE
// 2 - 5, 251)
440 eq(b
'\0' * 251, read
)
442 def _test_copy(self
, features
=None, order
=None, stripe_unit
=None,
445 data
= rand_data(256)
446 self
.image
.write(data
, 256)
447 image_name
= get_temp_image_name()
449 self
.image
.copy(ioctx
, image_name
)
451 self
.image
.copy(ioctx
, image_name
, features
)
452 elif stripe_unit
is None:
453 self
.image
.copy(ioctx
, image_name
, features
, order
)
454 elif stripe_count
is None:
455 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
)
457 self
.image
.copy(ioctx
, image_name
, features
, order
, stripe_unit
,
459 assert_raises(ImageExists
, self
.image
.copy
, ioctx
, image_name
)
460 copy
= Image(ioctx
, image_name
)
461 copy_data
= copy
.read(256, 256)
463 self
.rbd
.remove(ioctx
, image_name
)
469 def test_copy2(self
):
470 self
._test
_copy
(self
.image
.features(), self
.image
.stat()['order'])
472 @require_features([RBD_FEATURE_STRIPINGV2
])
473 def test_copy3(self
):
475 self
._test
_copy
(features
, self
.image
.stat()['order'],
476 self
.image
.stripe_unit(), self
.image
.stripe_count())
478 def test_create_snap(self
):
480 self
.image
.create_snap('snap1')
481 read
= self
.image
.read(0, 256)
482 eq(read
, b
'\0' * 256)
483 data
= rand_data(256)
484 self
.image
.write(data
, 0)
485 read
= self
.image
.read(0, 256)
487 at_snapshot
= Image(ioctx
, image_name
, 'snap1')
488 snap_data
= at_snapshot
.read(0, 256)
490 eq(snap_data
, b
'\0' * 256)
491 self
.image
.remove_snap('snap1')
493 def test_list_snaps(self
):
494 eq([], list(self
.image
.list_snaps()))
495 self
.image
.create_snap('snap1')
496 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
497 self
.image
.create_snap('snap2')
498 eq(['snap1', 'snap2'], [snap
['name'] for snap
in self
.image
.list_snaps()])
499 self
.image
.remove_snap('snap1')
500 self
.image
.remove_snap('snap2')
502 def test_list_snaps_iterator_auto_close(self
):
503 self
.image
.create_snap('snap1')
504 self
.image
.list_snaps()
505 self
.image
.remove_snap('snap1')
507 def test_remove_snap(self
):
508 eq([], list(self
.image
.list_snaps()))
509 self
.image
.create_snap('snap1')
510 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
511 self
.image
.remove_snap('snap1')
512 eq([], list(self
.image
.list_snaps()))
514 def test_rename_snap(self
):
515 eq([], list(self
.image
.list_snaps()))
516 self
.image
.create_snap('snap1')
517 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
518 self
.image
.rename_snap("snap1", "snap1-rename")
519 eq(['snap1-rename'], [snap
['name'] for snap
in self
.image
.list_snaps()])
520 self
.image
.remove_snap('snap1-rename')
521 eq([], list(self
.image
.list_snaps()))
523 @require_features([RBD_FEATURE_LAYERING
])
524 def test_protect_snap(self
):
525 self
.image
.create_snap('snap1')
526 assert(not self
.image
.is_protected_snap('snap1'))
527 self
.image
.protect_snap('snap1')
528 assert(self
.image
.is_protected_snap('snap1'))
529 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
530 self
.image
.unprotect_snap('snap1')
531 assert(not self
.image
.is_protected_snap('snap1'))
532 self
.image
.remove_snap('snap1')
533 assert_raises(ImageNotFound
, self
.image
.unprotect_snap
, 'snap1')
534 assert_raises(ImageNotFound
, self
.image
.is_protected_snap
, 'snap1')
536 def test_snap_timestamp(self
):
537 self
.image
.create_snap('snap1')
538 eq(['snap1'], [snap
['name'] for snap
in self
.image
.list_snaps()])
539 for snap
in self
.image
.list_snaps():
541 time
= self
.image
.get_snap_timestamp(snap_id
)
542 assert_not_equal(b
'', time
.year
)
543 assert_not_equal(0, time
.year
)
544 assert_not_equal(time
.year
, '1970')
545 self
.image
.remove_snap('snap1')
547 def test_limit_snaps(self
):
548 self
.image
.set_snap_limit(2)
549 eq(2, self
.image
.get_snap_limit())
550 self
.image
.create_snap('snap1')
551 self
.image
.create_snap('snap2')
552 assert_raises(DiskQuotaExceeded
, self
.image
.create_snap
, 'snap3')
553 self
.image
.remove_snap_limit()
554 self
.image
.create_snap('snap3')
556 self
.image
.remove_snap('snap1')
557 self
.image
.remove_snap('snap2')
558 self
.image
.remove_snap('snap3')
560 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
561 def test_remove_with_exclusive_lock(self
):
562 assert_raises(ImageBusy
, remove_image
)
564 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
565 def test_remove_with_snap(self
):
566 self
.image
.create_snap('snap1')
567 assert_raises(ImageHasSnapshots
, remove_image
)
568 self
.image
.remove_snap('snap1')
570 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
571 def test_remove_with_watcher(self
):
572 data
= rand_data(256)
573 self
.image
.write(data
, 0)
574 assert_raises(ImageBusy
, remove_image
)
575 read
= self
.image
.read(0, 256)
578 def test_rollback_to_snap(self
):
579 self
.image
.write(b
'\0' * 256, 0)
580 self
.image
.create_snap('snap1')
581 read
= self
.image
.read(0, 256)
582 eq(read
, b
'\0' * 256)
583 data
= rand_data(256)
584 self
.image
.write(data
, 0)
585 read
= self
.image
.read(0, 256)
587 self
.image
.rollback_to_snap('snap1')
588 read
= self
.image
.read(0, 256)
589 eq(read
, b
'\0' * 256)
590 self
.image
.remove_snap('snap1')
592 def test_rollback_to_snap_sparse(self
):
593 self
.image
.create_snap('snap1')
594 read
= self
.image
.read(0, 256)
595 eq(read
, b
'\0' * 256)
596 data
= rand_data(256)
597 self
.image
.write(data
, 0)
598 read
= self
.image
.read(0, 256)
600 self
.image
.rollback_to_snap('snap1')
601 read
= self
.image
.read(0, 256)
602 eq(read
, b
'\0' * 256)
603 self
.image
.remove_snap('snap1')
605 def test_rollback_with_resize(self
):
606 read
= self
.image
.read(0, 256)
607 eq(read
, b
'\0' * 256)
608 data
= rand_data(256)
609 self
.image
.write(data
, 0)
610 self
.image
.create_snap('snap1')
611 read
= self
.image
.read(0, 256)
613 new_size
= IMG_SIZE
* 2
614 self
.image
.resize(new_size
)
615 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
616 self
.image
.write(data
, new_size
- 256)
617 self
.image
.create_snap('snap2')
618 read
= self
.image
.read(new_size
- 256, 256)
620 self
.image
.rollback_to_snap('snap1')
621 check_stat(self
.image
.stat(), IMG_SIZE
, IMG_ORDER
)
622 assert_raises(InvalidArgument
, self
.image
.read
, new_size
- 256, 256)
623 self
.image
.rollback_to_snap('snap2')
624 check_stat(self
.image
.stat(), new_size
, IMG_ORDER
)
625 read
= self
.image
.read(new_size
- 256, 256)
627 self
.image
.remove_snap('snap1')
628 self
.image
.remove_snap('snap2')
630 def test_set_snap(self
):
631 self
.image
.write(b
'\0' * 256, 0)
632 self
.image
.create_snap('snap1')
633 read
= self
.image
.read(0, 256)
634 eq(read
, b
'\0' * 256)
635 data
= rand_data(256)
636 self
.image
.write(data
, 0)
637 read
= self
.image
.read(0, 256)
639 self
.image
.set_snap('snap1')
640 read
= self
.image
.read(0, 256)
641 eq(read
, b
'\0' * 256)
642 self
.image
.remove_snap('snap1')
644 def test_set_no_snap(self
):
645 self
.image
.write(b
'\0' * 256, 0)
646 self
.image
.create_snap('snap1')
647 read
= self
.image
.read(0, 256)
648 eq(read
, b
'\0' * 256)
649 data
= rand_data(256)
650 self
.image
.write(data
, 0)
651 read
= self
.image
.read(0, 256)
653 self
.image
.set_snap('snap1')
654 read
= self
.image
.read(0, 256)
655 eq(read
, b
'\0' * 256)
656 self
.image
.set_snap(None)
657 read
= self
.image
.read(0, 256)
659 self
.image
.remove_snap('snap1')
661 def test_set_snap_sparse(self
):
662 self
.image
.create_snap('snap1')
663 read
= self
.image
.read(0, 256)
664 eq(read
, b
'\0' * 256)
665 data
= rand_data(256)
666 self
.image
.write(data
, 0)
667 read
= self
.image
.read(0, 256)
669 self
.image
.set_snap('snap1')
670 read
= self
.image
.read(0, 256)
671 eq(read
, b
'\0' * 256)
672 self
.image
.remove_snap('snap1')
674 def test_many_snaps(self
):
676 for i
in range(num_snaps
):
677 self
.image
.create_snap(str(i
))
678 snaps
= sorted(self
.image
.list_snaps(),
679 key
=lambda snap
: int(snap
['name']))
680 eq(len(snaps
), num_snaps
)
681 for i
, snap
in enumerate(snaps
):
682 eq(snap
['size'], IMG_SIZE
)
683 eq(snap
['name'], str(i
))
684 for i
in range(num_snaps
):
685 self
.image
.remove_snap(str(i
))
687 def test_set_snap_deleted(self
):
688 self
.image
.write(b
'\0' * 256, 0)
689 self
.image
.create_snap('snap1')
690 read
= self
.image
.read(0, 256)
691 eq(read
, b
'\0' * 256)
692 data
= rand_data(256)
693 self
.image
.write(data
, 0)
694 read
= self
.image
.read(0, 256)
696 self
.image
.set_snap('snap1')
697 self
.image
.remove_snap('snap1')
698 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
699 self
.image
.set_snap(None)
700 read
= self
.image
.read(0, 256)
703 def test_set_snap_recreated(self
):
704 self
.image
.write(b
'\0' * 256, 0)
705 self
.image
.create_snap('snap1')
706 read
= self
.image
.read(0, 256)
707 eq(read
, b
'\0' * 256)
708 data
= rand_data(256)
709 self
.image
.write(data
, 0)
710 read
= self
.image
.read(0, 256)
712 self
.image
.set_snap('snap1')
713 self
.image
.remove_snap('snap1')
714 self
.image
.create_snap('snap1')
715 assert_raises(ImageNotFound
, self
.image
.read
, 0, 256)
716 self
.image
.set_snap(None)
717 read
= self
.image
.read(0, 256)
719 self
.image
.remove_snap('snap1')
721 def test_lock_unlock(self
):
722 assert_raises(ImageNotFound
, self
.image
.unlock
, '')
723 self
.image
.lock_exclusive('')
724 assert_raises(ImageExists
, self
.image
.lock_exclusive
, '')
725 assert_raises(ImageBusy
, self
.image
.lock_exclusive
, 'test')
726 assert_raises(ImageExists
, self
.image
.lock_shared
, '', '')
727 assert_raises(ImageBusy
, self
.image
.lock_shared
, 'foo', '')
728 self
.image
.unlock('')
730 def test_list_lockers(self
):
731 eq([], self
.image
.list_lockers())
732 self
.image
.lock_exclusive('test')
733 lockers
= self
.image
.list_lockers()
734 eq(1, len(lockers
['lockers']))
735 _
, cookie
, _
= lockers
['lockers'][0]
737 eq('', lockers
['tag'])
738 assert lockers
['exclusive']
739 self
.image
.unlock('test')
740 eq([], self
.image
.list_lockers())
743 for i
in range(num_shared
):
744 self
.image
.lock_shared(str(i
), 'tag')
745 lockers
= self
.image
.list_lockers()
746 eq('tag', lockers
['tag'])
747 assert not lockers
['exclusive']
748 eq(num_shared
, len(lockers
['lockers']))
749 cookies
= sorted(map(lambda x
: x
[1], lockers
['lockers']))
750 for i
in range(num_shared
):
751 eq(str(i
), cookies
[i
])
752 self
.image
.unlock(str(i
))
753 eq([], self
.image
.list_lockers())
755 def test_diff_iterate(self
):
756 check_diff(self
.image
, 0, IMG_SIZE
, None, [])
757 self
.image
.write(b
'a' * 256, 0)
758 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 256, True)])
759 self
.image
.write(b
'b' * 256, 256)
760 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
761 self
.image
.discard(128, 256)
762 check_diff(self
.image
, 0, IMG_SIZE
, None, [(0, 512, True)])
764 self
.image
.create_snap('snap1')
765 self
.image
.discard(0, 1 << IMG_ORDER
)
766 self
.image
.create_snap('snap2')
767 self
.image
.set_snap('snap2')
768 check_diff(self
.image
, 0, IMG_SIZE
, 'snap1', [(0, 512, False)])
769 self
.image
.remove_snap('snap1')
770 self
.image
.remove_snap('snap2')
772 def test_aio_read(self
):
773 # this is a list so that the local cb() can modify it
778 # test1: success case
779 comp
= self
.image
.aio_read(0, 20, cb
)
780 comp
.wait_for_complete_and_cb()
781 eq(retval
[0], b
'\0' * 20)
782 eq(comp
.get_return_value(), 20)
783 eq(sys
.getrefcount(comp
), 2)
787 comp
= self
.image
.aio_read(IMG_SIZE
, 20, cb
)
788 comp
.wait_for_complete_and_cb()
790 assert(comp
.get_return_value() < 0)
791 eq(sys
.getrefcount(comp
), 2)
793 def test_aio_write(self
):
796 retval
[0] = comp
.get_return_value()
798 data
= rand_data(256)
799 comp
= self
.image
.aio_write(data
, 256, cb
)
800 comp
.wait_for_complete_and_cb()
802 eq(comp
.get_return_value(), 0)
803 eq(sys
.getrefcount(comp
), 2)
804 eq(self
.image
.read(256, 256), data
)
806 def test_aio_discard(self
):
809 retval
[0] = comp
.get_return_value()
811 data
= rand_data(256)
812 self
.image
.write(data
, 0)
813 comp
= self
.image
.aio_discard(0, 256, cb
)
814 comp
.wait_for_complete_and_cb()
816 eq(comp
.get_return_value(), 0)
817 eq(sys
.getrefcount(comp
), 2)
818 eq(self
.image
.read(256, 256), b
'\0' * 256)
820 def test_aio_flush(self
):
823 retval
[0] = comp
.get_return_value()
825 comp
= self
.image
.aio_flush(cb
)
826 comp
.wait_for_complete_and_cb()
828 eq(sys
.getrefcount(comp
), 2)
830 def test_metadata(self
):
831 metadata
= list(self
.image
.metadata_list())
833 assert_raises(KeyError, self
.image
.metadata_get
, "key1")
834 self
.image
.metadata_set("key1", "value1")
835 self
.image
.metadata_set("key2", "value2")
836 value
= self
.image
.metadata_get("key1")
838 value
= self
.image
.metadata_get("key2")
840 metadata
= list(self
.image
.metadata_list())
842 self
.image
.metadata_remove("key1")
843 metadata
= list(self
.image
.metadata_list())
845 eq(metadata
[0], ("key2", "value2"))
846 self
.image
.metadata_remove("key2")
847 assert_raises(KeyError, self
.image
.metadata_remove
, "key2")
848 metadata
= list(self
.image
.metadata_list())
853 self
.image
.metadata_set("key" + str(i
), "X" * 1025)
854 metadata
= list(self
.image
.metadata_list())
857 self
.image
.metadata_remove("key" + str(i
))
858 metadata
= list(self
.image
.metadata_list())
859 eq(len(metadata
), N
- i
- 1)
861 def check_diff(image
, offset
, length
, from_snapshot
, expected
):
863 def cb(offset
, length
, exists
):
864 extents
.append((offset
, length
, exists
))
865 image
.diff_iterate(0, IMG_SIZE
, None, cb
)
866 eq(extents
, expected
)
868 class TestClone(object):
870 @require_features([RBD_FEATURE_LAYERING
])
876 self
.image
= Image(ioctx
, image_name
)
877 data
= rand_data(256)
878 self
.image
.write(data
, IMG_SIZE
// 2)
879 self
.image
.create_snap('snap1')
881 self
.image
.protect_snap('snap1')
882 self
.clone_name
= get_temp_image_name()
883 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
885 self
.clone
= Image(ioctx
, self
.clone_name
)
890 self
.rbd
.remove(ioctx
, self
.clone_name
)
891 self
.image
.unprotect_snap('snap1')
892 self
.image
.remove_snap('snap1')
896 def _test_with_params(self
, features
=None, order
=None, stripe_unit
=None,
898 self
.image
.create_snap('snap2')
899 self
.image
.protect_snap('snap2')
900 clone_name2
= get_temp_image_name()
902 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
)
904 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
906 elif stripe_unit
is None:
907 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
909 elif stripe_count
is None:
910 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
911 features
, order
, stripe_unit
)
913 self
.rbd
.clone(ioctx
, image_name
, 'snap2', ioctx
, clone_name2
,
914 features
, order
, stripe_unit
, stripe_count
)
915 self
.rbd
.remove(ioctx
, clone_name2
)
916 self
.image
.unprotect_snap('snap2')
917 self
.image
.remove_snap('snap2')
919 def test_with_params(self
):
920 self
._test
_with
_params
()
922 def test_with_params2(self
):
924 self
._test
_with
_params
(features
, self
.image
.stat()['order'])
926 @require_features([RBD_FEATURE_STRIPINGV2
])
927 def test_with_params3(self
):
929 self
._test
_with
_params
(features
, self
.image
.stat()['order'],
930 self
.image
.stripe_unit(),
931 self
.image
.stripe_count())
933 def test_unprotected(self
):
934 self
.image
.create_snap('snap2')
936 clone_name2
= get_temp_image_name()
937 assert_raises(InvalidArgument
, self
.rbd
.clone
, ioctx
, image_name
,
938 'snap2', ioctx
, clone_name2
, features
)
939 self
.image
.remove_snap('snap2')
941 def test_unprotect_with_children(self
):
943 # can't remove a snapshot that has dependent clones
944 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
946 # validate parent info of clone created by TestClone.setUp
947 (pool
, image
, snap
) = self
.clone
.parent_info()
949 eq(image
, image_name
)
951 eq(self
.image
.id(), self
.clone
.parent_id())
953 # create a new pool...
954 pool_name2
= get_temp_pool_name()
955 rados
.create_pool(pool_name2
)
956 other_ioctx
= rados
.open_ioctx(pool_name2
)
957 other_ioctx
.application_enable('rbd')
959 # ...with a clone of the same parent
960 other_clone_name
= get_temp_image_name()
961 self
.rbd
.clone(ioctx
, image_name
, 'snap1', other_ioctx
,
962 other_clone_name
, features
)
963 self
.other_clone
= Image(other_ioctx
, other_clone_name
)
964 # validate its parent info
965 (pool
, image
, snap
) = self
.other_clone
.parent_info()
967 eq(image
, image_name
)
969 eq(self
.image
.id(), self
.other_clone
.parent_id())
971 # can't unprotect snap with children
972 assert_raises(ImageBusy
, self
.image
.unprotect_snap
, 'snap1')
974 # 2 children, check that cannot remove the parent snap
975 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
977 # close and remove other pool's clone
978 self
.other_clone
.close()
979 self
.rbd
.remove(other_ioctx
, other_clone_name
)
981 # check that we cannot yet remove the parent snap
982 assert_raises(ImageBusy
, self
.image
.remove_snap
, 'snap1')
985 rados
.delete_pool(pool_name2
)
987 # unprotect, remove parent snap happen in cleanup, and should succeed
990 image_info
= self
.image
.stat()
991 clone_info
= self
.clone
.stat()
992 eq(clone_info
['size'], image_info
['size'])
993 eq(clone_info
['size'], self
.clone
.overlap())
995 def test_resize_stat(self
):
996 self
.clone
.resize(IMG_SIZE
// 2)
997 image_info
= self
.image
.stat()
998 clone_info
= self
.clone
.stat()
999 eq(clone_info
['size'], IMG_SIZE
// 2)
1000 eq(image_info
['size'], IMG_SIZE
)
1001 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
1003 self
.clone
.resize(IMG_SIZE
* 2)
1004 image_info
= self
.image
.stat()
1005 clone_info
= self
.clone
.stat()
1006 eq(clone_info
['size'], IMG_SIZE
* 2)
1007 eq(image_info
['size'], IMG_SIZE
)
1008 eq(self
.clone
.overlap(), IMG_SIZE
// 2)
1010 def test_resize_io(self
):
1011 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1012 self
.image
.resize(0)
1013 self
.clone
.resize(IMG_SIZE
// 2 + 128)
1014 child_data
= self
.clone
.read(IMG_SIZE
// 2, 128)
1015 eq(child_data
, parent_data
[:128])
1016 self
.clone
.resize(IMG_SIZE
)
1017 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1018 eq(child_data
, parent_data
[:128] + (b
'\0' * 128))
1019 self
.clone
.resize(IMG_SIZE
// 2 + 1)
1020 child_data
= self
.clone
.read(IMG_SIZE
// 2, 1)
1021 eq(child_data
, parent_data
[0:1])
1022 self
.clone
.resize(0)
1023 self
.clone
.resize(IMG_SIZE
)
1024 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1025 eq(child_data
, b
'\0' * 256)
1027 def test_read(self
):
1028 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1029 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1030 eq(child_data
, parent_data
)
1032 def test_write(self
):
1033 parent_data
= self
.image
.read(IMG_SIZE
// 2, 256)
1034 new_data
= rand_data(256)
1035 self
.clone
.write(new_data
, IMG_SIZE
// 2 + 256)
1036 child_data
= self
.clone
.read(IMG_SIZE
// 2 + 256, 256)
1037 eq(child_data
, new_data
)
1038 child_data
= self
.clone
.read(IMG_SIZE
// 2, 256)
1039 eq(child_data
, parent_data
)
1040 parent_data
= self
.image
.read(IMG_SIZE
// 2 + 256, 256)
1041 eq(parent_data
, b
'\0' * 256)
1043 def check_children(self
, expected
):
1044 actual
= self
.image
.list_children()
1045 # dedup for cache pools until
1046 # http://tracker.ceph.com/issues/8187 is fixed
1047 deduped
= set([(pool_name
, image
[1]) for image
in actual
])
1048 eq(deduped
, set(expected
))
1050 def test_list_children(self
):
1053 self
.image
.set_snap('snap1')
1054 self
.check_children([(pool_name
, self
.clone_name
)])
1056 self
.rbd
.remove(ioctx
, self
.clone_name
)
1057 eq(self
.image
.list_children(), [])
1059 clone_name
= get_temp_image_name() + '_'
1060 expected_children
= []
1062 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
,
1063 clone_name
+ str(i
), features
)
1064 expected_children
.append((pool_name
, clone_name
+ str(i
)))
1065 self
.check_children(expected_children
)
1068 self
.rbd
.remove(ioctx
, clone_name
+ str(i
))
1069 expected_children
.pop(0)
1070 self
.check_children(expected_children
)
1072 eq(self
.image
.list_children(), [])
1073 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, self
.clone_name
,
1075 self
.check_children([(pool_name
, self
.clone_name
)])
1076 self
.clone
= Image(ioctx
, self
.clone_name
)
1078 def test_flatten_errors(self
):
1079 # test that we can't flatten a non-clone
1080 assert_raises(InvalidArgument
, self
.image
.flatten
)
1082 # test that we can't flatten a snapshot
1083 self
.clone
.create_snap('snap2')
1084 self
.clone
.set_snap('snap2')
1085 assert_raises(ReadOnlyImage
, self
.clone
.flatten
)
1086 self
.clone
.remove_snap('snap2')
1088 def check_flatten_with_order(self
, new_order
):
1091 clone_name2
= get_temp_image_name()
1092 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1093 features
, new_order
)
1094 #with Image(ioctx, 'clone2') as clone:
1095 clone2
= Image(ioctx
, clone_name2
)
1097 eq(clone2
.overlap(), 0)
1099 self
.rbd
.remove(ioctx
, clone_name2
)
1101 # flatten after resizing to non-block size
1102 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1103 features
, new_order
)
1104 with
Image(ioctx
, clone_name2
) as clone
:
1105 clone
.resize(IMG_SIZE
// 2 - 1)
1107 eq(0, clone
.overlap())
1108 self
.rbd
.remove(ioctx
, clone_name2
)
1110 # flatten after resizing to non-block size
1111 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1112 features
, new_order
)
1113 with
Image(ioctx
, clone_name2
) as clone
:
1114 clone
.resize(IMG_SIZE
// 2 + 1)
1116 eq(clone
.overlap(), 0)
1117 self
.rbd
.remove(ioctx
, clone_name2
)
1119 def test_flatten_basic(self
):
1120 self
.check_flatten_with_order(IMG_ORDER
)
1122 def test_flatten_smaller_order(self
):
1123 self
.check_flatten_with_order(IMG_ORDER
- 2)
1125 def test_flatten_larger_order(self
):
1126 self
.check_flatten_with_order(IMG_ORDER
+ 2)
1128 def test_flatten_drops_cache(self
):
1131 clone_name2
= get_temp_image_name()
1132 self
.rbd
.clone(ioctx
, image_name
, 'snap1', ioctx
, clone_name2
,
1133 features
, IMG_ORDER
)
1134 with
Image(ioctx
, clone_name2
) as clone
:
1135 with
Image(ioctx
, clone_name2
) as clone2
:
1136 # cache object non-existence
1137 data
= clone
.read(IMG_SIZE
// 2, 256)
1138 clone2_data
= clone2
.read(IMG_SIZE
// 2, 256)
1139 eq(data
, clone2_data
)
1141 assert_raises(ImageNotFound
, clone
.parent_info
)
1142 assert_raises(ImageNotFound
, clone2
.parent_info
)
1143 assert_raises(ImageNotFound
, clone
.parent_id
)
1144 assert_raises(ImageNotFound
, clone2
.parent_id
)
1145 after_flatten
= clone
.read(IMG_SIZE
// 2, 256)
1146 eq(data
, after_flatten
)
1147 after_flatten
= clone2
.read(IMG_SIZE
// 2, 256)
1148 eq(data
, after_flatten
)
1149 self
.rbd
.remove(ioctx
, clone_name2
)
1151 def test_flatten_multi_level(self
):
1152 self
.clone
.create_snap('snap2')
1153 self
.clone
.protect_snap('snap2')
1154 clone_name3
= get_temp_image_name()
1155 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1157 self
.clone
.flatten()
1158 with
Image(ioctx
, clone_name3
) as clone3
:
1160 self
.clone
.unprotect_snap('snap2')
1161 self
.clone
.remove_snap('snap2')
1162 self
.rbd
.remove(ioctx
, clone_name3
)
1164 def test_resize_flatten_multi_level(self
):
1165 self
.clone
.create_snap('snap2')
1166 self
.clone
.protect_snap('snap2')
1167 clone_name3
= get_temp_image_name()
1168 self
.rbd
.clone(ioctx
, self
.clone_name
, 'snap2', ioctx
, clone_name3
,
1170 self
.clone
.resize(1)
1171 orig_data
= self
.image
.read(0, 256)
1172 with
Image(ioctx
, clone_name3
) as clone3
:
1173 clone3_data
= clone3
.read(0, 256)
1174 eq(orig_data
, clone3_data
)
1175 self
.clone
.flatten()
1176 with
Image(ioctx
, clone_name3
) as clone3
:
1177 clone3_data
= clone3
.read(0, 256)
1178 eq(orig_data
, clone3_data
)
1179 self
.rbd
.remove(ioctx
, clone_name3
)
1180 self
.clone
.unprotect_snap('snap2')
1181 self
.clone
.remove_snap('snap2')
1183 class TestExclusiveLock(object):
1185 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
])
1188 rados2
= Rados(conffile
='')
1191 ioctx2
= rados2
.open_ioctx(pool_name
)
1201 def test_ownership(self
):
1202 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1203 image1
.write(b
'0'*256, 0)
1204 eq(image1
.is_exclusive_lock_owner(), True)
1205 eq(image2
.is_exclusive_lock_owner(), False)
1207 def test_snapshot_leadership(self
):
1208 with
Image(ioctx
, image_name
) as image
:
1209 image
.create_snap('snap')
1210 eq(image
.is_exclusive_lock_owner(), True)
1212 with
Image(ioctx
, image_name
) as image
:
1213 image
.write(b
'0'*256, 0)
1214 eq(image
.is_exclusive_lock_owner(), True)
1215 image
.set_snap('snap')
1216 eq(image
.is_exclusive_lock_owner(), False)
1217 with
Image(ioctx
, image_name
, snapshot
='snap') as image
:
1218 eq(image
.is_exclusive_lock_owner(), False)
1220 with
Image(ioctx
, image_name
) as image
:
1221 image
.remove_snap('snap')
1223 def test_read_only_leadership(self
):
1224 with
Image(ioctx
, image_name
, read_only
=True) as image
:
1225 eq(image
.is_exclusive_lock_owner(), False)
1227 def test_follower_flatten(self
):
1228 with
Image(ioctx
, image_name
) as image
:
1229 image
.create_snap('snap')
1230 image
.protect_snap('snap')
1232 RBD().clone(ioctx
, image_name
, 'snap', ioctx
, 'clone', features
)
1233 with
Image(ioctx
, 'clone') as image1
, Image(ioctx2
, 'clone') as image2
:
1234 data
= rand_data(256)
1235 image1
.write(data
, 0)
1237 assert_raises(ImageNotFound
, image1
.parent_info
)
1238 assert_raises(ImageNotFound
, image1
.parent_id
)
1242 image2
.parent_info()
1243 except ImageNotFound
:
1248 RBD().remove(ioctx
, 'clone')
1249 with
Image(ioctx
, image_name
) as image
:
1250 image
.unprotect_snap('snap')
1251 image
.remove_snap('snap')
1253 def test_follower_resize(self
):
1254 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1255 image1
.write(b
'0'*256, 0)
1256 for new_size
in [IMG_SIZE
* 2, IMG_SIZE
// 2]:
1257 image2
.resize(new_size
);
1258 eq(new_size
, image1
.size())
1260 if new_size
== image2
.size():
1263 eq(new_size
, image2
.size())
1265 def test_follower_snap_create(self
):
1266 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1267 image2
.create_snap('snap1')
1268 image1
.remove_snap('snap1')
1270 def test_follower_snap_rollback(self
):
1271 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1272 image1
.create_snap('snap')
1274 assert_raises(ReadOnlyImage
, image2
.rollback_to_snap
, 'snap')
1275 image1
.rollback_to_snap('snap')
1277 image1
.remove_snap('snap')
1279 def test_follower_discard(self
):
1281 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1282 data
= rand_data(256)
1283 image1
.write(data
, 0)
1284 image2
.discard(0, 256)
1285 eq(image1
.is_exclusive_lock_owner(), False)
1286 eq(image2
.is_exclusive_lock_owner(), True)
1287 read
= image2
.read(0, 256)
1288 if rados
.conf_get('rbd_skip_partial_discard') == 'false':
1289 eq(256 * b
'\0', read
)
1293 def test_follower_write(self
):
1294 with
Image(ioctx
, image_name
) as image1
, Image(ioctx2
, image_name
) as image2
:
1295 data
= rand_data(256)
1296 image1
.write(data
, 0)
1297 image2
.write(data
, IMG_SIZE
// 2)
1298 eq(image1
.is_exclusive_lock_owner(), False)
1299 eq(image2
.is_exclusive_lock_owner(), True)
1300 for offset
in [0, IMG_SIZE
// 2]:
1301 read
= image2
.read(offset
, 256)
1303 def test_acquire_release_lock(self
):
1304 with
Image(ioctx
, image_name
) as image
:
1305 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
1306 image
.lock_release()
1308 def test_break_lock(self
):
1309 blacklist_rados
= Rados(conffile
='')
1310 blacklist_rados
.connect()
1312 blacklist_ioctx
= blacklist_rados
.open_ioctx(pool_name
)
1314 rados2
.conf_set('rbd_blacklist_on_break_lock', 'true')
1315 with
Image(ioctx2
, image_name
) as image
, \
1316 Image(blacklist_ioctx
, image_name
) as blacklist_image
:
1317 blacklist_image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
1318 assert_raises(ReadOnlyImage
, image
.lock_acquire
,
1319 RBD_LOCK_MODE_EXCLUSIVE
)
1321 lock_owners
= list(image
.lock_get_owners())
1322 eq(1, len(lock_owners
))
1323 eq(RBD_LOCK_MODE_EXCLUSIVE
, lock_owners
[0]['mode'])
1324 image
.lock_break(RBD_LOCK_MODE_EXCLUSIVE
,
1325 lock_owners
[0]['owner'])
1327 assert_raises(ConnectionShutdown
,
1328 blacklist_image
.is_exclusive_lock_owner
)
1330 blacklist_rados
.wait_for_latest_osdmap()
1331 data
= rand_data(256)
1332 assert_raises(ConnectionShutdown
,
1333 blacklist_image
.write
, data
, 0)
1335 image
.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE
)
1338 blacklist_image
.close()
1339 except ConnectionShutdown
:
1342 blacklist_ioctx
.close()
1344 blacklist_rados
.shutdown()
1346 class TestMirroring(object):
1349 def check_info(info
, global_id
, state
, primary
=None):
1350 eq(global_id
, info
['global_id'])
1351 eq(state
, info
['state'])
1352 if primary
is not None:
1353 eq(primary
, info
['primary'])
1357 self
.initial_mirror_mode
= self
.rbd
.mirror_mode_get(ioctx
)
1358 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
1360 self
.image
= Image(ioctx
, image_name
)
1365 self
.rbd
.mirror_mode_set(ioctx
, self
.initial_mirror_mode
)
1368 def test_mirror_peer(self
):
1369 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
1370 cluster_name
= "test_cluster"
1371 client_name
= "test_client"
1372 uuid
= self
.rbd
.mirror_peer_add(ioctx
, cluster_name
, client_name
)
1376 'cluster_name' : cluster_name
,
1377 'client_name' : client_name
,
1379 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
1380 cluster_name
= "test_cluster1"
1381 self
.rbd
.mirror_peer_set_cluster(ioctx
, uuid
, cluster_name
)
1382 client_name
= "test_client1"
1383 self
.rbd
.mirror_peer_set_client(ioctx
, uuid
, client_name
)
1386 'cluster_name' : cluster_name
,
1387 'client_name' : client_name
,
1389 eq([peer
], list(self
.rbd
.mirror_peer_list(ioctx
)))
1390 self
.rbd
.mirror_peer_remove(ioctx
, uuid
)
1391 eq([], list(self
.rbd
.mirror_peer_list(ioctx
)))
1393 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
1394 RBD_FEATURE_JOURNALING
])
1395 def test_mirror_image(self
):
1397 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_IMAGE
)
1398 self
.image
.mirror_image_disable(True)
1399 info
= self
.image
.mirror_image_get_info()
1400 self
.check_info(info
, '', RBD_MIRROR_IMAGE_DISABLED
, False)
1402 self
.image
.mirror_image_enable()
1403 info
= self
.image
.mirror_image_get_info()
1404 global_id
= info
['global_id']
1405 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
1407 self
.rbd
.mirror_mode_set(ioctx
, RBD_MIRROR_MODE_POOL
)
1410 self
.image
.mirror_image_disable(True)
1411 except InvalidArgument
:
1413 eq(True, fail
) # Fails because of mirror mode pool
1415 self
.image
.mirror_image_demote()
1416 info
= self
.image
.mirror_image_get_info()
1417 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, False)
1419 self
.image
.mirror_image_resync()
1421 self
.image
.mirror_image_promote(True)
1422 info
= self
.image
.mirror_image_get_info()
1423 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
1427 self
.image
.mirror_image_resync()
1428 except InvalidArgument
:
1430 eq(True, fail
) # Fails because it is primary
1432 status
= self
.image
.mirror_image_get_status()
1433 eq(image_name
, status
['name'])
1434 eq(False, status
['up'])
1435 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
1436 info
= status
['info']
1437 self
.check_info(info
, global_id
, RBD_MIRROR_IMAGE_ENABLED
, True)
1439 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK
,
1440 RBD_FEATURE_JOURNALING
])
1441 def test_mirror_image_status(self
):
1442 info
= self
.image
.mirror_image_get_info()
1443 global_id
= info
['global_id']
1444 state
= info
['state']
1445 primary
= info
['primary']
1447 status
= self
.image
.mirror_image_get_status()
1448 eq(image_name
, status
['name'])
1449 eq(False, status
['up'])
1450 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
1451 info
= status
['info']
1452 self
.check_info(info
, global_id
, state
, primary
)
1454 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
1457 eq(image_name
, status
['name'])
1458 eq(False, status
['up'])
1459 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, status
['state'])
1460 info
= status
['info']
1461 self
.check_info(info
, global_id
, state
)
1463 states
= self
.rbd
.mirror_image_status_summary(ioctx
)
1464 eq([(MIRROR_IMAGE_STATUS_STATE_UNKNOWN
, 1)], states
)
1468 self
.rbd
.create(ioctx
, image_name
+ str(i
), IMG_SIZE
, IMG_ORDER
,
1469 old_format
=False, features
=int(features
))
1470 images
= list(self
.rbd
.mirror_image_status_list(ioctx
))
1471 eq(N
+ 1, len(images
))
1473 self
.rbd
.remove(ioctx
, image_name
+ str(i
))
1476 class TestTrash(object):
1480 rados2
= Rados(conffile
='')
1483 ioctx2
= rados2
.open_ioctx(pool_name
)
1491 def test_move(self
):
1493 with
Image(ioctx
, image_name
) as image
:
1494 image_id
= image
.id()
1496 RBD().trash_move(ioctx
, image_name
, 1000)
1497 RBD().trash_remove(ioctx
, image_id
, True)
1499 def test_remove_denied(self
):
1501 with
Image(ioctx
, image_name
) as image
:
1502 image_id
= image
.id()
1504 RBD().trash_move(ioctx
, image_name
, 1000)
1505 assert_raises(PermissionError
, RBD().trash_remove
, ioctx
, image_id
)
1507 def test_remove(self
):
1509 with
Image(ioctx
, image_name
) as image
:
1510 image_id
= image
.id()
1512 RBD().trash_move(ioctx
, image_name
, 0)
1513 RBD().trash_remove(ioctx
, image_id
)
1517 with
Image(ioctx
, image_name
) as image
:
1518 image_id
= image
.id()
1520 RBD().trash_move(ioctx
, image_name
, 1000)
1522 info
= RBD().trash_get(ioctx
, image_id
)
1523 eq(image_id
, info
['id'])
1524 eq(image_name
, info
['name'])
1525 eq('USER', info
['source'])
1526 assert(info
['deferment_end_time'] > info
['deletion_time'])
1528 RBD().trash_remove(ioctx
, image_id
, True)
1530 def test_list(self
):
1532 with
Image(ioctx
, image_name
) as image
:
1533 image_id1
= image
.id()
1534 image_name1
= image_name
1535 RBD().trash_move(ioctx
, image_name
, 1000)
1538 with
Image(ioctx
, image_name
) as image
:
1539 image_id2
= image
.id()
1540 image_name2
= image_name
1541 RBD().trash_move(ioctx
, image_name
, 1000)
1543 entries
= list(RBD().trash_list(ioctx
))
1545 if e
['id'] == image_id1
:
1546 eq(e
['name'], image_name1
)
1547 elif e
['id'] == image_id2
:
1548 eq(e
['name'], image_name2
)
1551 eq(e
['source'], 'USER')
1552 assert e
['deferment_end_time'] > e
['deletion_time']
1554 RBD().trash_remove(ioctx
, image_id1
, True)
1555 RBD().trash_remove(ioctx
, image_id2
, True)
1557 def test_restore(self
):
1559 with
Image(ioctx
, image_name
) as image
:
1560 image_id
= image
.id()
1561 RBD().trash_move(ioctx
, image_name
, 1000)
1562 RBD().trash_restore(ioctx
, image_id
, image_name
)