]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/pybind/test_rbd.py
5e8e18d19043366503cda6b27e8fff1a96f29fa4
[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 global features
46 features = os.getenv("RBD_FEATURES")
47 features = int(features) if features is not None else 61
48
49 def teardown_module():
50 global ioctx
51 ioctx.close()
52 global rados
53 rados.delete_pool(pool_name)
54 rados.shutdown()
55
56 def get_temp_pool_name():
57 global pool_idx
58 pool_idx += 1
59 return "test-rbd-api-" + socket.gethostname() + '-' + str(os.getpid()) + \
60 '-' + str(pool_idx)
61
62 def get_temp_image_name():
63 global image_idx
64 image_idx += 1
65 return "image" + str(image_idx)
66
67 def create_image():
68 global image_name
69 image_name = get_temp_image_name()
70 if features is not None:
71 RBD().create(ioctx, image_name, IMG_SIZE, IMG_ORDER, old_format=False,
72 features=int(features))
73 else:
74 RBD().create(ioctx, image_name, IMG_SIZE, IMG_ORDER, old_format=True)
75
76 def remove_image():
77 if image_name is not None:
78 RBD().remove(ioctx, image_name)
79
80 def require_new_format():
81 def wrapper(fn):
82 def _require_new_format(*args, **kwargs):
83 global features
84 if features is None:
85 raise SkipTest
86 return fn(*args, **kwargs)
87 return functools.wraps(fn)(_require_new_format)
88 return wrapper
89
90 def require_features(required_features):
91 def wrapper(fn):
92 def _require_features(*args, **kwargs):
93 global features
94 if features is None:
95 raise SkipTest
96 for feature in required_features:
97 if feature & features != feature:
98 raise SkipTest
99 return fn(*args, **kwargs)
100 return functools.wraps(fn)(_require_features)
101 return wrapper
102
103 def blacklist_features(blacklisted_features):
104 def wrapper(fn):
105 def _blacklist_features(*args, **kwargs):
106 global features
107 for feature in blacklisted_features:
108 if features is not None and feature & features == feature:
109 raise SkipTest
110 return fn(*args, **kwargs)
111 return functools.wraps(fn)(_blacklist_features)
112 return wrapper
113
114 def test_version():
115 RBD().version()
116
117 def test_create():
118 create_image()
119 remove_image()
120
121 def check_default_params(format, order=None, features=None, stripe_count=None,
122 stripe_unit=None, exception=None):
123 global rados
124 global ioctx
125 orig_vals = {}
126 for k in ['rbd_default_format', 'rbd_default_order', 'rbd_default_features',
127 'rbd_default_stripe_count', 'rbd_default_stripe_unit']:
128 orig_vals[k] = rados.conf_get(k)
129 try:
130 rados.conf_set('rbd_default_format', str(format))
131 if order is not None:
132 rados.conf_set('rbd_default_order', str(order or 0))
133 if features is not None:
134 rados.conf_set('rbd_default_features', str(features or 0))
135 if stripe_count is not None:
136 rados.conf_set('rbd_default_stripe_count', str(stripe_count or 0))
137 if stripe_unit is not None:
138 rados.conf_set('rbd_default_stripe_unit', str(stripe_unit or 0))
139 feature_data_pool = 0
140 datapool = rados.conf_get('rbd_default_data_pool')
141 if not len(datapool) == 0:
142 feature_data_pool = 128
143 image_name = get_temp_image_name()
144 if exception is None:
145 RBD().create(ioctx, image_name, IMG_SIZE)
146 try:
147 with Image(ioctx, image_name) as image:
148 eq(format == 1, image.old_format())
149
150 expected_order = int(rados.conf_get('rbd_default_order'))
151 actual_order = image.stat()['order']
152 eq(expected_order, actual_order)
153
154 expected_features = features
155 if format == 1:
156 expected_features = 0
157 elif expected_features is None:
158 expected_features = 61 | feature_data_pool
159 else:
160 expected_features |= feature_data_pool
161 eq(expected_features, image.features())
162
163 expected_stripe_count = stripe_count
164 if not expected_stripe_count or format == 1 or \
165 features & RBD_FEATURE_STRIPINGV2 == 0:
166 expected_stripe_count = 1
167 eq(expected_stripe_count, image.stripe_count())
168
169 expected_stripe_unit = stripe_unit
170 if not expected_stripe_unit or format == 1 or \
171 features & RBD_FEATURE_STRIPINGV2 == 0:
172 expected_stripe_unit = 1 << actual_order
173 eq(expected_stripe_unit, image.stripe_unit())
174 finally:
175 RBD().remove(ioctx, image_name)
176 else:
177 assert_raises(exception, RBD().create, ioctx, image_name, IMG_SIZE)
178 finally:
179 for k, v in orig_vals.items():
180 rados.conf_set(k, v)
181
182 def test_create_defaults():
183 # basic format 1 and 2
184 check_default_params(1)
185 check_default_params(2)
186 # invalid order
187 check_default_params(1, 0, exception=ArgumentOutOfRange)
188 check_default_params(2, 0, exception=ArgumentOutOfRange)
189 check_default_params(1, 11, exception=ArgumentOutOfRange)
190 check_default_params(2, 11, exception=ArgumentOutOfRange)
191 check_default_params(1, 65, exception=ArgumentOutOfRange)
192 check_default_params(2, 65, exception=ArgumentOutOfRange)
193 # striping and features are ignored for format 1
194 check_default_params(1, 20, 0, 1, 1)
195 check_default_params(1, 20, 3, 1, 1)
196 check_default_params(1, 20, 0, 0, 0)
197 # striping is ignored if stripingv2 is not set
198 check_default_params(2, 20, 0, 1, 1 << 20)
199 check_default_params(2, 20, RBD_FEATURE_LAYERING, 1, 1 << 20)
200 check_default_params(2, 20, 0, 0, 0)
201 # striping with stripingv2 is fine
202 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2, 1, 1 << 16)
203 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2, 10, 1 << 20)
204 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2, 10, 1 << 16)
205 check_default_params(2, 20, 0, 0, 0)
206 # make sure invalid combinations of stripe unit and order are still invalid
207 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 10, 1 << 50, exception=InvalidArgument)
208 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 10, 100, exception=InvalidArgument)
209 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 0, 1, exception=InvalidArgument)
210 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 1, 0, exception=InvalidArgument)
211 # 0 stripe unit and count are still ignored
212 check_default_params(2, 22, 0, 0, 0)
213
214 def test_context_manager():
215 with Rados(conffile='') as cluster:
216 with cluster.open_ioctx(pool_name) as ioctx:
217 image_name = get_temp_image_name()
218 RBD().create(ioctx, image_name, IMG_SIZE)
219 with Image(ioctx, image_name) as image:
220 data = rand_data(256)
221 image.write(data, 0)
222 read = image.read(0, 256)
223 RBD().remove(ioctx, image_name)
224 eq(data, read)
225
226 def test_open_read_only():
227 with Rados(conffile='') as cluster:
228 with cluster.open_ioctx(pool_name) as ioctx:
229 image_name = get_temp_image_name()
230 RBD().create(ioctx, image_name, IMG_SIZE)
231 data = rand_data(256)
232 with Image(ioctx, image_name) as image:
233 image.write(data, 0)
234 image.create_snap('snap')
235 with Image(ioctx, image_name, read_only=True) as image:
236 read = image.read(0, 256)
237 eq(data, read)
238 assert_raises(ReadOnlyImage, image.write, data, 0)
239 assert_raises(ReadOnlyImage, image.create_snap, 'test')
240 assert_raises(ReadOnlyImage, image.remove_snap, 'snap')
241 assert_raises(ReadOnlyImage, image.rollback_to_snap, 'snap')
242 assert_raises(ReadOnlyImage, image.protect_snap, 'snap')
243 assert_raises(ReadOnlyImage, image.unprotect_snap, 'snap')
244 assert_raises(ReadOnlyImage, image.unprotect_snap, 'snap')
245 assert_raises(ReadOnlyImage, image.flatten)
246 with Image(ioctx, image_name) as image:
247 image.remove_snap('snap')
248 RBD().remove(ioctx, image_name)
249 eq(data, read)
250
251 def test_open_dne():
252 for i in range(100):
253 image_name = get_temp_image_name()
254 assert_raises(ImageNotFound, Image, ioctx, image_name + 'dne')
255 assert_raises(ImageNotFound, Image, ioctx, image_name, 'snap')
256
257 def test_open_readonly_dne():
258 for i in range(100):
259 image_name = get_temp_image_name()
260 assert_raises(ImageNotFound, Image, ioctx, image_name + 'dne',
261 read_only=True)
262 assert_raises(ImageNotFound, Image, ioctx, image_name, 'snap',
263 read_only=True)
264
265 def test_remove_dne():
266 assert_raises(ImageNotFound, remove_image)
267
268 def test_list_empty():
269 eq([], RBD().list(ioctx))
270
271 @with_setup(create_image, remove_image)
272 def test_list():
273 eq([image_name], RBD().list(ioctx))
274
275 @with_setup(create_image, remove_image)
276 def test_rename():
277 rbd = RBD()
278 image_name2 = get_temp_image_name()
279 rbd.rename(ioctx, image_name, image_name2)
280 eq([image_name2], rbd.list(ioctx))
281 rbd.rename(ioctx, image_name2, image_name)
282 eq([image_name], rbd.list(ioctx))
283
284 def rand_data(size):
285 return os.urandom(size)
286
287 def check_stat(info, size, order):
288 assert 'block_name_prefix' in info
289 eq(info['size'], size)
290 eq(info['order'], order)
291 eq(info['num_objs'], size // (1 << order))
292 eq(info['obj_size'], 1 << order)
293
294 class TestImage(object):
295
296 def setUp(self):
297 self.rbd = RBD()
298 create_image()
299 self.image = Image(ioctx, image_name)
300
301 def tearDown(self):
302 self.image.close()
303 remove_image()
304 self.image = None
305
306 @require_new_format()
307 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK])
308 def test_update_features(self):
309 features = self.image.features()
310 self.image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, True)
311 eq(features | RBD_FEATURE_EXCLUSIVE_LOCK, self.image.features())
312
313 @require_features([RBD_FEATURE_STRIPINGV2])
314 def test_create_with_params(self):
315 global features
316 image_name = get_temp_image_name()
317 order = 20
318 stripe_unit = 1 << 20
319 stripe_count = 10
320 self.rbd.create(ioctx, image_name, IMG_SIZE, order,
321 False, features, stripe_unit, stripe_count)
322 image = Image(ioctx, image_name)
323 info = image.stat()
324 check_stat(info, IMG_SIZE, order)
325 eq(image.features(), features)
326 eq(image.stripe_unit(), stripe_unit)
327 eq(image.stripe_count(), stripe_count)
328 image.close()
329 RBD().remove(ioctx, image_name)
330
331 @require_new_format()
332 def test_id(self):
333 assert_not_equal(b'', self.image.id())
334
335 def test_block_name_prefix(self):
336 assert_not_equal(b'', self.image.block_name_prefix())
337
338 def test_create_timestamp(self):
339 timestamp = self.image.create_timestamp()
340 assert_not_equal(0, timestamp.year)
341 assert_not_equal(1970, timestamp.year)
342
343 def test_invalidate_cache(self):
344 self.image.write(b'abc', 0)
345 eq(b'abc', self.image.read(0, 3))
346 self.image.invalidate_cache()
347 eq(b'abc', self.image.read(0, 3))
348
349 def test_stat(self):
350 info = self.image.stat()
351 check_stat(info, IMG_SIZE, IMG_ORDER)
352
353 def test_flags(self):
354 flags = self.image.flags()
355 eq(0, flags)
356
357 def test_image_auto_close(self):
358 image = Image(ioctx, image_name)
359
360 def test_write(self):
361 data = rand_data(256)
362 self.image.write(data, 0)
363
364 def test_write_with_fadvise_flags(self):
365 data = rand_data(256)
366 self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED)
367 self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_NOCACHE)
368
369 def test_read(self):
370 data = self.image.read(0, 20)
371 eq(data, b'\0' * 20)
372
373 def test_read_with_fadvise_flags(self):
374 data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED)
375 eq(data, b'\0' * 20)
376 data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM)
377 eq(data, b'\0' * 20)
378
379 def test_large_write(self):
380 data = rand_data(IMG_SIZE)
381 self.image.write(data, 0)
382
383 def test_large_read(self):
384 data = self.image.read(0, IMG_SIZE)
385 eq(data, b'\0' * IMG_SIZE)
386
387 def test_write_read(self):
388 data = rand_data(256)
389 offset = 50
390 self.image.write(data, offset)
391 read = self.image.read(offset, 256)
392 eq(data, read)
393
394 def test_read_bad_offset(self):
395 assert_raises(InvalidArgument, self.image.read, IMG_SIZE + 1, IMG_SIZE)
396
397 def test_resize(self):
398 new_size = IMG_SIZE * 2
399 self.image.resize(new_size)
400 info = self.image.stat()
401 check_stat(info, new_size, IMG_ORDER)
402
403 def test_size(self):
404 eq(IMG_SIZE, self.image.size())
405 self.image.create_snap('snap1')
406 new_size = IMG_SIZE * 2
407 self.image.resize(new_size)
408 eq(new_size, self.image.size())
409 self.image.create_snap('snap2')
410 self.image.set_snap('snap2')
411 eq(new_size, self.image.size())
412 self.image.set_snap('snap1')
413 eq(IMG_SIZE, self.image.size())
414 self.image.set_snap(None)
415 eq(new_size, self.image.size())
416 self.image.remove_snap('snap1')
417 self.image.remove_snap('snap2')
418
419 def test_resize_down(self):
420 new_size = IMG_SIZE // 2
421 data = rand_data(256)
422 self.image.write(data, IMG_SIZE // 2);
423 self.image.resize(new_size)
424 self.image.resize(IMG_SIZE)
425 read = self.image.read(IMG_SIZE // 2, 256)
426 eq(b'\0' * 256, read)
427
428 def test_resize_bytes(self):
429 new_size = IMG_SIZE // 2 - 5
430 data = rand_data(256)
431 self.image.write(data, IMG_SIZE // 2 - 10);
432 self.image.resize(new_size)
433 self.image.resize(IMG_SIZE)
434 read = self.image.read(IMG_SIZE // 2 - 10, 5)
435 eq(data[:5], read)
436 read = self.image.read(IMG_SIZE // 2 - 5, 251)
437 eq(b'\0' * 251, read)
438
439 def _test_copy(self, features=None, order=None, stripe_unit=None,
440 stripe_count=None):
441 global ioctx
442 data = rand_data(256)
443 self.image.write(data, 256)
444 image_name = get_temp_image_name()
445 if features is None:
446 self.image.copy(ioctx, image_name)
447 elif order is None:
448 self.image.copy(ioctx, image_name, features)
449 elif stripe_unit is None:
450 self.image.copy(ioctx, image_name, features, order)
451 elif stripe_count is None:
452 self.image.copy(ioctx, image_name, features, order, stripe_unit)
453 else:
454 self.image.copy(ioctx, image_name, features, order, stripe_unit,
455 stripe_count)
456 assert_raises(ImageExists, self.image.copy, ioctx, image_name)
457 copy = Image(ioctx, image_name)
458 copy_data = copy.read(256, 256)
459 copy.close()
460 self.rbd.remove(ioctx, image_name)
461 eq(data, copy_data)
462
463 def test_copy(self):
464 self._test_copy()
465
466 def test_copy2(self):
467 self._test_copy(self.image.features(), self.image.stat()['order'])
468
469 @require_features([RBD_FEATURE_STRIPINGV2])
470 def test_copy3(self):
471 global features
472 self._test_copy(features, self.image.stat()['order'],
473 self.image.stripe_unit(), self.image.stripe_count())
474
475 def test_create_snap(self):
476 global ioctx
477 self.image.create_snap('snap1')
478 read = self.image.read(0, 256)
479 eq(read, b'\0' * 256)
480 data = rand_data(256)
481 self.image.write(data, 0)
482 read = self.image.read(0, 256)
483 eq(read, data)
484 at_snapshot = Image(ioctx, image_name, 'snap1')
485 snap_data = at_snapshot.read(0, 256)
486 at_snapshot.close()
487 eq(snap_data, b'\0' * 256)
488 self.image.remove_snap('snap1')
489
490 def test_list_snaps(self):
491 eq([], list(self.image.list_snaps()))
492 self.image.create_snap('snap1')
493 eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
494 self.image.create_snap('snap2')
495 eq(['snap1', 'snap2'], [snap['name'] for snap in self.image.list_snaps()])
496 self.image.remove_snap('snap1')
497 self.image.remove_snap('snap2')
498
499 def test_list_snaps_iterator_auto_close(self):
500 self.image.create_snap('snap1')
501 self.image.list_snaps()
502 self.image.remove_snap('snap1')
503
504 def test_remove_snap(self):
505 eq([], list(self.image.list_snaps()))
506 self.image.create_snap('snap1')
507 eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
508 self.image.remove_snap('snap1')
509 eq([], list(self.image.list_snaps()))
510
511 def test_rename_snap(self):
512 eq([], list(self.image.list_snaps()))
513 self.image.create_snap('snap1')
514 eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
515 self.image.rename_snap("snap1", "snap1-rename")
516 eq(['snap1-rename'], [snap['name'] for snap in self.image.list_snaps()])
517 self.image.remove_snap('snap1-rename')
518 eq([], list(self.image.list_snaps()))
519
520 @require_features([RBD_FEATURE_LAYERING])
521 def test_protect_snap(self):
522 self.image.create_snap('snap1')
523 assert(not self.image.is_protected_snap('snap1'))
524 self.image.protect_snap('snap1')
525 assert(self.image.is_protected_snap('snap1'))
526 assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
527 self.image.unprotect_snap('snap1')
528 assert(not self.image.is_protected_snap('snap1'))
529 self.image.remove_snap('snap1')
530 assert_raises(ImageNotFound, self.image.unprotect_snap, 'snap1')
531 assert_raises(ImageNotFound, self.image.is_protected_snap, 'snap1')
532
533 def test_snap_timestamp(self):
534 self.image.create_snap('snap1')
535 eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
536 for snap in self.image.list_snaps():
537 snap_id = snap["id"]
538 time = self.image.get_snap_timestamp(snap_id)
539 assert_not_equal(b'', time.year)
540 assert_not_equal(0, time.year)
541 assert_not_equal(time.year, '1970')
542 self.image.remove_snap('snap1')
543
544 def test_limit_snaps(self):
545 self.image.set_snap_limit(2)
546 eq(2, self.image.get_snap_limit())
547 self.image.create_snap('snap1')
548 self.image.create_snap('snap2')
549 assert_raises(DiskQuotaExceeded, self.image.create_snap, 'snap3')
550 self.image.remove_snap_limit()
551 self.image.create_snap('snap3')
552
553 self.image.remove_snap('snap1')
554 self.image.remove_snap('snap2')
555 self.image.remove_snap('snap3')
556
557 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK])
558 def test_remove_with_exclusive_lock(self):
559 assert_raises(ImageBusy, remove_image)
560
561 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK])
562 def test_remove_with_snap(self):
563 self.image.create_snap('snap1')
564 assert_raises(ImageHasSnapshots, remove_image)
565 self.image.remove_snap('snap1')
566
567 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK])
568 def test_remove_with_watcher(self):
569 data = rand_data(256)
570 self.image.write(data, 0)
571 assert_raises(ImageBusy, remove_image)
572 read = self.image.read(0, 256)
573 eq(read, data)
574
575 def test_rollback_to_snap(self):
576 self.image.write(b'\0' * 256, 0)
577 self.image.create_snap('snap1')
578 read = self.image.read(0, 256)
579 eq(read, b'\0' * 256)
580 data = rand_data(256)
581 self.image.write(data, 0)
582 read = self.image.read(0, 256)
583 eq(read, data)
584 self.image.rollback_to_snap('snap1')
585 read = self.image.read(0, 256)
586 eq(read, b'\0' * 256)
587 self.image.remove_snap('snap1')
588
589 def test_rollback_to_snap_sparse(self):
590 self.image.create_snap('snap1')
591 read = self.image.read(0, 256)
592 eq(read, b'\0' * 256)
593 data = rand_data(256)
594 self.image.write(data, 0)
595 read = self.image.read(0, 256)
596 eq(read, data)
597 self.image.rollback_to_snap('snap1')
598 read = self.image.read(0, 256)
599 eq(read, b'\0' * 256)
600 self.image.remove_snap('snap1')
601
602 def test_rollback_with_resize(self):
603 read = self.image.read(0, 256)
604 eq(read, b'\0' * 256)
605 data = rand_data(256)
606 self.image.write(data, 0)
607 self.image.create_snap('snap1')
608 read = self.image.read(0, 256)
609 eq(read, data)
610 new_size = IMG_SIZE * 2
611 self.image.resize(new_size)
612 check_stat(self.image.stat(), new_size, IMG_ORDER)
613 self.image.write(data, new_size - 256)
614 self.image.create_snap('snap2')
615 read = self.image.read(new_size - 256, 256)
616 eq(read, data)
617 self.image.rollback_to_snap('snap1')
618 check_stat(self.image.stat(), IMG_SIZE, IMG_ORDER)
619 assert_raises(InvalidArgument, self.image.read, new_size - 256, 256)
620 self.image.rollback_to_snap('snap2')
621 check_stat(self.image.stat(), new_size, IMG_ORDER)
622 read = self.image.read(new_size - 256, 256)
623 eq(read, data)
624 self.image.remove_snap('snap1')
625 self.image.remove_snap('snap2')
626
627 def test_set_snap(self):
628 self.image.write(b'\0' * 256, 0)
629 self.image.create_snap('snap1')
630 read = self.image.read(0, 256)
631 eq(read, b'\0' * 256)
632 data = rand_data(256)
633 self.image.write(data, 0)
634 read = self.image.read(0, 256)
635 eq(read, data)
636 self.image.set_snap('snap1')
637 read = self.image.read(0, 256)
638 eq(read, b'\0' * 256)
639 self.image.remove_snap('snap1')
640
641 def test_set_no_snap(self):
642 self.image.write(b'\0' * 256, 0)
643 self.image.create_snap('snap1')
644 read = self.image.read(0, 256)
645 eq(read, b'\0' * 256)
646 data = rand_data(256)
647 self.image.write(data, 0)
648 read = self.image.read(0, 256)
649 eq(read, data)
650 self.image.set_snap('snap1')
651 read = self.image.read(0, 256)
652 eq(read, b'\0' * 256)
653 self.image.set_snap(None)
654 read = self.image.read(0, 256)
655 eq(read, data)
656 self.image.remove_snap('snap1')
657
658 def test_set_snap_sparse(self):
659 self.image.create_snap('snap1')
660 read = self.image.read(0, 256)
661 eq(read, b'\0' * 256)
662 data = rand_data(256)
663 self.image.write(data, 0)
664 read = self.image.read(0, 256)
665 eq(read, data)
666 self.image.set_snap('snap1')
667 read = self.image.read(0, 256)
668 eq(read, b'\0' * 256)
669 self.image.remove_snap('snap1')
670
671 def test_many_snaps(self):
672 num_snaps = 200
673 for i in range(num_snaps):
674 self.image.create_snap(str(i))
675 snaps = sorted(self.image.list_snaps(),
676 key=lambda snap: int(snap['name']))
677 eq(len(snaps), num_snaps)
678 for i, snap in enumerate(snaps):
679 eq(snap['size'], IMG_SIZE)
680 eq(snap['name'], str(i))
681 for i in range(num_snaps):
682 self.image.remove_snap(str(i))
683
684 def test_set_snap_deleted(self):
685 self.image.write(b'\0' * 256, 0)
686 self.image.create_snap('snap1')
687 read = self.image.read(0, 256)
688 eq(read, b'\0' * 256)
689 data = rand_data(256)
690 self.image.write(data, 0)
691 read = self.image.read(0, 256)
692 eq(read, data)
693 self.image.set_snap('snap1')
694 self.image.remove_snap('snap1')
695 assert_raises(ImageNotFound, self.image.read, 0, 256)
696 self.image.set_snap(None)
697 read = self.image.read(0, 256)
698 eq(read, data)
699
700 def test_set_snap_recreated(self):
701 self.image.write(b'\0' * 256, 0)
702 self.image.create_snap('snap1')
703 read = self.image.read(0, 256)
704 eq(read, b'\0' * 256)
705 data = rand_data(256)
706 self.image.write(data, 0)
707 read = self.image.read(0, 256)
708 eq(read, data)
709 self.image.set_snap('snap1')
710 self.image.remove_snap('snap1')
711 self.image.create_snap('snap1')
712 assert_raises(ImageNotFound, self.image.read, 0, 256)
713 self.image.set_snap(None)
714 read = self.image.read(0, 256)
715 eq(read, data)
716 self.image.remove_snap('snap1')
717
718 def test_lock_unlock(self):
719 assert_raises(ImageNotFound, self.image.unlock, '')
720 self.image.lock_exclusive('')
721 assert_raises(ImageExists, self.image.lock_exclusive, '')
722 assert_raises(ImageBusy, self.image.lock_exclusive, 'test')
723 assert_raises(ImageExists, self.image.lock_shared, '', '')
724 assert_raises(ImageBusy, self.image.lock_shared, 'foo', '')
725 self.image.unlock('')
726
727 def test_list_lockers(self):
728 eq([], self.image.list_lockers())
729 self.image.lock_exclusive('test')
730 lockers = self.image.list_lockers()
731 eq(1, len(lockers['lockers']))
732 _, cookie, _ = lockers['lockers'][0]
733 eq(cookie, 'test')
734 eq('', lockers['tag'])
735 assert lockers['exclusive']
736 self.image.unlock('test')
737 eq([], self.image.list_lockers())
738
739 num_shared = 10
740 for i in range(num_shared):
741 self.image.lock_shared(str(i), 'tag')
742 lockers = self.image.list_lockers()
743 eq('tag', lockers['tag'])
744 assert not lockers['exclusive']
745 eq(num_shared, len(lockers['lockers']))
746 cookies = sorted(map(lambda x: x[1], lockers['lockers']))
747 for i in range(num_shared):
748 eq(str(i), cookies[i])
749 self.image.unlock(str(i))
750 eq([], self.image.list_lockers())
751
752 def test_diff_iterate(self):
753 check_diff(self.image, 0, IMG_SIZE, None, [])
754 self.image.write(b'a' * 256, 0)
755 check_diff(self.image, 0, IMG_SIZE, None, [(0, 256, True)])
756 self.image.write(b'b' * 256, 256)
757 check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)])
758 self.image.discard(128, 256)
759 check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)])
760
761 self.image.create_snap('snap1')
762 self.image.discard(0, 1 << IMG_ORDER)
763 self.image.create_snap('snap2')
764 self.image.set_snap('snap2')
765 check_diff(self.image, 0, IMG_SIZE, 'snap1', [(0, 512, False)])
766 self.image.remove_snap('snap1')
767 self.image.remove_snap('snap2')
768
769 def test_aio_read(self):
770 # this is a list so that the local cb() can modify it
771 retval = [None]
772 def cb(_, buf):
773 retval[0] = buf
774
775 # test1: success case
776 comp = self.image.aio_read(0, 20, cb)
777 comp.wait_for_complete_and_cb()
778 eq(retval[0], b'\0' * 20)
779 eq(comp.get_return_value(), 20)
780 eq(sys.getrefcount(comp), 2)
781
782 # test2: error case
783 retval[0] = 1
784 comp = self.image.aio_read(IMG_SIZE, 20, cb)
785 comp.wait_for_complete_and_cb()
786 eq(None, retval[0])
787 assert(comp.get_return_value() < 0)
788 eq(sys.getrefcount(comp), 2)
789
790 def test_aio_write(self):
791 retval = [None]
792 def cb(comp):
793 retval[0] = comp.get_return_value()
794
795 data = rand_data(256)
796 comp = self.image.aio_write(data, 256, cb)
797 comp.wait_for_complete_and_cb()
798 eq(retval[0], 0)
799 eq(comp.get_return_value(), 0)
800 eq(sys.getrefcount(comp), 2)
801 eq(self.image.read(256, 256), data)
802
803 def test_aio_discard(self):
804 retval = [None]
805 def cb(comp):
806 retval[0] = comp.get_return_value()
807
808 data = rand_data(256)
809 self.image.write(data, 0)
810 comp = self.image.aio_discard(0, 256, cb)
811 comp.wait_for_complete_and_cb()
812 eq(retval[0], 0)
813 eq(comp.get_return_value(), 0)
814 eq(sys.getrefcount(comp), 2)
815 eq(self.image.read(256, 256), b'\0' * 256)
816
817 def test_aio_flush(self):
818 retval = [None]
819 def cb(comp):
820 retval[0] = comp.get_return_value()
821
822 comp = self.image.aio_flush(cb)
823 comp.wait_for_complete_and_cb()
824 eq(retval[0], 0)
825 eq(sys.getrefcount(comp), 2)
826
827 def test_metadata(self):
828 metadata = list(self.image.metadata_list())
829 eq(len(metadata), 0)
830 self.image.metadata_set("key1", "value1")
831 self.image.metadata_set("key2", "value2")
832 value = self.image.metadata_get("key1")
833 eq(value, "value1")
834 value = self.image.metadata_get("key2")
835 eq(value, "value2")
836 metadata = list(self.image.metadata_list())
837 eq(len(metadata), 2)
838 self.image.metadata_remove("key1")
839 metadata = list(self.image.metadata_list())
840 eq(len(metadata), 1)
841 eq(metadata[0], ("key2", "value2"))
842 self.image.metadata_remove("key2")
843 metadata = list(self.image.metadata_list())
844 eq(len(metadata), 0)
845
846 N = 65
847 for i in xrange(N):
848 self.image.metadata_set("key" + str(i), "X" * 1025)
849 metadata = list(self.image.metadata_list())
850 eq(len(metadata), N)
851 for i in xrange(N):
852 self.image.metadata_remove("key" + str(i))
853 metadata = list(self.image.metadata_list())
854 eq(len(metadata), N - i - 1)
855
856 def check_diff(image, offset, length, from_snapshot, expected):
857 extents = []
858 def cb(offset, length, exists):
859 extents.append((offset, length, exists))
860 image.diff_iterate(0, IMG_SIZE, None, cb)
861 eq(extents, expected)
862
863 class TestClone(object):
864
865 @require_features([RBD_FEATURE_LAYERING])
866 def setUp(self):
867 global ioctx
868 global features
869 self.rbd = RBD()
870 create_image()
871 self.image = Image(ioctx, image_name)
872 data = rand_data(256)
873 self.image.write(data, IMG_SIZE // 2)
874 self.image.create_snap('snap1')
875 global features
876 self.image.protect_snap('snap1')
877 self.clone_name = get_temp_image_name()
878 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name,
879 features)
880 self.clone = Image(ioctx, self.clone_name)
881
882 def tearDown(self):
883 global ioctx
884 self.clone.close()
885 self.rbd.remove(ioctx, self.clone_name)
886 self.image.unprotect_snap('snap1')
887 self.image.remove_snap('snap1')
888 self.image.close()
889 remove_image()
890
891 def _test_with_params(self, features=None, order=None, stripe_unit=None,
892 stripe_count=None):
893 self.image.create_snap('snap2')
894 self.image.protect_snap('snap2')
895 clone_name2 = get_temp_image_name()
896 if features is None:
897 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2)
898 elif order is None:
899 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
900 features)
901 elif stripe_unit is None:
902 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
903 features, order)
904 elif stripe_count is None:
905 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
906 features, order, stripe_unit)
907 else:
908 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
909 features, order, stripe_unit, stripe_count)
910 self.rbd.remove(ioctx, clone_name2)
911 self.image.unprotect_snap('snap2')
912 self.image.remove_snap('snap2')
913
914 def test_with_params(self):
915 self._test_with_params()
916
917 def test_with_params2(self):
918 global features
919 self._test_with_params(features, self.image.stat()['order'])
920
921 @require_features([RBD_FEATURE_STRIPINGV2])
922 def test_with_params3(self):
923 global features
924 self._test_with_params(features, self.image.stat()['order'],
925 self.image.stripe_unit(),
926 self.image.stripe_count())
927
928 def test_unprotected(self):
929 self.image.create_snap('snap2')
930 global features
931 clone_name2 = get_temp_image_name()
932 assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name,
933 'snap2', ioctx, clone_name2, features)
934 self.image.remove_snap('snap2')
935
936 def test_unprotect_with_children(self):
937 global features
938 # can't remove a snapshot that has dependent clones
939 assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
940
941 # validate parent info of clone created by TestClone.setUp
942 (pool, image, snap) = self.clone.parent_info()
943 eq(pool, pool_name)
944 eq(image, image_name)
945 eq(snap, 'snap1')
946 eq(self.image.id(), self.clone.parent_id())
947
948 # create a new pool...
949 pool_name2 = get_temp_pool_name()
950 rados.create_pool(pool_name2)
951 other_ioctx = rados.open_ioctx(pool_name2)
952
953 # ...with a clone of the same parent
954 other_clone_name = get_temp_image_name()
955 self.rbd.clone(ioctx, image_name, 'snap1', other_ioctx,
956 other_clone_name, features)
957 self.other_clone = Image(other_ioctx, other_clone_name)
958 # validate its parent info
959 (pool, image, snap) = self.other_clone.parent_info()
960 eq(pool, pool_name)
961 eq(image, image_name)
962 eq(snap, 'snap1')
963 eq(self.image.id(), self.other_clone.parent_id())
964
965 # can't unprotect snap with children
966 assert_raises(ImageBusy, self.image.unprotect_snap, 'snap1')
967
968 # 2 children, check that cannot remove the parent snap
969 assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
970
971 # close and remove other pool's clone
972 self.other_clone.close()
973 self.rbd.remove(other_ioctx, other_clone_name)
974
975 # check that we cannot yet remove the parent snap
976 assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
977
978 other_ioctx.close()
979 rados.delete_pool(pool_name2)
980
981 # unprotect, remove parent snap happen in cleanup, and should succeed
982
983 def test_stat(self):
984 image_info = self.image.stat()
985 clone_info = self.clone.stat()
986 eq(clone_info['size'], image_info['size'])
987 eq(clone_info['size'], self.clone.overlap())
988
989 def test_resize_stat(self):
990 self.clone.resize(IMG_SIZE // 2)
991 image_info = self.image.stat()
992 clone_info = self.clone.stat()
993 eq(clone_info['size'], IMG_SIZE // 2)
994 eq(image_info['size'], IMG_SIZE)
995 eq(self.clone.overlap(), IMG_SIZE // 2)
996
997 self.clone.resize(IMG_SIZE * 2)
998 image_info = self.image.stat()
999 clone_info = self.clone.stat()
1000 eq(clone_info['size'], IMG_SIZE * 2)
1001 eq(image_info['size'], IMG_SIZE)
1002 eq(self.clone.overlap(), IMG_SIZE // 2)
1003
1004 def test_resize_io(self):
1005 parent_data = self.image.read(IMG_SIZE // 2, 256)
1006 self.image.resize(0)
1007 self.clone.resize(IMG_SIZE // 2 + 128)
1008 child_data = self.clone.read(IMG_SIZE // 2, 128)
1009 eq(child_data, parent_data[:128])
1010 self.clone.resize(IMG_SIZE)
1011 child_data = self.clone.read(IMG_SIZE // 2, 256)
1012 eq(child_data, parent_data[:128] + (b'\0' * 128))
1013 self.clone.resize(IMG_SIZE // 2 + 1)
1014 child_data = self.clone.read(IMG_SIZE // 2, 1)
1015 eq(child_data, parent_data[0:1])
1016 self.clone.resize(0)
1017 self.clone.resize(IMG_SIZE)
1018 child_data = self.clone.read(IMG_SIZE // 2, 256)
1019 eq(child_data, b'\0' * 256)
1020
1021 def test_read(self):
1022 parent_data = self.image.read(IMG_SIZE // 2, 256)
1023 child_data = self.clone.read(IMG_SIZE // 2, 256)
1024 eq(child_data, parent_data)
1025
1026 def test_write(self):
1027 parent_data = self.image.read(IMG_SIZE // 2, 256)
1028 new_data = rand_data(256)
1029 self.clone.write(new_data, IMG_SIZE // 2 + 256)
1030 child_data = self.clone.read(IMG_SIZE // 2 + 256, 256)
1031 eq(child_data, new_data)
1032 child_data = self.clone.read(IMG_SIZE // 2, 256)
1033 eq(child_data, parent_data)
1034 parent_data = self.image.read(IMG_SIZE // 2 + 256, 256)
1035 eq(parent_data, b'\0' * 256)
1036
1037 def check_children(self, expected):
1038 actual = self.image.list_children()
1039 # dedup for cache pools until
1040 # http://tracker.ceph.com/issues/8187 is fixed
1041 deduped = set([(pool_name, image[1]) for image in actual])
1042 eq(deduped, set(expected))
1043
1044 def test_list_children(self):
1045 global ioctx
1046 global features
1047 self.image.set_snap('snap1')
1048 self.check_children([(pool_name, self.clone_name)])
1049 self.clone.close()
1050 self.rbd.remove(ioctx, self.clone_name)
1051 eq(self.image.list_children(), [])
1052
1053 clone_name = get_temp_image_name() + '_'
1054 expected_children = []
1055 for i in range(10):
1056 self.rbd.clone(ioctx, image_name, 'snap1', ioctx,
1057 clone_name + str(i), features)
1058 expected_children.append((pool_name, clone_name + str(i)))
1059 self.check_children(expected_children)
1060
1061 for i in range(10):
1062 self.rbd.remove(ioctx, clone_name + str(i))
1063 expected_children.pop(0)
1064 self.check_children(expected_children)
1065
1066 eq(self.image.list_children(), [])
1067 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name,
1068 features)
1069 self.check_children([(pool_name, self.clone_name)])
1070 self.clone = Image(ioctx, self.clone_name)
1071
1072 def test_flatten_errors(self):
1073 # test that we can't flatten a non-clone
1074 assert_raises(InvalidArgument, self.image.flatten)
1075
1076 # test that we can't flatten a snapshot
1077 self.clone.create_snap('snap2')
1078 self.clone.set_snap('snap2')
1079 assert_raises(ReadOnlyImage, self.clone.flatten)
1080 self.clone.remove_snap('snap2')
1081
1082 def check_flatten_with_order(self, new_order):
1083 global ioctx
1084 global features
1085 clone_name2 = get_temp_image_name()
1086 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1087 features, new_order)
1088 #with Image(ioctx, 'clone2') as clone:
1089 clone2 = Image(ioctx, clone_name2)
1090 clone2.flatten()
1091 eq(clone2.overlap(), 0)
1092 clone2.close()
1093 self.rbd.remove(ioctx, clone_name2)
1094
1095 # flatten after resizing to non-block size
1096 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1097 features, new_order)
1098 with Image(ioctx, clone_name2) as clone:
1099 clone.resize(IMG_SIZE // 2 - 1)
1100 clone.flatten()
1101 eq(0, clone.overlap())
1102 self.rbd.remove(ioctx, clone_name2)
1103
1104 # flatten after resizing to non-block size
1105 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1106 features, new_order)
1107 with Image(ioctx, clone_name2) as clone:
1108 clone.resize(IMG_SIZE // 2 + 1)
1109 clone.flatten()
1110 eq(clone.overlap(), 0)
1111 self.rbd.remove(ioctx, clone_name2)
1112
1113 def test_flatten_basic(self):
1114 self.check_flatten_with_order(IMG_ORDER)
1115
1116 def test_flatten_smaller_order(self):
1117 self.check_flatten_with_order(IMG_ORDER - 2)
1118
1119 def test_flatten_larger_order(self):
1120 self.check_flatten_with_order(IMG_ORDER + 2)
1121
1122 def test_flatten_drops_cache(self):
1123 global ioctx
1124 global features
1125 clone_name2 = get_temp_image_name()
1126 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1127 features, IMG_ORDER)
1128 with Image(ioctx, clone_name2) as clone:
1129 with Image(ioctx, clone_name2) as clone2:
1130 # cache object non-existence
1131 data = clone.read(IMG_SIZE // 2, 256)
1132 clone2_data = clone2.read(IMG_SIZE // 2, 256)
1133 eq(data, clone2_data)
1134 clone.flatten()
1135 assert_raises(ImageNotFound, clone.parent_info)
1136 assert_raises(ImageNotFound, clone2.parent_info)
1137 assert_raises(ImageNotFound, clone.parent_id)
1138 assert_raises(ImageNotFound, clone2.parent_id)
1139 after_flatten = clone.read(IMG_SIZE // 2, 256)
1140 eq(data, after_flatten)
1141 after_flatten = clone2.read(IMG_SIZE // 2, 256)
1142 eq(data, after_flatten)
1143 self.rbd.remove(ioctx, clone_name2)
1144
1145 def test_flatten_multi_level(self):
1146 self.clone.create_snap('snap2')
1147 self.clone.protect_snap('snap2')
1148 clone_name3 = get_temp_image_name()
1149 self.rbd.clone(ioctx, self.clone_name, 'snap2', ioctx, clone_name3,
1150 features)
1151 self.clone.flatten()
1152 with Image(ioctx, clone_name3) as clone3:
1153 clone3.flatten()
1154 self.clone.unprotect_snap('snap2')
1155 self.clone.remove_snap('snap2')
1156 self.rbd.remove(ioctx, clone_name3)
1157
1158 def test_resize_flatten_multi_level(self):
1159 self.clone.create_snap('snap2')
1160 self.clone.protect_snap('snap2')
1161 clone_name3 = get_temp_image_name()
1162 self.rbd.clone(ioctx, self.clone_name, 'snap2', ioctx, clone_name3,
1163 features)
1164 self.clone.resize(1)
1165 orig_data = self.image.read(0, 256)
1166 with Image(ioctx, clone_name3) as clone3:
1167 clone3_data = clone3.read(0, 256)
1168 eq(orig_data, clone3_data)
1169 self.clone.flatten()
1170 with Image(ioctx, clone_name3) as clone3:
1171 clone3_data = clone3.read(0, 256)
1172 eq(orig_data, clone3_data)
1173 self.rbd.remove(ioctx, clone_name3)
1174 self.clone.unprotect_snap('snap2')
1175 self.clone.remove_snap('snap2')
1176
1177 class TestExclusiveLock(object):
1178
1179 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK])
1180 def setUp(self):
1181 global rados2
1182 rados2 = Rados(conffile='')
1183 rados2.connect()
1184 global ioctx2
1185 ioctx2 = rados2.open_ioctx(pool_name)
1186 create_image()
1187
1188 def tearDown(self):
1189 remove_image()
1190 global ioctx2
1191 ioctx2.close()
1192 global rados2
1193 rados2.shutdown()
1194
1195 def test_ownership(self):
1196 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1197 image1.write(b'0'*256, 0)
1198 eq(image1.is_exclusive_lock_owner(), True)
1199 eq(image2.is_exclusive_lock_owner(), False)
1200
1201 def test_snapshot_leadership(self):
1202 with Image(ioctx, image_name) as image:
1203 image.create_snap('snap')
1204 eq(image.is_exclusive_lock_owner(), True)
1205 try:
1206 with Image(ioctx, image_name) as image:
1207 image.write(b'0'*256, 0)
1208 eq(image.is_exclusive_lock_owner(), True)
1209 image.set_snap('snap')
1210 eq(image.is_exclusive_lock_owner(), False)
1211 with Image(ioctx, image_name, snapshot='snap') as image:
1212 eq(image.is_exclusive_lock_owner(), False)
1213 finally:
1214 with Image(ioctx, image_name) as image:
1215 image.remove_snap('snap')
1216
1217 def test_read_only_leadership(self):
1218 with Image(ioctx, image_name, read_only=True) as image:
1219 eq(image.is_exclusive_lock_owner(), False)
1220
1221 def test_follower_flatten(self):
1222 with Image(ioctx, image_name) as image:
1223 image.create_snap('snap')
1224 image.protect_snap('snap')
1225 try:
1226 RBD().clone(ioctx, image_name, 'snap', ioctx, 'clone', features)
1227 with Image(ioctx, 'clone') as image1, Image(ioctx2, 'clone') as image2:
1228 data = rand_data(256)
1229 image1.write(data, 0)
1230 image2.flatten()
1231 assert_raises(ImageNotFound, image1.parent_info)
1232 assert_raises(ImageNotFound, image1.parent_id)
1233 parent = True
1234 for x in range(30):
1235 try:
1236 image2.parent_info()
1237 except ImageNotFound:
1238 parent = False
1239 break
1240 eq(False, parent)
1241 finally:
1242 RBD().remove(ioctx, 'clone')
1243 with Image(ioctx, image_name) as image:
1244 image.unprotect_snap('snap')
1245 image.remove_snap('snap')
1246
1247 def test_follower_resize(self):
1248 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1249 image1.write(b'0'*256, 0)
1250 for new_size in [IMG_SIZE * 2, IMG_SIZE // 2]:
1251 image2.resize(new_size);
1252 eq(new_size, image1.size())
1253 for x in range(30):
1254 if new_size == image2.size():
1255 break
1256 time.sleep(1)
1257 eq(new_size, image2.size())
1258
1259 def test_follower_snap_create(self):
1260 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1261 image2.create_snap('snap1')
1262 image1.remove_snap('snap1')
1263
1264 def test_follower_snap_rollback(self):
1265 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1266 image1.create_snap('snap')
1267 try:
1268 assert_raises(ReadOnlyImage, image2.rollback_to_snap, 'snap')
1269 image1.rollback_to_snap('snap')
1270 finally:
1271 image1.remove_snap('snap')
1272
1273 def test_follower_discard(self):
1274 global rados
1275 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1276 data = rand_data(256)
1277 image1.write(data, 0)
1278 image2.discard(0, 256)
1279 eq(image1.is_exclusive_lock_owner(), False)
1280 eq(image2.is_exclusive_lock_owner(), True)
1281 read = image2.read(0, 256)
1282 if rados.conf_get('rbd_skip_partial_discard') == 'false':
1283 eq(256 * b'\0', read)
1284 else:
1285 eq(data, read)
1286
1287 def test_follower_write(self):
1288 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1289 data = rand_data(256)
1290 image1.write(data, 0)
1291 image2.write(data, IMG_SIZE // 2)
1292 eq(image1.is_exclusive_lock_owner(), False)
1293 eq(image2.is_exclusive_lock_owner(), True)
1294 for offset in [0, IMG_SIZE // 2]:
1295 read = image2.read(offset, 256)
1296 eq(data, read)
1297 def test_acquire_release_lock(self):
1298 with Image(ioctx, image_name) as image:
1299 image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
1300 image.lock_release()
1301
1302 def test_break_lock(self):
1303 blacklist_rados = Rados(conffile='')
1304 blacklist_rados.connect()
1305 try:
1306 blacklist_ioctx = blacklist_rados.open_ioctx(pool_name)
1307 try:
1308 rados2.conf_set('rbd_blacklist_on_break_lock', 'true')
1309 with Image(ioctx2, image_name) as image, \
1310 Image(blacklist_ioctx, image_name) as blacklist_image:
1311 blacklist_image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
1312 assert_raises(ReadOnlyImage, image.lock_acquire,
1313 RBD_LOCK_MODE_EXCLUSIVE)
1314
1315 lock_owners = list(image.lock_get_owners())
1316 eq(1, len(lock_owners))
1317 eq(RBD_LOCK_MODE_EXCLUSIVE, lock_owners[0]['mode'])
1318 image.lock_break(RBD_LOCK_MODE_EXCLUSIVE,
1319 lock_owners[0]['owner'])
1320
1321 assert_raises(ConnectionShutdown,
1322 blacklist_image.is_exclusive_lock_owner)
1323
1324 blacklist_rados.wait_for_latest_osdmap()
1325 data = rand_data(256)
1326 assert_raises(ConnectionShutdown,
1327 blacklist_image.write, data, 0)
1328
1329 image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
1330
1331 try:
1332 blacklist_image.close()
1333 except ConnectionShutdown:
1334 pass
1335 finally:
1336 blacklist_ioctx.close()
1337 finally:
1338 blacklist_rados.shutdown()
1339
1340 class TestMirroring(object):
1341
1342 @staticmethod
1343 def check_info(info, global_id, state, primary=None):
1344 eq(global_id, info['global_id'])
1345 eq(state, info['state'])
1346 if primary is not None:
1347 eq(primary, info['primary'])
1348
1349 def setUp(self):
1350 self.rbd = RBD()
1351 self.initial_mirror_mode = self.rbd.mirror_mode_get(ioctx)
1352 self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL)
1353 create_image()
1354 self.image = Image(ioctx, image_name)
1355
1356 def tearDown(self):
1357 self.image.close()
1358 remove_image()
1359 self.rbd.mirror_mode_set(ioctx, self.initial_mirror_mode)
1360
1361
1362 def test_mirror_peer(self):
1363 eq([], list(self.rbd.mirror_peer_list(ioctx)))
1364 cluster_name = "test_cluster"
1365 client_name = "test_client"
1366 uuid = self.rbd.mirror_peer_add(ioctx, cluster_name, client_name)
1367 assert(uuid)
1368 peer = {
1369 'uuid' : uuid,
1370 'cluster_name' : cluster_name,
1371 'client_name' : client_name,
1372 }
1373 eq([peer], list(self.rbd.mirror_peer_list(ioctx)))
1374 cluster_name = "test_cluster1"
1375 self.rbd.mirror_peer_set_cluster(ioctx, uuid, cluster_name)
1376 client_name = "test_client1"
1377 self.rbd.mirror_peer_set_client(ioctx, uuid, client_name)
1378 peer = {
1379 'uuid' : uuid,
1380 'cluster_name' : cluster_name,
1381 'client_name' : client_name,
1382 }
1383 eq([peer], list(self.rbd.mirror_peer_list(ioctx)))
1384 self.rbd.mirror_peer_remove(ioctx, uuid)
1385 eq([], list(self.rbd.mirror_peer_list(ioctx)))
1386
1387 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK,
1388 RBD_FEATURE_JOURNALING])
1389 def test_mirror_image(self):
1390
1391 self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE)
1392 self.image.mirror_image_disable(True)
1393 info = self.image.mirror_image_get_info()
1394 self.check_info(info, '', RBD_MIRROR_IMAGE_DISABLED, False)
1395
1396 self.image.mirror_image_enable()
1397 info = self.image.mirror_image_get_info()
1398 global_id = info['global_id']
1399 self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
1400
1401 self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL)
1402 fail = False
1403 try:
1404 self.image.mirror_image_disable(True)
1405 except InvalidArgument:
1406 fail = True
1407 eq(True, fail) # Fails because of mirror mode pool
1408
1409 self.image.mirror_image_demote()
1410 info = self.image.mirror_image_get_info()
1411 self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, False)
1412
1413 self.image.mirror_image_resync()
1414
1415 self.image.mirror_image_promote(True)
1416 info = self.image.mirror_image_get_info()
1417 self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
1418
1419 fail = False
1420 try:
1421 self.image.mirror_image_resync()
1422 except InvalidArgument:
1423 fail = True
1424 eq(True, fail) # Fails because it is primary
1425
1426 status = self.image.mirror_image_get_status()
1427 eq(image_name, status['name'])
1428 eq(False, status['up'])
1429 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
1430 info = status['info']
1431 self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
1432
1433 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK,
1434 RBD_FEATURE_JOURNALING])
1435 def test_mirror_image_status(self):
1436 info = self.image.mirror_image_get_info()
1437 global_id = info['global_id']
1438 state = info['state']
1439 primary = info['primary']
1440
1441 status = self.image.mirror_image_get_status()
1442 eq(image_name, status['name'])
1443 eq(False, status['up'])
1444 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
1445 info = status['info']
1446 self.check_info(info, global_id, state, primary)
1447
1448 images = list(self.rbd.mirror_image_status_list(ioctx))
1449 eq(1, len(images))
1450 status = images[0]
1451 eq(image_name, status['name'])
1452 eq(False, status['up'])
1453 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
1454 info = status['info']
1455 self.check_info(info, global_id, state)
1456
1457 states = self.rbd.mirror_image_status_summary(ioctx)
1458 eq([(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, 1)], states)
1459
1460 N = 65
1461 for i in range(N):
1462 self.rbd.create(ioctx, image_name + str(i), IMG_SIZE, IMG_ORDER,
1463 old_format=False, features=int(features))
1464 images = list(self.rbd.mirror_image_status_list(ioctx))
1465 eq(N + 1, len(images))
1466 for i in range(N):
1467 self.rbd.remove(ioctx, image_name + str(i))
1468
1469
1470 class TestTrash(object):
1471
1472 def setUp(self):
1473 global rados2
1474 rados2 = Rados(conffile='')
1475 rados2.connect()
1476 global ioctx2
1477 ioctx2 = rados2.open_ioctx(pool_name)
1478
1479 def tearDown(self):
1480 global ioctx2
1481 ioctx2.close()
1482 global rados2
1483 rados2.shutdown()
1484
1485 def test_move(self):
1486 create_image()
1487 with Image(ioctx, image_name) as image:
1488 image_id = image.id()
1489
1490 RBD().trash_move(ioctx, image_name, 1000)
1491 RBD().trash_remove(ioctx, image_id, True)
1492
1493 def test_remove_denied(self):
1494 create_image()
1495 with Image(ioctx, image_name) as image:
1496 image_id = image.id()
1497
1498 RBD().trash_move(ioctx, image_name, 1000)
1499 assert_raises(PermissionError, RBD().trash_remove, ioctx, image_id)
1500
1501 def test_remove(self):
1502 create_image()
1503 with Image(ioctx, image_name) as image:
1504 image_id = image.id()
1505
1506 RBD().trash_move(ioctx, image_name, 0)
1507 RBD().trash_remove(ioctx, image_id)
1508
1509 def test_get(self):
1510 create_image()
1511 with Image(ioctx, image_name) as image:
1512 image_id = image.id()
1513
1514 RBD().trash_move(ioctx, image_name, 1000)
1515
1516 info = RBD().trash_get(ioctx, image_id)
1517 eq(image_id, info['id'])
1518 eq(image_name, info['name'])
1519 eq('USER', info['source'])
1520 assert(info['deferment_end_time'] > info['deletion_time'])
1521
1522 RBD().trash_remove(ioctx, image_id, True)
1523
1524 def test_list(self):
1525 create_image()
1526 with Image(ioctx, image_name) as image:
1527 image_id1 = image.id()
1528 image_name1 = image_name
1529 RBD().trash_move(ioctx, image_name, 1000)
1530
1531 create_image()
1532 with Image(ioctx, image_name) as image:
1533 image_id2 = image.id()
1534 image_name2 = image_name
1535 RBD().trash_move(ioctx, image_name, 1000)
1536
1537 entries = list(RBD().trash_list(ioctx))
1538 for e in entries:
1539 if e['id'] == image_id1:
1540 eq(e['name'], image_name1)
1541 elif e['id'] == image_id2:
1542 eq(e['name'], image_name2)
1543 else:
1544 assert False
1545 eq(e['source'], 'USER')
1546 assert e['deferment_end_time'] > e['deletion_time']
1547
1548 RBD().trash_remove(ioctx, image_id1, True)
1549 RBD().trash_remove(ioctx, image_id2, True)
1550
1551 def test_restore(self):
1552 create_image()
1553 with Image(ioctx, image_name) as image:
1554 image_id = image.id()
1555 RBD().trash_move(ioctx, image_name, 1000)
1556 RBD().trash_restore(ioctx, image_id, image_name)
1557 remove_image()