]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/pybind/test_rbd.py
update sources to v12.2.3
[ceph.git] / ceph / src / test / pybind / test_rbd.py
1 # vim: expandtab smarttab shiftwidth=4 softtabstop=4
2 import functools
3 import socket
4 import os
5 import time
6 import sys
7
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)
25
26 rados = None
27 ioctx = None
28 features = None
29 image_idx = 0
30 image_name = None
31 pool_idx = 0
32 pool_name = None
33 IMG_SIZE = 8 << 20 # 8 MiB
34 IMG_ORDER = 22 # 4 MiB objects
35
36 def setup_module():
37 global rados
38 rados = Rados(conffile='')
39 rados.connect()
40 global pool_name
41 pool_name = get_temp_pool_name()
42 rados.create_pool(pool_name)
43 global ioctx
44 ioctx = rados.open_ioctx(pool_name)
45 ioctx.application_enable('rbd')
46 global features
47 features = os.getenv("RBD_FEATURES")
48 features = int(features) if features is not None else 61
49
50 def teardown_module():
51 global ioctx
52 ioctx.close()
53 global rados
54 rados.delete_pool(pool_name)
55 rados.shutdown()
56
57 def get_temp_pool_name():
58 global pool_idx
59 pool_idx += 1
60 return "test-rbd-api-" + socket.gethostname() + '-' + str(os.getpid()) + \
61 '-' + str(pool_idx)
62
63 def get_temp_image_name():
64 global image_idx
65 image_idx += 1
66 return "image" + str(image_idx)
67
68 def create_image():
69 global image_name
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))
74 else:
75 RBD().create(ioctx, image_name, IMG_SIZE, IMG_ORDER, old_format=True)
76
77 def remove_image():
78 if image_name is not None:
79 RBD().remove(ioctx, image_name)
80
81 def require_new_format():
82 def wrapper(fn):
83 def _require_new_format(*args, **kwargs):
84 global features
85 if features is None:
86 raise SkipTest
87 return fn(*args, **kwargs)
88 return functools.wraps(fn)(_require_new_format)
89 return wrapper
90
91 def require_features(required_features):
92 def wrapper(fn):
93 def _require_features(*args, **kwargs):
94 global features
95 if features is None:
96 raise SkipTest
97 for feature in required_features:
98 if feature & features != feature:
99 raise SkipTest
100 return fn(*args, **kwargs)
101 return functools.wraps(fn)(_require_features)
102 return wrapper
103
104 def blacklist_features(blacklisted_features):
105 def wrapper(fn):
106 def _blacklist_features(*args, **kwargs):
107 global features
108 for feature in blacklisted_features:
109 if features is not None and feature & features == feature:
110 raise SkipTest
111 return fn(*args, **kwargs)
112 return functools.wraps(fn)(_blacklist_features)
113 return wrapper
114
115 def test_version():
116 RBD().version()
117
118 def test_create():
119 create_image()
120 remove_image()
121
122 def check_default_params(format, order=None, features=None, stripe_count=None,
123 stripe_unit=None, exception=None):
124 global rados
125 global ioctx
126 orig_vals = {}
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)
130 try:
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)
147 try:
148 with Image(ioctx, image_name) as image:
149 eq(format == 1, image.old_format())
150
151 expected_order = int(rados.conf_get('rbd_default_order'))
152 actual_order = image.stat()['order']
153 eq(expected_order, actual_order)
154
155 expected_features = features
156 if format == 1:
157 expected_features = 0
158 elif expected_features is None:
159 expected_features = 61 | feature_data_pool
160 else:
161 expected_features |= feature_data_pool
162 eq(expected_features, image.features())
163
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())
169
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())
175 finally:
176 RBD().remove(ioctx, image_name)
177 else:
178 assert_raises(exception, RBD().create, ioctx, image_name, IMG_SIZE)
179 finally:
180 for k, v in orig_vals.items():
181 rados.conf_set(k, v)
182
183 def test_create_defaults():
184 # basic format 1 and 2
185 check_default_params(1)
186 check_default_params(2)
187 # invalid order
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)
214
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)
222 image.write(data, 0)
223 read = image.read(0, 256)
224 RBD().remove(ioctx, image_name)
225 eq(data, read)
226
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:
234 image.write(data, 0)
235 image.create_snap('snap')
236 with Image(ioctx, image_name, read_only=True) as image:
237 read = image.read(0, 256)
238 eq(data, read)
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)
250 eq(data, read)
251
252 def test_open_dne():
253 for i in range(100):
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')
257
258 def test_open_readonly_dne():
259 for i in range(100):
260 image_name = get_temp_image_name()
261 assert_raises(ImageNotFound, Image, ioctx, image_name + 'dne',
262 read_only=True)
263 assert_raises(ImageNotFound, Image, ioctx, image_name, 'snap',
264 read_only=True)
265
266 def test_remove_dne():
267 assert_raises(ImageNotFound, remove_image)
268
269 def test_list_empty():
270 eq([], RBD().list(ioctx))
271
272 @with_setup(create_image, remove_image)
273 def test_list():
274 eq([image_name], RBD().list(ioctx))
275
276 @with_setup(create_image, remove_image)
277 def test_rename():
278 rbd = RBD()
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))
284
285 def rand_data(size):
286 return os.urandom(size)
287
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)
294
295 class TestImage(object):
296
297 def setUp(self):
298 self.rbd = RBD()
299 create_image()
300 self.image = Image(ioctx, image_name)
301
302 def tearDown(self):
303 self.image.close()
304 remove_image()
305 self.image = None
306
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())
313
314 @require_features([RBD_FEATURE_STRIPINGV2])
315 def test_create_with_params(self):
316 global features
317 image_name = get_temp_image_name()
318 order = 20
319 stripe_unit = 1 << 20
320 stripe_count = 10
321 self.rbd.create(ioctx, image_name, IMG_SIZE, order,
322 False, features, stripe_unit, stripe_count)
323 image = Image(ioctx, image_name)
324 info = image.stat()
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)
329 image.close()
330 RBD().remove(ioctx, image_name)
331
332 @require_new_format()
333 def test_id(self):
334 assert_not_equal(b'', self.image.id())
335
336 def test_block_name_prefix(self):
337 assert_not_equal(b'', self.image.block_name_prefix())
338
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)
343
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))
349
350 def test_stat(self):
351 info = self.image.stat()
352 check_stat(info, IMG_SIZE, IMG_ORDER)
353
354 def test_flags(self):
355 flags = self.image.flags()
356 eq(0, flags)
357
358 def test_image_auto_close(self):
359 image = Image(ioctx, image_name)
360
361 def test_write(self):
362 data = rand_data(256)
363 self.image.write(data, 0)
364
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)
369
370 def test_read(self):
371 data = self.image.read(0, 20)
372 eq(data, b'\0' * 20)
373
374 def test_read_with_fadvise_flags(self):
375 data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED)
376 eq(data, b'\0' * 20)
377 data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM)
378 eq(data, b'\0' * 20)
379
380 def test_large_write(self):
381 data = rand_data(IMG_SIZE)
382 self.image.write(data, 0)
383
384 def test_large_read(self):
385 data = self.image.read(0, IMG_SIZE)
386 eq(data, b'\0' * IMG_SIZE)
387
388 def test_write_read(self):
389 data = rand_data(256)
390 offset = 50
391 self.image.write(data, offset)
392 read = self.image.read(offset, 256)
393 eq(data, read)
394
395 def test_read_bad_offset(self):
396 assert_raises(InvalidArgument, self.image.read, IMG_SIZE + 1, IMG_SIZE)
397
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)
403
404 def test_size(self):
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')
419
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)
428
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)
436 eq(data[:5], read)
437 read = self.image.read(IMG_SIZE // 2 - 5, 251)
438 eq(b'\0' * 251, read)
439
440 def _test_copy(self, features=None, order=None, stripe_unit=None,
441 stripe_count=None):
442 global ioctx
443 data = rand_data(256)
444 self.image.write(data, 256)
445 image_name = get_temp_image_name()
446 if features is None:
447 self.image.copy(ioctx, image_name)
448 elif order is None:
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)
454 else:
455 self.image.copy(ioctx, image_name, features, order, stripe_unit,
456 stripe_count)
457 assert_raises(ImageExists, self.image.copy, ioctx, image_name)
458 copy = Image(ioctx, image_name)
459 copy_data = copy.read(256, 256)
460 copy.close()
461 self.rbd.remove(ioctx, image_name)
462 eq(data, copy_data)
463
464 def test_copy(self):
465 self._test_copy()
466
467 def test_copy2(self):
468 self._test_copy(self.image.features(), self.image.stat()['order'])
469
470 @require_features([RBD_FEATURE_STRIPINGV2])
471 def test_copy3(self):
472 global features
473 self._test_copy(features, self.image.stat()['order'],
474 self.image.stripe_unit(), self.image.stripe_count())
475
476 def test_create_snap(self):
477 global ioctx
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)
484 eq(read, data)
485 at_snapshot = Image(ioctx, image_name, 'snap1')
486 snap_data = at_snapshot.read(0, 256)
487 at_snapshot.close()
488 eq(snap_data, b'\0' * 256)
489 self.image.remove_snap('snap1')
490
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')
499
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')
504
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()))
511
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()))
520
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')
533
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():
538 snap_id = snap["id"]
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')
544
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')
553
554 self.image.remove_snap('snap1')
555 self.image.remove_snap('snap2')
556 self.image.remove_snap('snap3')
557
558 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK])
559 def test_remove_with_exclusive_lock(self):
560 assert_raises(ImageBusy, remove_image)
561
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')
567
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)
574 eq(read, data)
575
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)
584 eq(read, data)
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')
589
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)
597 eq(read, data)
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')
602
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)
610 eq(read, data)
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)
617 eq(read, data)
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)
624 eq(read, data)
625 self.image.remove_snap('snap1')
626 self.image.remove_snap('snap2')
627
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)
636 eq(read, data)
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')
641
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)
650 eq(read, data)
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)
656 eq(read, data)
657 self.image.remove_snap('snap1')
658
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)
666 eq(read, data)
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')
671
672 def test_many_snaps(self):
673 num_snaps = 200
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))
684
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)
693 eq(read, data)
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)
699 eq(read, data)
700
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)
709 eq(read, data)
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)
716 eq(read, data)
717 self.image.remove_snap('snap1')
718
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('')
727
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]
734 eq(cookie, 'test')
735 eq('', lockers['tag'])
736 assert lockers['exclusive']
737 self.image.unlock('test')
738 eq([], self.image.list_lockers())
739
740 num_shared = 10
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())
752
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)])
761
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')
769
770 def test_aio_read(self):
771 # this is a list so that the local cb() can modify it
772 retval = [None]
773 def cb(_, buf):
774 retval[0] = buf
775
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)
782
783 # test2: error case
784 retval[0] = 1
785 comp = self.image.aio_read(IMG_SIZE, 20, cb)
786 comp.wait_for_complete_and_cb()
787 eq(None, retval[0])
788 assert(comp.get_return_value() < 0)
789 eq(sys.getrefcount(comp), 2)
790
791 def test_aio_write(self):
792 retval = [None]
793 def cb(comp):
794 retval[0] = comp.get_return_value()
795
796 data = rand_data(256)
797 comp = self.image.aio_write(data, 256, cb)
798 comp.wait_for_complete_and_cb()
799 eq(retval[0], 0)
800 eq(comp.get_return_value(), 0)
801 eq(sys.getrefcount(comp), 2)
802 eq(self.image.read(256, 256), data)
803
804 def test_aio_discard(self):
805 retval = [None]
806 def cb(comp):
807 retval[0] = comp.get_return_value()
808
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()
813 eq(retval[0], 0)
814 eq(comp.get_return_value(), 0)
815 eq(sys.getrefcount(comp), 2)
816 eq(self.image.read(256, 256), b'\0' * 256)
817
818 def test_aio_flush(self):
819 retval = [None]
820 def cb(comp):
821 retval[0] = comp.get_return_value()
822
823 comp = self.image.aio_flush(cb)
824 comp.wait_for_complete_and_cb()
825 eq(retval[0], 0)
826 eq(sys.getrefcount(comp), 2)
827
828 def test_metadata(self):
829 metadata = list(self.image.metadata_list())
830 eq(len(metadata), 0)
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")
835 eq(value, "value1")
836 value = self.image.metadata_get("key2")
837 eq(value, "value2")
838 metadata = list(self.image.metadata_list())
839 eq(len(metadata), 2)
840 self.image.metadata_remove("key1")
841 metadata = list(self.image.metadata_list())
842 eq(len(metadata), 1)
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())
847 eq(len(metadata), 0)
848
849 N = 65
850 for i in xrange(N):
851 self.image.metadata_set("key" + str(i), "X" * 1025)
852 metadata = list(self.image.metadata_list())
853 eq(len(metadata), N)
854 for i in xrange(N):
855 self.image.metadata_remove("key" + str(i))
856 metadata = list(self.image.metadata_list())
857 eq(len(metadata), N - i - 1)
858
859 def check_diff(image, offset, length, from_snapshot, expected):
860 extents = []
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)
865
866 class TestClone(object):
867
868 @require_features([RBD_FEATURE_LAYERING])
869 def setUp(self):
870 global ioctx
871 global features
872 self.rbd = RBD()
873 create_image()
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')
878 global features
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,
882 features)
883 self.clone = Image(ioctx, self.clone_name)
884
885 def tearDown(self):
886 global ioctx
887 self.clone.close()
888 self.rbd.remove(ioctx, self.clone_name)
889 self.image.unprotect_snap('snap1')
890 self.image.remove_snap('snap1')
891 self.image.close()
892 remove_image()
893
894 def _test_with_params(self, features=None, order=None, stripe_unit=None,
895 stripe_count=None):
896 self.image.create_snap('snap2')
897 self.image.protect_snap('snap2')
898 clone_name2 = get_temp_image_name()
899 if features is None:
900 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2)
901 elif order is None:
902 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
903 features)
904 elif stripe_unit is None:
905 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
906 features, order)
907 elif stripe_count is None:
908 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
909 features, order, stripe_unit)
910 else:
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')
916
917 def test_with_params(self):
918 self._test_with_params()
919
920 def test_with_params2(self):
921 global features
922 self._test_with_params(features, self.image.stat()['order'])
923
924 @require_features([RBD_FEATURE_STRIPINGV2])
925 def test_with_params3(self):
926 global features
927 self._test_with_params(features, self.image.stat()['order'],
928 self.image.stripe_unit(),
929 self.image.stripe_count())
930
931 def test_unprotected(self):
932 self.image.create_snap('snap2')
933 global features
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')
938
939 def test_unprotect_with_children(self):
940 global features
941 # can't remove a snapshot that has dependent clones
942 assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
943
944 # validate parent info of clone created by TestClone.setUp
945 (pool, image, snap) = self.clone.parent_info()
946 eq(pool, pool_name)
947 eq(image, image_name)
948 eq(snap, 'snap1')
949 eq(self.image.id(), self.clone.parent_id())
950
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')
956
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()
964 eq(pool, pool_name)
965 eq(image, image_name)
966 eq(snap, 'snap1')
967 eq(self.image.id(), self.other_clone.parent_id())
968
969 # can't unprotect snap with children
970 assert_raises(ImageBusy, self.image.unprotect_snap, 'snap1')
971
972 # 2 children, check that cannot remove the parent snap
973 assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
974
975 # close and remove other pool's clone
976 self.other_clone.close()
977 self.rbd.remove(other_ioctx, other_clone_name)
978
979 # check that we cannot yet remove the parent snap
980 assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
981
982 other_ioctx.close()
983 rados.delete_pool(pool_name2)
984
985 # unprotect, remove parent snap happen in cleanup, and should succeed
986
987 def test_stat(self):
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())
992
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)
1000
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)
1007
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)
1024
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)
1029
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)
1040
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))
1047
1048 def test_list_children(self):
1049 global ioctx
1050 global features
1051 self.image.set_snap('snap1')
1052 self.check_children([(pool_name, self.clone_name)])
1053 self.clone.close()
1054 self.rbd.remove(ioctx, self.clone_name)
1055 eq(self.image.list_children(), [])
1056
1057 clone_name = get_temp_image_name() + '_'
1058 expected_children = []
1059 for i in range(10):
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)
1064
1065 for i in range(10):
1066 self.rbd.remove(ioctx, clone_name + str(i))
1067 expected_children.pop(0)
1068 self.check_children(expected_children)
1069
1070 eq(self.image.list_children(), [])
1071 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name,
1072 features)
1073 self.check_children([(pool_name, self.clone_name)])
1074 self.clone = Image(ioctx, self.clone_name)
1075
1076 def test_flatten_errors(self):
1077 # test that we can't flatten a non-clone
1078 assert_raises(InvalidArgument, self.image.flatten)
1079
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')
1085
1086 def check_flatten_with_order(self, new_order):
1087 global ioctx
1088 global features
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)
1094 clone2.flatten()
1095 eq(clone2.overlap(), 0)
1096 clone2.close()
1097 self.rbd.remove(ioctx, clone_name2)
1098
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)
1104 clone.flatten()
1105 eq(0, clone.overlap())
1106 self.rbd.remove(ioctx, clone_name2)
1107
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)
1113 clone.flatten()
1114 eq(clone.overlap(), 0)
1115 self.rbd.remove(ioctx, clone_name2)
1116
1117 def test_flatten_basic(self):
1118 self.check_flatten_with_order(IMG_ORDER)
1119
1120 def test_flatten_smaller_order(self):
1121 self.check_flatten_with_order(IMG_ORDER - 2)
1122
1123 def test_flatten_larger_order(self):
1124 self.check_flatten_with_order(IMG_ORDER + 2)
1125
1126 def test_flatten_drops_cache(self):
1127 global ioctx
1128 global features
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)
1138 clone.flatten()
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)
1148
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,
1154 features)
1155 self.clone.flatten()
1156 with Image(ioctx, clone_name3) as clone3:
1157 clone3.flatten()
1158 self.clone.unprotect_snap('snap2')
1159 self.clone.remove_snap('snap2')
1160 self.rbd.remove(ioctx, clone_name3)
1161
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,
1167 features)
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')
1180
1181 class TestExclusiveLock(object):
1182
1183 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK])
1184 def setUp(self):
1185 global rados2
1186 rados2 = Rados(conffile='')
1187 rados2.connect()
1188 global ioctx2
1189 ioctx2 = rados2.open_ioctx(pool_name)
1190 create_image()
1191
1192 def tearDown(self):
1193 remove_image()
1194 global ioctx2
1195 ioctx2.close()
1196 global rados2
1197 rados2.shutdown()
1198
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)
1204
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)
1209 try:
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)
1217 finally:
1218 with Image(ioctx, image_name) as image:
1219 image.remove_snap('snap')
1220
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)
1224
1225 def test_follower_flatten(self):
1226 with Image(ioctx, image_name) as image:
1227 image.create_snap('snap')
1228 image.protect_snap('snap')
1229 try:
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)
1234 image2.flatten()
1235 assert_raises(ImageNotFound, image1.parent_info)
1236 assert_raises(ImageNotFound, image1.parent_id)
1237 parent = True
1238 for x in range(30):
1239 try:
1240 image2.parent_info()
1241 except ImageNotFound:
1242 parent = False
1243 break
1244 eq(False, parent)
1245 finally:
1246 RBD().remove(ioctx, 'clone')
1247 with Image(ioctx, image_name) as image:
1248 image.unprotect_snap('snap')
1249 image.remove_snap('snap')
1250
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())
1257 for x in range(30):
1258 if new_size == image2.size():
1259 break
1260 time.sleep(1)
1261 eq(new_size, image2.size())
1262
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')
1267
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')
1271 try:
1272 assert_raises(ReadOnlyImage, image2.rollback_to_snap, 'snap')
1273 image1.rollback_to_snap('snap')
1274 finally:
1275 image1.remove_snap('snap')
1276
1277 def test_follower_discard(self):
1278 global rados
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)
1288 else:
1289 eq(data, read)
1290
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)
1300 eq(data, read)
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()
1305
1306 def test_break_lock(self):
1307 blacklist_rados = Rados(conffile='')
1308 blacklist_rados.connect()
1309 try:
1310 blacklist_ioctx = blacklist_rados.open_ioctx(pool_name)
1311 try:
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)
1318
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'])
1324
1325 assert_raises(ConnectionShutdown,
1326 blacklist_image.is_exclusive_lock_owner)
1327
1328 blacklist_rados.wait_for_latest_osdmap()
1329 data = rand_data(256)
1330 assert_raises(ConnectionShutdown,
1331 blacklist_image.write, data, 0)
1332
1333 image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
1334
1335 try:
1336 blacklist_image.close()
1337 except ConnectionShutdown:
1338 pass
1339 finally:
1340 blacklist_ioctx.close()
1341 finally:
1342 blacklist_rados.shutdown()
1343
1344 class TestMirroring(object):
1345
1346 @staticmethod
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'])
1352
1353 def setUp(self):
1354 self.rbd = RBD()
1355 self.initial_mirror_mode = self.rbd.mirror_mode_get(ioctx)
1356 self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL)
1357 create_image()
1358 self.image = Image(ioctx, image_name)
1359
1360 def tearDown(self):
1361 self.image.close()
1362 remove_image()
1363 self.rbd.mirror_mode_set(ioctx, self.initial_mirror_mode)
1364
1365
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)
1371 assert(uuid)
1372 peer = {
1373 'uuid' : uuid,
1374 'cluster_name' : cluster_name,
1375 'client_name' : client_name,
1376 }
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)
1382 peer = {
1383 'uuid' : uuid,
1384 'cluster_name' : cluster_name,
1385 'client_name' : client_name,
1386 }
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)))
1390
1391 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK,
1392 RBD_FEATURE_JOURNALING])
1393 def test_mirror_image(self):
1394
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)
1399
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)
1404
1405 self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL)
1406 fail = False
1407 try:
1408 self.image.mirror_image_disable(True)
1409 except InvalidArgument:
1410 fail = True
1411 eq(True, fail) # Fails because of mirror mode pool
1412
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)
1416
1417 self.image.mirror_image_resync()
1418
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)
1422
1423 fail = False
1424 try:
1425 self.image.mirror_image_resync()
1426 except InvalidArgument:
1427 fail = True
1428 eq(True, fail) # Fails because it is primary
1429
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)
1436
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']
1444
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)
1451
1452 images = list(self.rbd.mirror_image_status_list(ioctx))
1453 eq(1, len(images))
1454 status = images[0]
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)
1460
1461 states = self.rbd.mirror_image_status_summary(ioctx)
1462 eq([(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, 1)], states)
1463
1464 N = 65
1465 for i in range(N):
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))
1470 for i in range(N):
1471 self.rbd.remove(ioctx, image_name + str(i))
1472
1473
1474 class TestTrash(object):
1475
1476 def setUp(self):
1477 global rados2
1478 rados2 = Rados(conffile='')
1479 rados2.connect()
1480 global ioctx2
1481 ioctx2 = rados2.open_ioctx(pool_name)
1482
1483 def tearDown(self):
1484 global ioctx2
1485 ioctx2.close()
1486 global rados2
1487 rados2.shutdown()
1488
1489 def test_move(self):
1490 create_image()
1491 with Image(ioctx, image_name) as image:
1492 image_id = image.id()
1493
1494 RBD().trash_move(ioctx, image_name, 1000)
1495 RBD().trash_remove(ioctx, image_id, True)
1496
1497 def test_remove_denied(self):
1498 create_image()
1499 with Image(ioctx, image_name) as image:
1500 image_id = image.id()
1501
1502 RBD().trash_move(ioctx, image_name, 1000)
1503 assert_raises(PermissionError, RBD().trash_remove, ioctx, image_id)
1504
1505 def test_remove(self):
1506 create_image()
1507 with Image(ioctx, image_name) as image:
1508 image_id = image.id()
1509
1510 RBD().trash_move(ioctx, image_name, 0)
1511 RBD().trash_remove(ioctx, image_id)
1512
1513 def test_get(self):
1514 create_image()
1515 with Image(ioctx, image_name) as image:
1516 image_id = image.id()
1517
1518 RBD().trash_move(ioctx, image_name, 1000)
1519
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'])
1525
1526 RBD().trash_remove(ioctx, image_id, True)
1527
1528 def test_list(self):
1529 create_image()
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)
1534
1535 create_image()
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)
1540
1541 entries = list(RBD().trash_list(ioctx))
1542 for e in entries:
1543 if e['id'] == image_id1:
1544 eq(e['name'], image_name1)
1545 elif e['id'] == image_id2:
1546 eq(e['name'], image_name2)
1547 else:
1548 assert False
1549 eq(e['source'], 'USER')
1550 assert e['deferment_end_time'] > e['deletion_time']
1551
1552 RBD().trash_remove(ioctx, image_id1, True)
1553 RBD().trash_remove(ioctx, image_id2, True)
1554
1555 def test_restore(self):
1556 create_image()
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)
1561 remove_image()