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