]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/pybind/test_rbd.py
import 14.2.4 nautilus point release
[ceph.git] / ceph / src / test / pybind / test_rbd.py
1 # vim: expandtab smarttab shiftwidth=4 softtabstop=4
2 import errno
3 import functools
4 import socket
5 import os
6 import time
7 import sys
8
9 from datetime import datetime, timedelta
10 from nose import with_setup, SkipTest
11 from nose.tools import eq_ as eq, assert_raises, assert_not_equal
12 from rados import (Rados,
13 LIBRADOS_OP_FLAG_FADVISE_DONTNEED,
14 LIBRADOS_OP_FLAG_FADVISE_NOCACHE,
15 LIBRADOS_OP_FLAG_FADVISE_RANDOM)
16 from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists,
17 ImageBusy, ImageHasSnapshots, ReadOnlyImage,
18 FunctionNotSupported, ArgumentOutOfRange,
19 ECANCELED, OperationCanceled,
20 DiskQuotaExceeded, ConnectionShutdown, PermissionError,
21 RBD_FEATURE_LAYERING, RBD_FEATURE_STRIPINGV2,
22 RBD_FEATURE_EXCLUSIVE_LOCK, RBD_FEATURE_JOURNALING,
23 RBD_MIRROR_MODE_DISABLED, RBD_MIRROR_MODE_IMAGE,
24 RBD_MIRROR_MODE_POOL, RBD_MIRROR_IMAGE_ENABLED,
25 RBD_MIRROR_IMAGE_DISABLED, MIRROR_IMAGE_STATUS_STATE_UNKNOWN,
26 RBD_LOCK_MODE_EXCLUSIVE, RBD_OPERATION_FEATURE_GROUP,
27 RBD_SNAP_NAMESPACE_TYPE_TRASH,
28 RBD_IMAGE_MIGRATION_STATE_PREPARED, RBD_CONFIG_SOURCE_CONFIG,
29 RBD_CONFIG_SOURCE_POOL, RBD_CONFIG_SOURCE_IMAGE,
30 RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST,
31 RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY)
32
33 rados = None
34 ioctx = None
35 features = None
36 image_idx = 0
37 group_idx = 0
38 snap_idx = 0
39 image_name = None
40 group_name = None
41 snap_name = None
42 pool_idx = 0
43 pool_name = None
44 IMG_SIZE = 8 << 20 # 8 MiB
45 IMG_ORDER = 22 # 4 MiB objects
46
47 os.environ["RBD_FORCE_ALLOW_V1"] = "1"
48
49 def setup_module():
50 global rados
51 rados = Rados(conffile='')
52 rados.connect()
53 global pool_name
54 pool_name = get_temp_pool_name()
55 rados.create_pool(pool_name)
56 global ioctx
57 ioctx = rados.open_ioctx(pool_name)
58 RBD().pool_init(ioctx, True)
59 global features
60 features = os.getenv("RBD_FEATURES")
61 features = int(features) if features is not None else 61
62
63 def teardown_module():
64 global ioctx
65 ioctx.close()
66 global rados
67 rados.delete_pool(pool_name)
68 rados.shutdown()
69
70 def get_temp_pool_name():
71 global pool_idx
72 pool_idx += 1
73 return "test-rbd-api-" + socket.gethostname() + '-' + str(os.getpid()) + \
74 '-' + str(pool_idx)
75
76 def get_temp_image_name():
77 global image_idx
78 image_idx += 1
79 return "image" + str(image_idx)
80
81 def get_temp_group_name():
82 global group_idx
83 group_idx += 1
84 return "group" + str(group_idx)
85
86 def get_temp_snap_name():
87 global snap_idx
88 snap_idx += 1
89 return "snap" + str(snap_idx)
90
91 def create_image():
92 global image_name
93 image_name = get_temp_image_name()
94 if features is not None:
95 RBD().create(ioctx, image_name, IMG_SIZE, IMG_ORDER, old_format=False,
96 features=int(features))
97 else:
98 RBD().create(ioctx, image_name, IMG_SIZE, IMG_ORDER, old_format=True)
99 return image_name
100
101 def remove_image():
102 if image_name is not None:
103 RBD().remove(ioctx, image_name)
104
105 def create_group():
106 global group_name
107 group_name = get_temp_group_name()
108 RBD().group_create(ioctx, group_name)
109
110 def remove_group():
111 if group_name is not None:
112 RBD().group_remove(ioctx, group_name)
113
114 def rename_group():
115 new_group_name = "new" + group_name
116 RBD().group_rename(ioctx, group_name, new_group_name)
117
118 def require_new_format():
119 def wrapper(fn):
120 def _require_new_format(*args, **kwargs):
121 global features
122 if features is None:
123 raise SkipTest
124 return fn(*args, **kwargs)
125 return functools.wraps(fn)(_require_new_format)
126 return wrapper
127
128 def require_features(required_features):
129 def wrapper(fn):
130 def _require_features(*args, **kwargs):
131 global features
132 if features is None:
133 raise SkipTest
134 for feature in required_features:
135 if feature & features != feature:
136 raise SkipTest
137 return fn(*args, **kwargs)
138 return functools.wraps(fn)(_require_features)
139 return wrapper
140
141 def blacklist_features(blacklisted_features):
142 def wrapper(fn):
143 def _blacklist_features(*args, **kwargs):
144 global features
145 for feature in blacklisted_features:
146 if features is not None and feature & features == feature:
147 raise SkipTest
148 return fn(*args, **kwargs)
149 return functools.wraps(fn)(_blacklist_features)
150 return wrapper
151
152 def test_version():
153 RBD().version()
154
155 def test_create():
156 create_image()
157 remove_image()
158
159 def check_default_params(format, order=None, features=None, stripe_count=None,
160 stripe_unit=None, exception=None):
161 global rados
162 global ioctx
163 orig_vals = {}
164 for k in ['rbd_default_format', 'rbd_default_order', 'rbd_default_features',
165 'rbd_default_stripe_count', 'rbd_default_stripe_unit']:
166 orig_vals[k] = rados.conf_get(k)
167 try:
168 rados.conf_set('rbd_default_format', str(format))
169 if order is not None:
170 rados.conf_set('rbd_default_order', str(order or 0))
171 if features is not None:
172 rados.conf_set('rbd_default_features', str(features or 0))
173 if stripe_count is not None:
174 rados.conf_set('rbd_default_stripe_count', str(stripe_count or 0))
175 if stripe_unit is not None:
176 rados.conf_set('rbd_default_stripe_unit', str(stripe_unit or 0))
177 feature_data_pool = 0
178 datapool = rados.conf_get('rbd_default_data_pool')
179 if not len(datapool) == 0:
180 feature_data_pool = 128
181 image_name = get_temp_image_name()
182 if exception is None:
183 RBD().create(ioctx, image_name, IMG_SIZE)
184 try:
185 with Image(ioctx, image_name) as image:
186 eq(format == 1, image.old_format())
187
188 expected_order = int(rados.conf_get('rbd_default_order'))
189 actual_order = image.stat()['order']
190 eq(expected_order, actual_order)
191
192 expected_features = features
193 if format == 1:
194 expected_features = 0
195 elif expected_features is None:
196 expected_features = 61 | feature_data_pool
197 else:
198 expected_features |= feature_data_pool
199 eq(expected_features, image.features())
200
201 expected_stripe_count = stripe_count
202 if not expected_stripe_count or format == 1 or \
203 features & RBD_FEATURE_STRIPINGV2 == 0:
204 expected_stripe_count = 1
205 eq(expected_stripe_count, image.stripe_count())
206
207 expected_stripe_unit = stripe_unit
208 if not expected_stripe_unit or format == 1 or \
209 features & RBD_FEATURE_STRIPINGV2 == 0:
210 expected_stripe_unit = 1 << actual_order
211 eq(expected_stripe_unit, image.stripe_unit())
212 finally:
213 RBD().remove(ioctx, image_name)
214 else:
215 assert_raises(exception, RBD().create, ioctx, image_name, IMG_SIZE)
216 finally:
217 for k, v in orig_vals.items():
218 rados.conf_set(k, v)
219
220 def test_create_defaults():
221 # basic format 1 and 2
222 check_default_params(1)
223 check_default_params(2)
224 # invalid order
225 check_default_params(1, 0, exception=ArgumentOutOfRange)
226 check_default_params(2, 0, exception=ArgumentOutOfRange)
227 check_default_params(1, 11, exception=ArgumentOutOfRange)
228 check_default_params(2, 11, exception=ArgumentOutOfRange)
229 check_default_params(1, 65, exception=ArgumentOutOfRange)
230 check_default_params(2, 65, exception=ArgumentOutOfRange)
231 # striping and features are ignored for format 1
232 check_default_params(1, 20, 0, 1, 1)
233 check_default_params(1, 20, 3, 1, 1)
234 check_default_params(1, 20, 0, 0, 0)
235 # striping is ignored if stripingv2 is not set
236 check_default_params(2, 20, 0, 1, 1 << 20)
237 check_default_params(2, 20, RBD_FEATURE_LAYERING, 1, 1 << 20)
238 check_default_params(2, 20, 0, 0, 0)
239 # striping with stripingv2 is fine
240 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2, 1, 1 << 16)
241 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2, 10, 1 << 20)
242 check_default_params(2, 20, RBD_FEATURE_STRIPINGV2, 10, 1 << 16)
243 check_default_params(2, 20, 0, 0, 0)
244 # make sure invalid combinations of stripe unit and order are still invalid
245 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 10, 1 << 50, exception=InvalidArgument)
246 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 10, 100, exception=InvalidArgument)
247 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 0, 1, exception=InvalidArgument)
248 check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 1, 0, exception=InvalidArgument)
249 # 0 stripe unit and count are still ignored
250 check_default_params(2, 22, 0, 0, 0)
251
252 def test_context_manager():
253 with Rados(conffile='') as cluster:
254 with cluster.open_ioctx(pool_name) as ioctx:
255 image_name = get_temp_image_name()
256 RBD().create(ioctx, image_name, IMG_SIZE)
257 with Image(ioctx, image_name) as image:
258 data = rand_data(256)
259 image.write(data, 0)
260 read = image.read(0, 256)
261 RBD().remove(ioctx, image_name)
262 eq(data, read)
263
264 def test_open_read_only():
265 with Rados(conffile='') as cluster:
266 with cluster.open_ioctx(pool_name) as ioctx:
267 image_name = get_temp_image_name()
268 RBD().create(ioctx, image_name, IMG_SIZE)
269 data = rand_data(256)
270 with Image(ioctx, image_name) as image:
271 image.write(data, 0)
272 image.create_snap('snap')
273 with Image(ioctx, image_name, read_only=True) as image:
274 read = image.read(0, 256)
275 eq(data, read)
276 assert_raises(ReadOnlyImage, image.write, data, 0)
277 assert_raises(ReadOnlyImage, image.create_snap, 'test')
278 assert_raises(ReadOnlyImage, image.remove_snap, 'snap')
279 assert_raises(ReadOnlyImage, image.rollback_to_snap, 'snap')
280 assert_raises(ReadOnlyImage, image.protect_snap, 'snap')
281 assert_raises(ReadOnlyImage, image.unprotect_snap, 'snap')
282 assert_raises(ReadOnlyImage, image.unprotect_snap, 'snap')
283 assert_raises(ReadOnlyImage, image.flatten)
284 with Image(ioctx, image_name) as image:
285 image.remove_snap('snap')
286 RBD().remove(ioctx, image_name)
287 eq(data, read)
288
289 def test_open_dne():
290 for i in range(100):
291 image_name = get_temp_image_name()
292 assert_raises(ImageNotFound, Image, ioctx, image_name + 'dne')
293 assert_raises(ImageNotFound, Image, ioctx, image_name, 'snap')
294
295 def test_open_readonly_dne():
296 for i in range(100):
297 image_name = get_temp_image_name()
298 assert_raises(ImageNotFound, Image, ioctx, image_name + 'dne',
299 read_only=True)
300 assert_raises(ImageNotFound, Image, ioctx, image_name, 'snap',
301 read_only=True)
302
303 @require_new_format()
304 def test_open_by_id():
305 with Rados(conffile='') as cluster:
306 with cluster.open_ioctx(pool_name) as ioctx:
307 image_name = get_temp_image_name()
308 RBD().create(ioctx, image_name, IMG_SIZE)
309 with Image(ioctx, image_name) as image:
310 image_id = image.id()
311 with Image(ioctx, image_id=image_id) as image:
312 eq(image.get_name(), image_name)
313 RBD().remove(ioctx, image_name)
314
315 def test_remove_dne():
316 assert_raises(ImageNotFound, remove_image)
317
318 def test_list_empty():
319 eq([], RBD().list(ioctx))
320
321 @with_setup(create_image, remove_image)
322 def test_list():
323 eq([image_name], RBD().list(ioctx))
324
325 with Image(ioctx, image_name) as image:
326 image_id = image.id()
327 eq([{'id': image_id, 'name': image_name}], list(RBD().list2(ioctx)))
328
329 @with_setup(create_image)
330 def test_remove_with_progress():
331 d = {'received_callback': False}
332 def progress_cb(current, total):
333 d['received_callback'] = True
334 return 0
335
336 RBD().remove(ioctx, image_name, on_progress=progress_cb)
337 eq(True, d['received_callback'])
338
339 @with_setup(create_image)
340 def test_remove_canceled():
341 def progress_cb(current, total):
342 return -ECANCELED
343
344 assert_raises(OperationCanceled, RBD().remove, ioctx, image_name,
345 on_progress=progress_cb)
346
347 @with_setup(create_image, remove_image)
348 def test_rename():
349 rbd = RBD()
350 image_name2 = get_temp_image_name()
351 rbd.rename(ioctx, image_name, image_name2)
352 eq([image_name2], rbd.list(ioctx))
353 rbd.rename(ioctx, image_name2, image_name)
354 eq([image_name], rbd.list(ioctx))
355
356 def test_pool_metadata():
357 rbd = RBD()
358 metadata = list(rbd.pool_metadata_list(ioctx))
359 eq(len(metadata), 0)
360 assert_raises(KeyError, rbd.pool_metadata_get, ioctx, "key1")
361 rbd.pool_metadata_set(ioctx, "key1", "value1")
362 rbd.pool_metadata_set(ioctx, "key2", "value2")
363 value = rbd.pool_metadata_get(ioctx, "key1")
364 eq(value, "value1")
365 value = rbd.pool_metadata_get(ioctx, "key2")
366 eq(value, "value2")
367 metadata = list(rbd.pool_metadata_list(ioctx))
368 eq(len(metadata), 2)
369 rbd.pool_metadata_remove(ioctx, "key1")
370 metadata = list(rbd.pool_metadata_list(ioctx))
371 eq(len(metadata), 1)
372 eq(metadata[0], ("key2", "value2"))
373 rbd.pool_metadata_remove(ioctx, "key2")
374 assert_raises(KeyError, rbd.pool_metadata_remove, ioctx, "key2")
375 metadata = list(rbd.pool_metadata_list(ioctx))
376 eq(len(metadata), 0)
377
378 N = 65
379 for i in range(N):
380 rbd.pool_metadata_set(ioctx, "key" + str(i), "X" * 1025)
381 metadata = list(rbd.pool_metadata_list(ioctx))
382 eq(len(metadata), N)
383 for i in range(N):
384 rbd.pool_metadata_remove(ioctx, "key" + str(i))
385 metadata = list(rbd.pool_metadata_list(ioctx))
386 eq(len(metadata), N - i - 1)
387
388 def test_config_list():
389 rbd = RBD()
390
391 for option in rbd.config_list(ioctx):
392 eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
393
394 rbd.pool_metadata_set(ioctx, "conf_rbd_cache", "true")
395
396 for option in rbd.config_list(ioctx):
397 if option['name'] == "rbd_cache":
398 eq(option['source'], RBD_CONFIG_SOURCE_POOL)
399 else:
400 eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
401
402 rbd.pool_metadata_remove(ioctx, "conf_rbd_cache")
403
404 for option in rbd.config_list(ioctx):
405 eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
406
407 def test_namespaces():
408 rbd = RBD()
409
410 eq(False, rbd.namespace_exists(ioctx, 'ns1'))
411 eq([], rbd.namespace_list(ioctx))
412 assert_raises(ImageNotFound, rbd.namespace_remove, ioctx, 'ns1')
413
414 rbd.namespace_create(ioctx, 'ns1')
415 eq(True, rbd.namespace_exists(ioctx, 'ns1'))
416
417 assert_raises(ImageExists, rbd.namespace_create, ioctx, 'ns1')
418 eq(['ns1'], rbd.namespace_list(ioctx))
419 rbd.namespace_remove(ioctx, 'ns1')
420 eq([], rbd.namespace_list(ioctx))
421
422 @require_new_format()
423 def test_pool_stats():
424 rbd = RBD()
425
426 try:
427 image1 = create_image()
428 image2 = create_image()
429 image3 = create_image()
430 image4 = create_image()
431 with Image(ioctx, image4) as image:
432 image.create_snap('snap')
433 image.resize(0)
434
435 stats = rbd.pool_stats_get(ioctx)
436 eq(stats['image_count'], 4)
437 eq(stats['image_provisioned_bytes'], 3 * IMG_SIZE)
438 eq(stats['image_max_provisioned_bytes'], 4 * IMG_SIZE)
439 eq(stats['image_snap_count'], 1)
440 eq(stats['trash_count'], 0)
441 eq(stats['trash_provisioned_bytes'], 0)
442 eq(stats['trash_max_provisioned_bytes'], 0)
443 eq(stats['trash_snap_count'], 0)
444 finally:
445 rbd.remove(ioctx, image1)
446 rbd.remove(ioctx, image2)
447 rbd.remove(ioctx, image3)
448 with Image(ioctx, image4) as image:
449 image.remove_snap('snap')
450 rbd.remove(ioctx, image4)
451
452 def rand_data(size):
453 return os.urandom(size)
454
455 def check_stat(info, size, order):
456 assert 'block_name_prefix' in info
457 eq(info['size'], size)
458 eq(info['order'], order)
459 eq(info['num_objs'], size // (1 << order))
460 eq(info['obj_size'], 1 << order)
461
462 class TestImage(object):
463
464 def setUp(self):
465 self.rbd = RBD()
466 create_image()
467 self.image = Image(ioctx, image_name)
468
469 def tearDown(self):
470 self.image.close()
471 remove_image()
472 self.image = None
473
474 @require_new_format()
475 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK])
476 def test_update_features(self):
477 features = self.image.features()
478 self.image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, True)
479 eq(features | RBD_FEATURE_EXCLUSIVE_LOCK, self.image.features())
480
481 @require_features([RBD_FEATURE_STRIPINGV2])
482 def test_create_with_params(self):
483 global features
484 image_name = get_temp_image_name()
485 order = 20
486 stripe_unit = 1 << 20
487 stripe_count = 10
488 self.rbd.create(ioctx, image_name, IMG_SIZE, order,
489 False, features, stripe_unit, stripe_count)
490 image = Image(ioctx, image_name)
491 info = image.stat()
492 check_stat(info, IMG_SIZE, order)
493 eq(image.features(), features)
494 eq(image.stripe_unit(), stripe_unit)
495 eq(image.stripe_count(), stripe_count)
496 image.close()
497 RBD().remove(ioctx, image_name)
498
499 @require_new_format()
500 def test_id(self):
501 assert_not_equal(b'', self.image.id())
502
503 def test_block_name_prefix(self):
504 assert_not_equal(b'', self.image.block_name_prefix())
505
506 def test_create_timestamp(self):
507 timestamp = self.image.create_timestamp()
508 assert_not_equal(0, timestamp.year)
509 assert_not_equal(1970, timestamp.year)
510
511 def test_access_timestamp(self):
512 timestamp = self.image.access_timestamp()
513 assert_not_equal(0, timestamp.year)
514 assert_not_equal(1970, timestamp.year)
515
516 def test_modify_timestamp(self):
517 timestamp = self.image.modify_timestamp()
518 assert_not_equal(0, timestamp.year)
519 assert_not_equal(1970, timestamp.year)
520
521 def test_invalidate_cache(self):
522 self.image.write(b'abc', 0)
523 eq(b'abc', self.image.read(0, 3))
524 self.image.invalidate_cache()
525 eq(b'abc', self.image.read(0, 3))
526
527 def test_stat(self):
528 info = self.image.stat()
529 check_stat(info, IMG_SIZE, IMG_ORDER)
530
531 def test_flags(self):
532 flags = self.image.flags()
533 eq(0, flags)
534
535 def test_image_auto_close(self):
536 image = Image(ioctx, image_name)
537
538 def test_write(self):
539 data = rand_data(256)
540 self.image.write(data, 0)
541
542 def test_write_with_fadvise_flags(self):
543 data = rand_data(256)
544 self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED)
545 self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_NOCACHE)
546
547 def test_read(self):
548 data = self.image.read(0, 20)
549 eq(data, b'\0' * 20)
550
551 def test_read_with_fadvise_flags(self):
552 data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED)
553 eq(data, b'\0' * 20)
554 data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM)
555 eq(data, b'\0' * 20)
556
557 def test_large_write(self):
558 data = rand_data(IMG_SIZE)
559 self.image.write(data, 0)
560
561 def test_large_read(self):
562 data = self.image.read(0, IMG_SIZE)
563 eq(data, b'\0' * IMG_SIZE)
564
565 def test_write_read(self):
566 data = rand_data(256)
567 offset = 50
568 self.image.write(data, offset)
569 read = self.image.read(offset, 256)
570 eq(data, read)
571
572 def test_read_bad_offset(self):
573 assert_raises(InvalidArgument, self.image.read, IMG_SIZE + 1, IMG_SIZE)
574
575 def test_resize(self):
576 new_size = IMG_SIZE * 2
577 self.image.resize(new_size)
578 info = self.image.stat()
579 check_stat(info, new_size, IMG_ORDER)
580
581 def test_resize_allow_shrink_False(self):
582 new_size = IMG_SIZE * 2
583 self.image.resize(new_size)
584 info = self.image.stat()
585 check_stat(info, new_size, IMG_ORDER)
586 assert_raises(InvalidArgument, self.image.resize, IMG_SIZE, False)
587
588 def test_size(self):
589 eq(IMG_SIZE, self.image.size())
590 self.image.create_snap('snap1')
591 new_size = IMG_SIZE * 2
592 self.image.resize(new_size)
593 eq(new_size, self.image.size())
594 self.image.create_snap('snap2')
595 self.image.set_snap('snap2')
596 eq(new_size, self.image.size())
597 self.image.set_snap('snap1')
598 eq(IMG_SIZE, self.image.size())
599 self.image.set_snap(None)
600 eq(new_size, self.image.size())
601 self.image.remove_snap('snap1')
602 self.image.remove_snap('snap2')
603
604 def test_resize_down(self):
605 new_size = IMG_SIZE // 2
606 data = rand_data(256)
607 self.image.write(data, IMG_SIZE // 2);
608 self.image.resize(new_size)
609 self.image.resize(IMG_SIZE)
610 read = self.image.read(IMG_SIZE // 2, 256)
611 eq(b'\0' * 256, read)
612
613 def test_resize_bytes(self):
614 new_size = IMG_SIZE // 2 - 5
615 data = rand_data(256)
616 self.image.write(data, IMG_SIZE // 2 - 10);
617 self.image.resize(new_size)
618 self.image.resize(IMG_SIZE)
619 read = self.image.read(IMG_SIZE // 2 - 10, 5)
620 eq(data[:5], read)
621 read = self.image.read(IMG_SIZE // 2 - 5, 251)
622 eq(b'\0' * 251, read)
623
624 def _test_copy(self, features=None, order=None, stripe_unit=None,
625 stripe_count=None):
626 global ioctx
627 data = rand_data(256)
628 self.image.write(data, 256)
629 image_name = get_temp_image_name()
630 if features is None:
631 self.image.copy(ioctx, image_name)
632 elif order is None:
633 self.image.copy(ioctx, image_name, features)
634 elif stripe_unit is None:
635 self.image.copy(ioctx, image_name, features, order)
636 elif stripe_count is None:
637 self.image.copy(ioctx, image_name, features, order, stripe_unit)
638 else:
639 self.image.copy(ioctx, image_name, features, order, stripe_unit,
640 stripe_count)
641 assert_raises(ImageExists, self.image.copy, ioctx, image_name)
642 copy = Image(ioctx, image_name)
643 copy_data = copy.read(256, 256)
644 copy.close()
645 self.rbd.remove(ioctx, image_name)
646 eq(data, copy_data)
647
648 def test_copy(self):
649 self._test_copy()
650
651 def test_copy2(self):
652 self._test_copy(self.image.features(), self.image.stat()['order'])
653
654 @require_features([RBD_FEATURE_STRIPINGV2])
655 def test_copy3(self):
656 global features
657 self._test_copy(features, self.image.stat()['order'],
658 self.image.stripe_unit(), self.image.stripe_count())
659
660 def test_deep_copy(self):
661 global ioctx
662 global features
663 self.image.write(b'a' * 256, 0)
664 self.image.create_snap('snap1')
665 self.image.write(b'b' * 256, 0)
666 dst_name = get_temp_image_name()
667 self.image.deep_copy(ioctx, dst_name, features=features,
668 order=self.image.stat()['order'],
669 stripe_unit=self.image.stripe_unit(),
670 stripe_count=self.image.stripe_count(),
671 data_pool=None)
672 self.image.remove_snap('snap1')
673 with Image(ioctx, dst_name, 'snap1') as copy:
674 copy_data = copy.read(0, 256)
675 eq(b'a' * 256, copy_data)
676 with Image(ioctx, dst_name) as copy:
677 copy_data = copy.read(0, 256)
678 eq(b'b' * 256, copy_data)
679 copy.remove_snap('snap1')
680 self.rbd.remove(ioctx, dst_name)
681
682 @require_features([RBD_FEATURE_LAYERING])
683 def test_deep_copy_clone(self):
684 global ioctx
685 global features
686 self.image.write(b'a' * 256, 0)
687 self.image.create_snap('snap1')
688 self.image.write(b'b' * 256, 0)
689 self.image.protect_snap('snap1')
690 clone_name = get_temp_image_name()
691 dst_name = get_temp_image_name()
692 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name)
693 with Image(ioctx, clone_name) as child:
694 child.create_snap('snap1')
695 child.deep_copy(ioctx, dst_name, features=features,
696 order=self.image.stat()['order'],
697 stripe_unit=self.image.stripe_unit(),
698 stripe_count=self.image.stripe_count(),
699 data_pool=None)
700 child.remove_snap('snap1')
701
702 with Image(ioctx, dst_name) as copy:
703 copy_data = copy.read(0, 256)
704 eq(b'a' * 256, copy_data)
705 copy.remove_snap('snap1')
706 self.rbd.remove(ioctx, dst_name)
707 self.rbd.remove(ioctx, clone_name)
708 self.image.unprotect_snap('snap1')
709 self.image.remove_snap('snap1')
710
711 def test_create_snap(self):
712 global ioctx
713 self.image.create_snap('snap1')
714 read = self.image.read(0, 256)
715 eq(read, b'\0' * 256)
716 data = rand_data(256)
717 self.image.write(data, 0)
718 read = self.image.read(0, 256)
719 eq(read, data)
720 at_snapshot = Image(ioctx, image_name, 'snap1')
721 snap_data = at_snapshot.read(0, 256)
722 at_snapshot.close()
723 eq(snap_data, b'\0' * 256)
724 self.image.remove_snap('snap1')
725
726 def test_list_snaps(self):
727 eq([], list(self.image.list_snaps()))
728 self.image.create_snap('snap1')
729 eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
730 self.image.create_snap('snap2')
731 eq(['snap1', 'snap2'], [snap['name'] for snap in self.image.list_snaps()])
732 self.image.remove_snap('snap1')
733 self.image.remove_snap('snap2')
734
735 def test_list_snaps_iterator_auto_close(self):
736 self.image.create_snap('snap1')
737 self.image.list_snaps()
738 self.image.remove_snap('snap1')
739
740 def test_remove_snap(self):
741 eq([], list(self.image.list_snaps()))
742 self.image.create_snap('snap1')
743 eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
744 self.image.remove_snap('snap1')
745 eq([], list(self.image.list_snaps()))
746
747 def test_rename_snap(self):
748 eq([], list(self.image.list_snaps()))
749 self.image.create_snap('snap1')
750 eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
751 self.image.rename_snap("snap1", "snap1-rename")
752 eq(['snap1-rename'], [snap['name'] for snap in self.image.list_snaps()])
753 self.image.remove_snap('snap1-rename')
754 eq([], list(self.image.list_snaps()))
755
756 @require_features([RBD_FEATURE_LAYERING])
757 def test_protect_snap(self):
758 self.image.create_snap('snap1')
759 assert(not self.image.is_protected_snap('snap1'))
760 self.image.protect_snap('snap1')
761 assert(self.image.is_protected_snap('snap1'))
762 assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
763 self.image.unprotect_snap('snap1')
764 assert(not self.image.is_protected_snap('snap1'))
765 self.image.remove_snap('snap1')
766 assert_raises(ImageNotFound, self.image.unprotect_snap, 'snap1')
767 assert_raises(ImageNotFound, self.image.is_protected_snap, 'snap1')
768
769 def test_snap_timestamp(self):
770 self.image.create_snap('snap1')
771 eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
772 for snap in self.image.list_snaps():
773 snap_id = snap["id"]
774 time = self.image.get_snap_timestamp(snap_id)
775 assert_not_equal(b'', time.year)
776 assert_not_equal(0, time.year)
777 assert_not_equal(time.year, '1970')
778 self.image.remove_snap('snap1')
779
780 def test_limit_snaps(self):
781 self.image.set_snap_limit(2)
782 eq(2, self.image.get_snap_limit())
783 self.image.create_snap('snap1')
784 self.image.create_snap('snap2')
785 assert_raises(DiskQuotaExceeded, self.image.create_snap, 'snap3')
786 self.image.remove_snap_limit()
787 self.image.create_snap('snap3')
788
789 self.image.remove_snap('snap1')
790 self.image.remove_snap('snap2')
791 self.image.remove_snap('snap3')
792
793 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK])
794 def test_remove_with_exclusive_lock(self):
795 assert_raises(ImageBusy, remove_image)
796
797 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK])
798 def test_remove_with_snap(self):
799 self.image.create_snap('snap1')
800 assert_raises(ImageHasSnapshots, remove_image)
801 self.image.remove_snap('snap1')
802
803 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK])
804 def test_remove_with_watcher(self):
805 data = rand_data(256)
806 self.image.write(data, 0)
807 assert_raises(ImageBusy, remove_image)
808 read = self.image.read(0, 256)
809 eq(read, data)
810
811 def test_rollback_to_snap(self):
812 self.image.write(b'\0' * 256, 0)
813 self.image.create_snap('snap1')
814 read = self.image.read(0, 256)
815 eq(read, b'\0' * 256)
816 data = rand_data(256)
817 self.image.write(data, 0)
818 read = self.image.read(0, 256)
819 eq(read, data)
820 self.image.rollback_to_snap('snap1')
821 read = self.image.read(0, 256)
822 eq(read, b'\0' * 256)
823 self.image.remove_snap('snap1')
824
825 def test_rollback_to_snap_sparse(self):
826 self.image.create_snap('snap1')
827 read = self.image.read(0, 256)
828 eq(read, b'\0' * 256)
829 data = rand_data(256)
830 self.image.write(data, 0)
831 read = self.image.read(0, 256)
832 eq(read, data)
833 self.image.rollback_to_snap('snap1')
834 read = self.image.read(0, 256)
835 eq(read, b'\0' * 256)
836 self.image.remove_snap('snap1')
837
838 def test_rollback_with_resize(self):
839 read = self.image.read(0, 256)
840 eq(read, b'\0' * 256)
841 data = rand_data(256)
842 self.image.write(data, 0)
843 self.image.create_snap('snap1')
844 read = self.image.read(0, 256)
845 eq(read, data)
846 new_size = IMG_SIZE * 2
847 self.image.resize(new_size)
848 check_stat(self.image.stat(), new_size, IMG_ORDER)
849 self.image.write(data, new_size - 256)
850 self.image.create_snap('snap2')
851 read = self.image.read(new_size - 256, 256)
852 eq(read, data)
853 self.image.rollback_to_snap('snap1')
854 check_stat(self.image.stat(), IMG_SIZE, IMG_ORDER)
855 assert_raises(InvalidArgument, self.image.read, new_size - 256, 256)
856 self.image.rollback_to_snap('snap2')
857 check_stat(self.image.stat(), new_size, IMG_ORDER)
858 read = self.image.read(new_size - 256, 256)
859 eq(read, data)
860 self.image.remove_snap('snap1')
861 self.image.remove_snap('snap2')
862
863 def test_set_snap(self):
864 self.image.write(b'\0' * 256, 0)
865 self.image.create_snap('snap1')
866 read = self.image.read(0, 256)
867 eq(read, b'\0' * 256)
868 data = rand_data(256)
869 self.image.write(data, 0)
870 read = self.image.read(0, 256)
871 eq(read, data)
872 self.image.set_snap('snap1')
873 read = self.image.read(0, 256)
874 eq(read, b'\0' * 256)
875 assert_raises(ReadOnlyImage, self.image.write, data, 0)
876 self.image.remove_snap('snap1')
877
878 def test_set_no_snap(self):
879 self.image.write(b'\0' * 256, 0)
880 self.image.create_snap('snap1')
881 read = self.image.read(0, 256)
882 eq(read, b'\0' * 256)
883 data = rand_data(256)
884 self.image.write(data, 0)
885 read = self.image.read(0, 256)
886 eq(read, data)
887 self.image.set_snap('snap1')
888 read = self.image.read(0, 256)
889 eq(read, b'\0' * 256)
890 assert_raises(ReadOnlyImage, self.image.write, data, 0)
891 self.image.set_snap(None)
892 read = self.image.read(0, 256)
893 eq(read, data)
894 self.image.remove_snap('snap1')
895
896 def test_set_snap_by_id(self):
897 self.image.write(b'\0' * 256, 0)
898 self.image.create_snap('snap1')
899 read = self.image.read(0, 256)
900 eq(read, b'\0' * 256)
901 data = rand_data(256)
902 self.image.write(data, 0)
903 read = self.image.read(0, 256)
904 eq(read, data)
905 snaps = list(self.image.list_snaps())
906 self.image.set_snap_by_id(snaps[0]['id'])
907 read = self.image.read(0, 256)
908 eq(read, b'\0' * 256)
909 assert_raises(ReadOnlyImage, self.image.write, data, 0)
910 self.image.set_snap_by_id(None)
911 read = self.image.read(0, 256)
912 eq(read, data)
913 self.image.remove_snap('snap1')
914
915 def test_set_snap_sparse(self):
916 self.image.create_snap('snap1')
917 read = self.image.read(0, 256)
918 eq(read, b'\0' * 256)
919 data = rand_data(256)
920 self.image.write(data, 0)
921 read = self.image.read(0, 256)
922 eq(read, data)
923 self.image.set_snap('snap1')
924 read = self.image.read(0, 256)
925 eq(read, b'\0' * 256)
926 assert_raises(ReadOnlyImage, self.image.write, data, 0)
927 self.image.remove_snap('snap1')
928
929 def test_many_snaps(self):
930 num_snaps = 200
931 for i in range(num_snaps):
932 self.image.create_snap(str(i))
933 snaps = sorted(self.image.list_snaps(),
934 key=lambda snap: int(snap['name']))
935 eq(len(snaps), num_snaps)
936 for i, snap in enumerate(snaps):
937 eq(snap['size'], IMG_SIZE)
938 eq(snap['name'], str(i))
939 for i in range(num_snaps):
940 self.image.remove_snap(str(i))
941
942 def test_set_snap_deleted(self):
943 self.image.write(b'\0' * 256, 0)
944 self.image.create_snap('snap1')
945 read = self.image.read(0, 256)
946 eq(read, b'\0' * 256)
947 data = rand_data(256)
948 self.image.write(data, 0)
949 read = self.image.read(0, 256)
950 eq(read, data)
951 self.image.set_snap('snap1')
952 self.image.remove_snap('snap1')
953 assert_raises(ImageNotFound, self.image.read, 0, 256)
954 self.image.set_snap(None)
955 read = self.image.read(0, 256)
956 eq(read, data)
957
958 def test_set_snap_recreated(self):
959 self.image.write(b'\0' * 256, 0)
960 self.image.create_snap('snap1')
961 read = self.image.read(0, 256)
962 eq(read, b'\0' * 256)
963 data = rand_data(256)
964 self.image.write(data, 0)
965 read = self.image.read(0, 256)
966 eq(read, data)
967 self.image.set_snap('snap1')
968 self.image.remove_snap('snap1')
969 self.image.create_snap('snap1')
970 assert_raises(ImageNotFound, self.image.read, 0, 256)
971 self.image.set_snap(None)
972 read = self.image.read(0, 256)
973 eq(read, data)
974 self.image.remove_snap('snap1')
975
976 def test_lock_unlock(self):
977 assert_raises(ImageNotFound, self.image.unlock, '')
978 self.image.lock_exclusive('')
979 assert_raises(ImageExists, self.image.lock_exclusive, '')
980 assert_raises(ImageBusy, self.image.lock_exclusive, 'test')
981 assert_raises(ImageExists, self.image.lock_shared, '', '')
982 assert_raises(ImageBusy, self.image.lock_shared, 'foo', '')
983 self.image.unlock('')
984
985 def test_list_lockers(self):
986 eq([], self.image.list_lockers())
987 self.image.lock_exclusive('test')
988 lockers = self.image.list_lockers()
989 eq(1, len(lockers['lockers']))
990 _, cookie, _ = lockers['lockers'][0]
991 eq(cookie, 'test')
992 eq('', lockers['tag'])
993 assert lockers['exclusive']
994 self.image.unlock('test')
995 eq([], self.image.list_lockers())
996
997 num_shared = 10
998 for i in range(num_shared):
999 self.image.lock_shared(str(i), 'tag')
1000 lockers = self.image.list_lockers()
1001 eq('tag', lockers['tag'])
1002 assert not lockers['exclusive']
1003 eq(num_shared, len(lockers['lockers']))
1004 cookies = sorted(map(lambda x: x[1], lockers['lockers']))
1005 for i in range(num_shared):
1006 eq(str(i), cookies[i])
1007 self.image.unlock(str(i))
1008 eq([], self.image.list_lockers())
1009
1010 def test_diff_iterate(self):
1011 check_diff(self.image, 0, IMG_SIZE, None, [])
1012 self.image.write(b'a' * 256, 0)
1013 check_diff(self.image, 0, IMG_SIZE, None, [(0, 256, True)])
1014 self.image.write(b'b' * 256, 256)
1015 check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)])
1016 self.image.discard(128, 256)
1017 check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)])
1018
1019 self.image.create_snap('snap1')
1020 self.image.discard(0, 1 << IMG_ORDER)
1021 self.image.create_snap('snap2')
1022 self.image.set_snap('snap2')
1023 check_diff(self.image, 0, IMG_SIZE, 'snap1', [(0, 512, False)])
1024 self.image.remove_snap('snap1')
1025 self.image.remove_snap('snap2')
1026
1027 def test_aio_read(self):
1028 # this is a list so that the local cb() can modify it
1029 retval = [None]
1030 def cb(_, buf):
1031 retval[0] = buf
1032
1033 # test1: success case
1034 comp = self.image.aio_read(0, 20, cb)
1035 comp.wait_for_complete_and_cb()
1036 eq(retval[0], b'\0' * 20)
1037 eq(comp.get_return_value(), 20)
1038 eq(sys.getrefcount(comp), 2)
1039
1040 # test2: error case
1041 retval[0] = 1
1042 comp = self.image.aio_read(IMG_SIZE, 20, cb)
1043 comp.wait_for_complete_and_cb()
1044 eq(None, retval[0])
1045 assert(comp.get_return_value() < 0)
1046 eq(sys.getrefcount(comp), 2)
1047
1048 def test_aio_write(self):
1049 retval = [None]
1050 def cb(comp):
1051 retval[0] = comp.get_return_value()
1052
1053 data = rand_data(256)
1054 comp = self.image.aio_write(data, 256, cb)
1055 comp.wait_for_complete_and_cb()
1056 eq(retval[0], 0)
1057 eq(comp.get_return_value(), 0)
1058 eq(sys.getrefcount(comp), 2)
1059 eq(self.image.read(256, 256), data)
1060
1061 def test_aio_discard(self):
1062 retval = [None]
1063 def cb(comp):
1064 retval[0] = comp.get_return_value()
1065
1066 data = rand_data(256)
1067 self.image.write(data, 0)
1068 comp = self.image.aio_discard(0, 256, cb)
1069 comp.wait_for_complete_and_cb()
1070 eq(retval[0], 0)
1071 eq(comp.get_return_value(), 0)
1072 eq(sys.getrefcount(comp), 2)
1073 eq(self.image.read(256, 256), b'\0' * 256)
1074
1075 def test_aio_flush(self):
1076 retval = [None]
1077 def cb(comp):
1078 retval[0] = comp.get_return_value()
1079
1080 comp = self.image.aio_flush(cb)
1081 comp.wait_for_complete_and_cb()
1082 eq(retval[0], 0)
1083 eq(sys.getrefcount(comp), 2)
1084
1085 def test_metadata(self):
1086 metadata = list(self.image.metadata_list())
1087 eq(len(metadata), 0)
1088 assert_raises(KeyError, self.image.metadata_get, "key1")
1089 self.image.metadata_set("key1", "value1")
1090 self.image.metadata_set("key2", "value2")
1091 value = self.image.metadata_get("key1")
1092 eq(value, "value1")
1093 value = self.image.metadata_get("key2")
1094 eq(value, "value2")
1095 metadata = list(self.image.metadata_list())
1096 eq(len(metadata), 2)
1097 self.image.metadata_remove("key1")
1098 metadata = list(self.image.metadata_list())
1099 eq(len(metadata), 1)
1100 eq(metadata[0], ("key2", "value2"))
1101 self.image.metadata_remove("key2")
1102 assert_raises(KeyError, self.image.metadata_remove, "key2")
1103 metadata = list(self.image.metadata_list())
1104 eq(len(metadata), 0)
1105
1106 N = 65
1107 for i in range(N):
1108 self.image.metadata_set("key" + str(i), "X" * 1025)
1109 metadata = list(self.image.metadata_list())
1110 eq(len(metadata), N)
1111 for i in range(N):
1112 self.image.metadata_remove("key" + str(i))
1113 metadata = list(self.image.metadata_list())
1114 eq(len(metadata), N - i - 1)
1115
1116 def test_watchers_list(self):
1117 watchers = list(self.image.watchers_list())
1118 # The image is open (in r/w mode) from setup, so expect there to be one
1119 # watcher.
1120 eq(len(watchers), 1)
1121
1122 def test_config_list(self):
1123 with Image(ioctx, image_name) as image:
1124 for option in image.config_list():
1125 eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
1126
1127 image.metadata_set("conf_rbd_cache", "true")
1128
1129 for option in image.config_list():
1130 if option['name'] == "rbd_cache":
1131 eq(option['source'], RBD_CONFIG_SOURCE_IMAGE)
1132 else:
1133 eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
1134
1135 image.metadata_remove("conf_rbd_cache")
1136
1137 for option in image.config_list():
1138 eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
1139
1140 def test_sparsify(self):
1141 assert_raises(InvalidArgument, self.image.sparsify, 16)
1142 self.image.sparsify(4096)
1143
1144 class TestImageId(object):
1145
1146 def setUp(self):
1147 self.rbd = RBD()
1148 create_image()
1149 self.image = Image(ioctx, image_name)
1150 self.image2 = Image(ioctx, None, None, False, self.image.id())
1151
1152 def tearDown(self):
1153 self.image.close()
1154 self.image2.close()
1155 remove_image()
1156 self.image = None
1157 self.image2 = None
1158
1159 def test_read(self):
1160 data = self.image2.read(0, 20)
1161 eq(data, b'\0' * 20)
1162
1163 def test_write(self):
1164 data = rand_data(256)
1165 self.image2.write(data, 0)
1166
1167 def test_resize(self):
1168 new_size = IMG_SIZE * 2
1169 self.image2.resize(new_size)
1170 info = self.image2.stat()
1171 check_stat(info, new_size, IMG_ORDER)
1172
1173 def check_diff(image, offset, length, from_snapshot, expected):
1174 extents = []
1175 def cb(offset, length, exists):
1176 extents.append((offset, length, exists))
1177 image.diff_iterate(0, IMG_SIZE, None, cb)
1178 eq(extents, expected)
1179
1180 class TestClone(object):
1181
1182 @require_features([RBD_FEATURE_LAYERING])
1183 def setUp(self):
1184 global ioctx
1185 global features
1186 self.rbd = RBD()
1187 create_image()
1188 self.image = Image(ioctx, image_name)
1189 data = rand_data(256)
1190 self.image.write(data, IMG_SIZE // 2)
1191 self.image.create_snap('snap1')
1192 global features
1193 self.image.protect_snap('snap1')
1194 self.clone_name = get_temp_image_name()
1195 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name,
1196 features)
1197 self.clone = Image(ioctx, self.clone_name)
1198
1199 def tearDown(self):
1200 global ioctx
1201 self.clone.close()
1202 self.rbd.remove(ioctx, self.clone_name)
1203 self.image.unprotect_snap('snap1')
1204 self.image.remove_snap('snap1')
1205 self.image.close()
1206 remove_image()
1207
1208 def _test_with_params(self, features=None, order=None, stripe_unit=None,
1209 stripe_count=None):
1210 self.image.create_snap('snap2')
1211 self.image.protect_snap('snap2')
1212 clone_name2 = get_temp_image_name()
1213 if features is None:
1214 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2)
1215 elif order is None:
1216 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
1217 features)
1218 elif stripe_unit is None:
1219 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
1220 features, order)
1221 elif stripe_count is None:
1222 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
1223 features, order, stripe_unit)
1224 else:
1225 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
1226 features, order, stripe_unit, stripe_count)
1227 self.rbd.remove(ioctx, clone_name2)
1228 self.image.unprotect_snap('snap2')
1229 self.image.remove_snap('snap2')
1230
1231 def test_with_params(self):
1232 self._test_with_params()
1233
1234 def test_with_params2(self):
1235 global features
1236 self._test_with_params(features, self.image.stat()['order'])
1237
1238 @require_features([RBD_FEATURE_STRIPINGV2])
1239 def test_with_params3(self):
1240 global features
1241 self._test_with_params(features, self.image.stat()['order'],
1242 self.image.stripe_unit(),
1243 self.image.stripe_count())
1244
1245 def test_unprotected(self):
1246 self.image.create_snap('snap2')
1247 global features
1248 clone_name2 = get_temp_image_name()
1249 rados.conf_set("rbd_default_clone_format", "1")
1250 assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name,
1251 'snap2', ioctx, clone_name2, features)
1252 rados.conf_set("rbd_default_clone_format", "auto")
1253 self.image.remove_snap('snap2')
1254
1255 def test_unprotect_with_children(self):
1256 global features
1257 # can't remove a snapshot that has dependent clones
1258 assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
1259
1260 # validate parent info of clone created by TestClone.setUp
1261 (pool, image, snap) = self.clone.parent_info()
1262 eq(pool, pool_name)
1263 eq(image, image_name)
1264 eq(snap, 'snap1')
1265 eq(self.image.id(), self.clone.parent_id())
1266
1267 # create a new pool...
1268 pool_name2 = get_temp_pool_name()
1269 rados.create_pool(pool_name2)
1270 other_ioctx = rados.open_ioctx(pool_name2)
1271 other_ioctx.application_enable('rbd')
1272
1273 # ...with a clone of the same parent
1274 other_clone_name = get_temp_image_name()
1275 rados.conf_set("rbd_default_clone_format", "1")
1276 self.rbd.clone(ioctx, image_name, 'snap1', other_ioctx,
1277 other_clone_name, features)
1278 rados.conf_set("rbd_default_clone_format", "auto")
1279 self.other_clone = Image(other_ioctx, other_clone_name)
1280 # validate its parent info
1281 (pool, image, snap) = self.other_clone.parent_info()
1282 eq(pool, pool_name)
1283 eq(image, image_name)
1284 eq(snap, 'snap1')
1285 eq(self.image.id(), self.other_clone.parent_id())
1286
1287 # can't unprotect snap with children
1288 assert_raises(ImageBusy, self.image.unprotect_snap, 'snap1')
1289
1290 # 2 children, check that cannot remove the parent snap
1291 assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
1292
1293 # close and remove other pool's clone
1294 self.other_clone.close()
1295 self.rbd.remove(other_ioctx, other_clone_name)
1296
1297 # check that we cannot yet remove the parent snap
1298 assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
1299
1300 other_ioctx.close()
1301 rados.delete_pool(pool_name2)
1302
1303 # unprotect, remove parent snap happen in cleanup, and should succeed
1304
1305 def test_stat(self):
1306 image_info = self.image.stat()
1307 clone_info = self.clone.stat()
1308 eq(clone_info['size'], image_info['size'])
1309 eq(clone_info['size'], self.clone.overlap())
1310
1311 def test_resize_stat(self):
1312 self.clone.resize(IMG_SIZE // 2)
1313 image_info = self.image.stat()
1314 clone_info = self.clone.stat()
1315 eq(clone_info['size'], IMG_SIZE // 2)
1316 eq(image_info['size'], IMG_SIZE)
1317 eq(self.clone.overlap(), IMG_SIZE // 2)
1318
1319 self.clone.resize(IMG_SIZE * 2)
1320 image_info = self.image.stat()
1321 clone_info = self.clone.stat()
1322 eq(clone_info['size'], IMG_SIZE * 2)
1323 eq(image_info['size'], IMG_SIZE)
1324 eq(self.clone.overlap(), IMG_SIZE // 2)
1325
1326 def test_resize_io(self):
1327 parent_data = self.image.read(IMG_SIZE // 2, 256)
1328 self.image.resize(0)
1329 self.clone.resize(IMG_SIZE // 2 + 128)
1330 child_data = self.clone.read(IMG_SIZE // 2, 128)
1331 eq(child_data, parent_data[:128])
1332 self.clone.resize(IMG_SIZE)
1333 child_data = self.clone.read(IMG_SIZE // 2, 256)
1334 eq(child_data, parent_data[:128] + (b'\0' * 128))
1335 self.clone.resize(IMG_SIZE // 2 + 1)
1336 child_data = self.clone.read(IMG_SIZE // 2, 1)
1337 eq(child_data, parent_data[0:1])
1338 self.clone.resize(0)
1339 self.clone.resize(IMG_SIZE)
1340 child_data = self.clone.read(IMG_SIZE // 2, 256)
1341 eq(child_data, b'\0' * 256)
1342
1343 def test_read(self):
1344 parent_data = self.image.read(IMG_SIZE // 2, 256)
1345 child_data = self.clone.read(IMG_SIZE // 2, 256)
1346 eq(child_data, parent_data)
1347
1348 def test_write(self):
1349 parent_data = self.image.read(IMG_SIZE // 2, 256)
1350 new_data = rand_data(256)
1351 self.clone.write(new_data, IMG_SIZE // 2 + 256)
1352 child_data = self.clone.read(IMG_SIZE // 2 + 256, 256)
1353 eq(child_data, new_data)
1354 child_data = self.clone.read(IMG_SIZE // 2, 256)
1355 eq(child_data, parent_data)
1356 parent_data = self.image.read(IMG_SIZE // 2 + 256, 256)
1357 eq(parent_data, b'\0' * 256)
1358
1359 def check_children(self, expected):
1360 actual = self.image.list_children()
1361 # dedup for cache pools until
1362 # http://tracker.ceph.com/issues/8187 is fixed
1363 deduped = set([(pool_name, image[1]) for image in actual])
1364 eq(deduped, set(expected))
1365
1366 def check_children2(self, expected):
1367 actual = [{k:v for k,v in x.items() if k in expected[0]} \
1368 for x in self.image.list_children2()]
1369 eq(actual, expected)
1370
1371 def check_descendants(self, expected):
1372 eq(list(self.image.list_descendants()), expected)
1373
1374 def get_image_id(self, ioctx, name):
1375 with Image(ioctx, name) as image:
1376 return image.id()
1377
1378 def test_list_children(self):
1379 global ioctx
1380 global features
1381 self.image.set_snap('snap1')
1382 self.check_children([(pool_name, self.clone_name)])
1383 self.check_children2(
1384 [{'pool': pool_name, 'pool_namespace': '',
1385 'image': self.clone_name, 'trash': False,
1386 'id': self.get_image_id(ioctx, self.clone_name)}])
1387 self.check_descendants(
1388 [{'pool': pool_name, 'pool_namespace': '',
1389 'image': self.clone_name, 'trash': False,
1390 'id': self.get_image_id(ioctx, self.clone_name)}])
1391 self.clone.close()
1392 self.rbd.remove(ioctx, self.clone_name)
1393 eq(self.image.list_children(), [])
1394 eq(list(self.image.list_children2()), [])
1395 eq(list(self.image.list_descendants()), [])
1396
1397 clone_name = get_temp_image_name() + '_'
1398 expected_children = []
1399 expected_children2 = []
1400 for i in range(10):
1401 self.rbd.clone(ioctx, image_name, 'snap1', ioctx,
1402 clone_name + str(i), features)
1403 expected_children.append((pool_name, clone_name + str(i)))
1404 expected_children2.append(
1405 {'pool': pool_name, 'pool_namespace': '',
1406 'image': clone_name + str(i), 'trash': False,
1407 'id': self.get_image_id(ioctx, clone_name + str(i))})
1408 self.check_children(expected_children)
1409 self.check_children2(expected_children2)
1410 self.check_descendants(expected_children2)
1411
1412 image6_id = self.get_image_id(ioctx, clone_name + str(5))
1413 RBD().trash_move(ioctx, clone_name + str(5), 0)
1414 expected_children.remove((pool_name, clone_name + str(5)))
1415 for item in expected_children2:
1416 for k, v in item.items():
1417 if v == image6_id:
1418 item["trash"] = True
1419 self.check_children(expected_children)
1420 self.check_children2(expected_children2)
1421 self.check_descendants(expected_children2)
1422
1423 RBD().trash_restore(ioctx, image6_id, clone_name + str(5))
1424 expected_children.append((pool_name, clone_name + str(5)))
1425 for item in expected_children2:
1426 for k, v in item.items():
1427 if v == image6_id:
1428 item["trash"] = False
1429 self.check_children(expected_children)
1430 self.check_children2(expected_children2)
1431 self.check_descendants(expected_children2)
1432
1433 for i in range(10):
1434 self.rbd.remove(ioctx, clone_name + str(i))
1435 expected_children.remove((pool_name, clone_name + str(i)))
1436 expected_children2.pop(0)
1437 self.check_children(expected_children)
1438 self.check_children2(expected_children2)
1439 self.check_descendants(expected_children2)
1440
1441 eq(self.image.list_children(), [])
1442 eq(list(self.image.list_children2()), [])
1443 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name,
1444 features)
1445 self.check_children([(pool_name, self.clone_name)])
1446 self.check_children2(
1447 [{'pool': pool_name, 'pool_namespace': '',
1448 'image': self.clone_name, 'trash': False,
1449 'id': self.get_image_id(ioctx, self.clone_name)}])
1450 self.check_descendants(
1451 [{'pool': pool_name, 'pool_namespace': '',
1452 'image': self.clone_name, 'trash': False,
1453 'id': self.get_image_id(ioctx, self.clone_name)}])
1454 self.clone = Image(ioctx, self.clone_name)
1455
1456 def test_flatten_errors(self):
1457 # test that we can't flatten a non-clone
1458 assert_raises(InvalidArgument, self.image.flatten)
1459
1460 # test that we can't flatten a snapshot
1461 self.clone.create_snap('snap2')
1462 self.clone.set_snap('snap2')
1463 assert_raises(ReadOnlyImage, self.clone.flatten)
1464 self.clone.remove_snap('snap2')
1465
1466 def check_flatten_with_order(self, new_order):
1467 global ioctx
1468 global features
1469 clone_name2 = get_temp_image_name()
1470 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1471 features, new_order)
1472 #with Image(ioctx, 'clone2') as clone:
1473 clone2 = Image(ioctx, clone_name2)
1474 clone2.flatten()
1475 eq(clone2.overlap(), 0)
1476 clone2.close()
1477 self.rbd.remove(ioctx, clone_name2)
1478
1479 # flatten after resizing to non-block size
1480 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1481 features, new_order)
1482 with Image(ioctx, clone_name2) as clone:
1483 clone.resize(IMG_SIZE // 2 - 1)
1484 clone.flatten()
1485 eq(0, clone.overlap())
1486 self.rbd.remove(ioctx, clone_name2)
1487
1488 # flatten after resizing to non-block size
1489 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1490 features, new_order)
1491 with Image(ioctx, clone_name2) as clone:
1492 clone.resize(IMG_SIZE // 2 + 1)
1493 clone.flatten()
1494 eq(clone.overlap(), 0)
1495 self.rbd.remove(ioctx, clone_name2)
1496
1497 def test_flatten_basic(self):
1498 self.check_flatten_with_order(IMG_ORDER)
1499
1500 def test_flatten_smaller_order(self):
1501 self.check_flatten_with_order(IMG_ORDER - 2)
1502
1503 def test_flatten_larger_order(self):
1504 self.check_flatten_with_order(IMG_ORDER + 2)
1505
1506 def test_flatten_drops_cache(self):
1507 global ioctx
1508 global features
1509 clone_name2 = get_temp_image_name()
1510 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1511 features, IMG_ORDER)
1512 with Image(ioctx, clone_name2) as clone:
1513 with Image(ioctx, clone_name2) as clone2:
1514 # cache object non-existence
1515 data = clone.read(IMG_SIZE // 2, 256)
1516 clone2_data = clone2.read(IMG_SIZE // 2, 256)
1517 eq(data, clone2_data)
1518 clone.flatten()
1519 assert_raises(ImageNotFound, clone.parent_info)
1520 assert_raises(ImageNotFound, clone2.parent_info)
1521 assert_raises(ImageNotFound, clone.parent_id)
1522 assert_raises(ImageNotFound, clone2.parent_id)
1523 after_flatten = clone.read(IMG_SIZE // 2, 256)
1524 eq(data, after_flatten)
1525 after_flatten = clone2.read(IMG_SIZE // 2, 256)
1526 eq(data, after_flatten)
1527 self.rbd.remove(ioctx, clone_name2)
1528
1529 def test_flatten_multi_level(self):
1530 self.clone.create_snap('snap2')
1531 self.clone.protect_snap('snap2')
1532 clone_name3 = get_temp_image_name()
1533 self.rbd.clone(ioctx, self.clone_name, 'snap2', ioctx, clone_name3,
1534 features)
1535 self.clone.flatten()
1536 with Image(ioctx, clone_name3) as clone3:
1537 clone3.flatten()
1538 self.clone.unprotect_snap('snap2')
1539 self.clone.remove_snap('snap2')
1540 self.rbd.remove(ioctx, clone_name3)
1541
1542 def test_flatten_with_progress(self):
1543 d = {'received_callback': False}
1544 def progress_cb(current, total):
1545 d['received_callback'] = True
1546 return 0
1547
1548 global ioctx
1549 global features
1550 clone_name = get_temp_image_name()
1551 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name,
1552 features, 0)
1553 with Image(ioctx, clone_name) as clone:
1554 clone.flatten(on_progress=progress_cb)
1555 self.rbd.remove(ioctx, clone_name)
1556 eq(True, d['received_callback'])
1557
1558 def test_resize_flatten_multi_level(self):
1559 self.clone.create_snap('snap2')
1560 self.clone.protect_snap('snap2')
1561 clone_name3 = get_temp_image_name()
1562 self.rbd.clone(ioctx, self.clone_name, 'snap2', ioctx, clone_name3,
1563 features)
1564 self.clone.resize(1)
1565 orig_data = self.image.read(0, 256)
1566 with Image(ioctx, clone_name3) as clone3:
1567 clone3_data = clone3.read(0, 256)
1568 eq(orig_data, clone3_data)
1569 self.clone.flatten()
1570 with Image(ioctx, clone_name3) as clone3:
1571 clone3_data = clone3.read(0, 256)
1572 eq(orig_data, clone3_data)
1573 self.rbd.remove(ioctx, clone_name3)
1574 self.clone.unprotect_snap('snap2')
1575 self.clone.remove_snap('snap2')
1576
1577 def test_trash_snapshot(self):
1578 self.image.create_snap('snap2')
1579 global features
1580 clone_name = get_temp_image_name()
1581 rados.conf_set("rbd_default_clone_format", "2")
1582 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name, features)
1583 rados.conf_set("rbd_default_clone_format", "auto")
1584
1585 self.image.remove_snap('snap2')
1586
1587 snaps = [s for s in self.image.list_snaps() if s['name'] != 'snap1']
1588 eq([RBD_SNAP_NAMESPACE_TYPE_TRASH], [s['namespace'] for s in snaps])
1589 eq([{'original_name' : 'snap2'}], [s['trash'] for s in snaps])
1590
1591 self.rbd.remove(ioctx, clone_name)
1592 eq([], [s for s in self.image.list_snaps() if s['name'] != 'snap1'])
1593
1594 class TestExclusiveLock(object):
1595
1596 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK])
1597 def setUp(self):
1598 global rados2
1599 rados2 = Rados(conffile='')
1600 rados2.connect()
1601 global ioctx2
1602 ioctx2 = rados2.open_ioctx(pool_name)
1603 create_image()
1604
1605 def tearDown(self):
1606 remove_image()
1607 global ioctx2
1608 ioctx2.close()
1609 global rados2
1610 rados2.shutdown()
1611
1612 def test_ownership(self):
1613 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1614 image1.write(b'0'*256, 0)
1615 eq(image1.is_exclusive_lock_owner(), True)
1616 eq(image2.is_exclusive_lock_owner(), False)
1617
1618 def test_snapshot_leadership(self):
1619 with Image(ioctx, image_name) as image:
1620 image.create_snap('snap')
1621 eq(image.is_exclusive_lock_owner(), True)
1622 try:
1623 with Image(ioctx, image_name) as image:
1624 image.write(b'0'*256, 0)
1625 eq(image.is_exclusive_lock_owner(), True)
1626 image.set_snap('snap')
1627 eq(image.is_exclusive_lock_owner(), False)
1628 with Image(ioctx, image_name, snapshot='snap') as image:
1629 eq(image.is_exclusive_lock_owner(), False)
1630 finally:
1631 with Image(ioctx, image_name) as image:
1632 image.remove_snap('snap')
1633
1634 def test_read_only_leadership(self):
1635 with Image(ioctx, image_name, read_only=True) as image:
1636 eq(image.is_exclusive_lock_owner(), False)
1637
1638 def test_follower_flatten(self):
1639 with Image(ioctx, image_name) as image:
1640 image.create_snap('snap')
1641 image.protect_snap('snap')
1642 try:
1643 RBD().clone(ioctx, image_name, 'snap', ioctx, 'clone', features)
1644 with Image(ioctx, 'clone') as image1, Image(ioctx2, 'clone') as image2:
1645 data = rand_data(256)
1646 image1.write(data, 0)
1647 image2.flatten()
1648 assert_raises(ImageNotFound, image1.parent_info)
1649 assert_raises(ImageNotFound, image1.parent_id)
1650 parent = True
1651 for x in range(30):
1652 try:
1653 image2.parent_info()
1654 except ImageNotFound:
1655 parent = False
1656 break
1657 eq(False, parent)
1658 finally:
1659 RBD().remove(ioctx, 'clone')
1660 with Image(ioctx, image_name) as image:
1661 image.unprotect_snap('snap')
1662 image.remove_snap('snap')
1663
1664 def test_follower_resize(self):
1665 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1666 image1.write(b'0'*256, 0)
1667 for new_size in [IMG_SIZE * 2, IMG_SIZE // 2]:
1668 image2.resize(new_size);
1669 eq(new_size, image1.size())
1670 for x in range(30):
1671 if new_size == image2.size():
1672 break
1673 time.sleep(1)
1674 eq(new_size, image2.size())
1675
1676 def test_follower_snap_create(self):
1677 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1678 image2.create_snap('snap1')
1679 image1.remove_snap('snap1')
1680
1681 def test_follower_snap_rollback(self):
1682 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1683 image1.create_snap('snap')
1684 try:
1685 assert_raises(ReadOnlyImage, image2.rollback_to_snap, 'snap')
1686 image1.rollback_to_snap('snap')
1687 finally:
1688 image1.remove_snap('snap')
1689
1690 def test_follower_discard(self):
1691 global rados
1692 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1693 data = rand_data(256)
1694 image1.write(data, 0)
1695 image2.discard(0, 256)
1696 eq(image1.is_exclusive_lock_owner(), False)
1697 eq(image2.is_exclusive_lock_owner(), True)
1698 read = image2.read(0, 256)
1699 if rados.conf_get('rbd_skip_partial_discard') == 'false':
1700 eq(256 * b'\0', read)
1701 else:
1702 eq(data, read)
1703
1704 def test_follower_write(self):
1705 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1706 data = rand_data(256)
1707 image1.write(data, 0)
1708 image2.write(data, IMG_SIZE // 2)
1709 eq(image1.is_exclusive_lock_owner(), False)
1710 eq(image2.is_exclusive_lock_owner(), True)
1711 for offset in [0, IMG_SIZE // 2]:
1712 read = image2.read(offset, 256)
1713 eq(data, read)
1714 def test_acquire_release_lock(self):
1715 with Image(ioctx, image_name) as image:
1716 image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
1717 image.lock_release()
1718
1719 def test_break_lock(self):
1720 blacklist_rados = Rados(conffile='')
1721 blacklist_rados.connect()
1722 try:
1723 blacklist_ioctx = blacklist_rados.open_ioctx(pool_name)
1724 try:
1725 rados2.conf_set('rbd_blacklist_on_break_lock', 'true')
1726 with Image(ioctx2, image_name) as image, \
1727 Image(blacklist_ioctx, image_name) as blacklist_image:
1728 blacklist_image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
1729 assert_raises(ReadOnlyImage, image.lock_acquire,
1730 RBD_LOCK_MODE_EXCLUSIVE)
1731
1732 lock_owners = list(image.lock_get_owners())
1733 eq(1, len(lock_owners))
1734 eq(RBD_LOCK_MODE_EXCLUSIVE, lock_owners[0]['mode'])
1735 image.lock_break(RBD_LOCK_MODE_EXCLUSIVE,
1736 lock_owners[0]['owner'])
1737
1738 assert_raises(ConnectionShutdown,
1739 blacklist_image.is_exclusive_lock_owner)
1740
1741 blacklist_rados.wait_for_latest_osdmap()
1742 data = rand_data(256)
1743 assert_raises(ConnectionShutdown,
1744 blacklist_image.write, data, 0)
1745
1746 image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
1747
1748 try:
1749 blacklist_image.close()
1750 except ConnectionShutdown:
1751 pass
1752 finally:
1753 blacklist_ioctx.close()
1754 finally:
1755 blacklist_rados.shutdown()
1756
1757 class TestMirroring(object):
1758
1759 @staticmethod
1760 def check_info(info, global_id, state, primary=None):
1761 eq(global_id, info['global_id'])
1762 eq(state, info['state'])
1763 if primary is not None:
1764 eq(primary, info['primary'])
1765
1766 def setUp(self):
1767 self.rbd = RBD()
1768 self.initial_mirror_mode = self.rbd.mirror_mode_get(ioctx)
1769 self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL)
1770 create_image()
1771 self.image = Image(ioctx, image_name)
1772
1773 def tearDown(self):
1774 self.image.close()
1775 remove_image()
1776 self.rbd.mirror_mode_set(ioctx, self.initial_mirror_mode)
1777
1778
1779 def test_mirror_peer(self):
1780 eq([], list(self.rbd.mirror_peer_list(ioctx)))
1781 cluster_name = "test_cluster"
1782 client_name = "test_client"
1783 uuid = self.rbd.mirror_peer_add(ioctx, cluster_name, client_name)
1784 assert(uuid)
1785 peer = {
1786 'uuid' : uuid,
1787 'cluster_name' : cluster_name,
1788 'client_name' : client_name,
1789 }
1790 eq([peer], list(self.rbd.mirror_peer_list(ioctx)))
1791 cluster_name = "test_cluster1"
1792 self.rbd.mirror_peer_set_cluster(ioctx, uuid, cluster_name)
1793 client_name = "test_client1"
1794 self.rbd.mirror_peer_set_client(ioctx, uuid, client_name)
1795 peer = {
1796 'uuid' : uuid,
1797 'cluster_name' : cluster_name,
1798 'client_name' : client_name,
1799 }
1800 eq([peer], list(self.rbd.mirror_peer_list(ioctx)))
1801
1802 attribs = {
1803 RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST: 'host1',
1804 RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY: 'abc'
1805 }
1806 self.rbd.mirror_peer_set_attributes(ioctx, uuid, attribs)
1807 eq(attribs, self.rbd.mirror_peer_get_attributes(ioctx, uuid))
1808
1809 self.rbd.mirror_peer_remove(ioctx, uuid)
1810 eq([], list(self.rbd.mirror_peer_list(ioctx)))
1811
1812 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK,
1813 RBD_FEATURE_JOURNALING])
1814 def test_mirror_image(self):
1815
1816 self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE)
1817 self.image.mirror_image_disable(True)
1818 info = self.image.mirror_image_get_info()
1819 self.check_info(info, '', RBD_MIRROR_IMAGE_DISABLED, False)
1820
1821 self.image.mirror_image_enable()
1822 info = self.image.mirror_image_get_info()
1823 global_id = info['global_id']
1824 self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
1825
1826 self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL)
1827 fail = False
1828 try:
1829 self.image.mirror_image_disable(True)
1830 except InvalidArgument:
1831 fail = True
1832 eq(True, fail) # Fails because of mirror mode pool
1833
1834 self.image.mirror_image_demote()
1835 info = self.image.mirror_image_get_info()
1836 self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, False)
1837
1838 self.image.mirror_image_resync()
1839
1840 self.image.mirror_image_promote(True)
1841 info = self.image.mirror_image_get_info()
1842 self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
1843
1844 fail = False
1845 try:
1846 self.image.mirror_image_resync()
1847 except InvalidArgument:
1848 fail = True
1849 eq(True, fail) # Fails because it is primary
1850
1851 status = self.image.mirror_image_get_status()
1852 eq(image_name, status['name'])
1853 eq(False, status['up'])
1854 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
1855 info = status['info']
1856 self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
1857
1858 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK,
1859 RBD_FEATURE_JOURNALING])
1860 def test_mirror_image_status(self):
1861 info = self.image.mirror_image_get_info()
1862 global_id = info['global_id']
1863 state = info['state']
1864 primary = info['primary']
1865
1866 status = self.image.mirror_image_get_status()
1867 eq(image_name, status['name'])
1868 eq(False, status['up'])
1869 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
1870 info = status['info']
1871 self.check_info(info, global_id, state, primary)
1872
1873 images = list(self.rbd.mirror_image_status_list(ioctx))
1874 eq(1, len(images))
1875 status = images[0]
1876 eq(image_name, status['name'])
1877 eq(False, status['up'])
1878 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
1879 info = status['info']
1880 self.check_info(info, global_id, state)
1881
1882 states = self.rbd.mirror_image_status_summary(ioctx)
1883 eq([(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, 1)], states)
1884
1885 assert_raises(ImageNotFound, self.image.mirror_image_get_instance_id)
1886 instance_ids = list(self.rbd.mirror_image_instance_id_list(ioctx))
1887 eq(0, len(instance_ids))
1888
1889 N = 65
1890 for i in range(N):
1891 self.rbd.create(ioctx, image_name + str(i), IMG_SIZE, IMG_ORDER,
1892 old_format=False, features=int(features))
1893 images = list(self.rbd.mirror_image_status_list(ioctx))
1894 eq(N + 1, len(images))
1895 for i in range(N):
1896 self.rbd.remove(ioctx, image_name + str(i))
1897
1898
1899 class TestTrash(object):
1900
1901 def setUp(self):
1902 global rados2
1903 rados2 = Rados(conffile='')
1904 rados2.connect()
1905 global ioctx2
1906 ioctx2 = rados2.open_ioctx(pool_name)
1907
1908 def tearDown(self):
1909 global ioctx2
1910 ioctx2.close()
1911 global rados2
1912 rados2.shutdown()
1913
1914 def test_move(self):
1915 create_image()
1916 with Image(ioctx, image_name) as image:
1917 image_id = image.id()
1918
1919 RBD().trash_move(ioctx, image_name, 1000)
1920 RBD().trash_remove(ioctx, image_id, True)
1921
1922 def test_purge(self):
1923 create_image()
1924 with Image(ioctx, image_name) as image:
1925 image_name1 = image_name
1926 image_id1 = image.id()
1927
1928 create_image()
1929 with Image(ioctx, image_name) as image:
1930 image_name2 = image_name
1931 image_id2 = image.id()
1932
1933 RBD().trash_move(ioctx, image_name1, 0)
1934 RBD().trash_move(ioctx, image_name2, 1000)
1935 RBD().trash_purge(ioctx, datetime.now())
1936
1937 entries = list(RBD().trash_list(ioctx))
1938 eq([image_id2], [x['id'] for x in entries])
1939 RBD().trash_remove(ioctx, image_id2, True)
1940
1941 def test_remove_denied(self):
1942 create_image()
1943 with Image(ioctx, image_name) as image:
1944 image_id = image.id()
1945
1946 RBD().trash_move(ioctx, image_name, 1000)
1947 assert_raises(PermissionError, RBD().trash_remove, ioctx, image_id)
1948 RBD().trash_remove(ioctx, image_id, True)
1949
1950 def test_remove(self):
1951 create_image()
1952 with Image(ioctx, image_name) as image:
1953 image_id = image.id()
1954
1955 RBD().trash_move(ioctx, image_name, 0)
1956 RBD().trash_remove(ioctx, image_id)
1957
1958 def test_remove_with_progress(self):
1959 d = {'received_callback': False}
1960 def progress_cb(current, total):
1961 d['received_callback'] = True
1962 return 0
1963
1964 create_image()
1965 with Image(ioctx, image_name) as image:
1966 image_id = image.id()
1967
1968 RBD().trash_move(ioctx, image_name, 0)
1969 RBD().trash_remove(ioctx, image_id, on_progress=progress_cb)
1970 eq(True, d['received_callback'])
1971
1972 def test_get(self):
1973 create_image()
1974 with Image(ioctx, image_name) as image:
1975 image_id = image.id()
1976
1977 RBD().trash_move(ioctx, image_name, 1000)
1978
1979 info = RBD().trash_get(ioctx, image_id)
1980 eq(image_id, info['id'])
1981 eq(image_name, info['name'])
1982 eq('USER', info['source'])
1983 assert(info['deferment_end_time'] > info['deletion_time'])
1984
1985 RBD().trash_remove(ioctx, image_id, True)
1986
1987 def test_list(self):
1988 create_image()
1989 with Image(ioctx, image_name) as image:
1990 image_id1 = image.id()
1991 image_name1 = image_name
1992 RBD().trash_move(ioctx, image_name, 1000)
1993
1994 create_image()
1995 with Image(ioctx, image_name) as image:
1996 image_id2 = image.id()
1997 image_name2 = image_name
1998 RBD().trash_move(ioctx, image_name, 1000)
1999
2000 entries = list(RBD().trash_list(ioctx))
2001 for e in entries:
2002 if e['id'] == image_id1:
2003 eq(e['name'], image_name1)
2004 elif e['id'] == image_id2:
2005 eq(e['name'], image_name2)
2006 else:
2007 assert False
2008 eq(e['source'], 'USER')
2009 assert e['deferment_end_time'] > e['deletion_time']
2010
2011 RBD().trash_remove(ioctx, image_id1, True)
2012 RBD().trash_remove(ioctx, image_id2, True)
2013
2014 def test_restore(self):
2015 create_image()
2016 with Image(ioctx, image_name) as image:
2017 image_id = image.id()
2018 RBD().trash_move(ioctx, image_name, 1000)
2019 RBD().trash_restore(ioctx, image_id, image_name)
2020 remove_image()
2021
2022 def test_create_group():
2023 create_group()
2024 remove_group()
2025
2026 def test_rename_group():
2027 create_group()
2028 if group_name is not None:
2029 rename_group()
2030 eq(["new" + group_name], RBD().group_list(ioctx))
2031 RBD().group_remove(ioctx, "new" + group_name)
2032 else:
2033 remove_group()
2034
2035 def test_list_groups_empty():
2036 eq([], RBD().group_list(ioctx))
2037
2038 @with_setup(create_group, remove_group)
2039 def test_list_groups():
2040 eq([group_name], RBD().group_list(ioctx))
2041
2042 @with_setup(create_group)
2043 def test_list_groups_after_removed():
2044 remove_group()
2045 eq([], RBD().group_list(ioctx))
2046
2047 class TestGroups(object):
2048
2049 def setUp(self):
2050 global snap_name
2051 self.rbd = RBD()
2052 create_image()
2053 self.image_names = [image_name]
2054 self.image = Image(ioctx, image_name)
2055
2056 create_group()
2057 snap_name = get_temp_snap_name()
2058 self.group = Group(ioctx, group_name)
2059
2060 def tearDown(self):
2061 remove_group()
2062 self.image = None
2063 for name in self.image_names:
2064 RBD().remove(ioctx, name)
2065
2066 def test_group_image_add(self):
2067 self.group.add_image(ioctx, image_name)
2068
2069 def test_group_image_list_empty(self):
2070 eq([], list(self.group.list_images()))
2071
2072 def test_group_image_list(self):
2073 eq([], list(self.group.list_images()))
2074 self.group.add_image(ioctx, image_name)
2075 eq([image_name], [img['name'] for img in self.group.list_images()])
2076
2077 def test_group_image_list_move_to_trash(self):
2078 eq([], list(self.group.list_images()))
2079 with Image(ioctx, image_name) as image:
2080 image_id = image.id()
2081 self.group.add_image(ioctx, image_name)
2082 eq([image_name], [img['name'] for img in self.group.list_images()])
2083 RBD().trash_move(ioctx, image_name, 0)
2084 eq([], list(self.group.list_images()))
2085 RBD().trash_restore(ioctx, image_id, image_name)
2086
2087 def test_group_image_many_images(self):
2088 eq([], list(self.group.list_images()))
2089 self.group.add_image(ioctx, image_name)
2090
2091 for x in range(0, 20):
2092 create_image()
2093 self.image_names.append(image_name)
2094 self.group.add_image(ioctx, image_name)
2095
2096 self.image_names.sort()
2097 answer = [img['name'] for img in self.group.list_images()]
2098 answer.sort()
2099 eq(self.image_names, answer)
2100
2101 def test_group_image_remove(self):
2102 eq([], list(self.group.list_images()))
2103 self.group.add_image(ioctx, image_name)
2104 with Image(ioctx, image_name) as image:
2105 eq(RBD_OPERATION_FEATURE_GROUP,
2106 image.op_features() & RBD_OPERATION_FEATURE_GROUP)
2107 group = image.group()
2108 eq(group_name, group['name'])
2109
2110 eq([image_name], [img['name'] for img in self.group.list_images()])
2111 self.group.remove_image(ioctx, image_name)
2112 eq([], list(self.group.list_images()))
2113 with Image(ioctx, image_name) as image:
2114 eq(0, image.op_features() & RBD_OPERATION_FEATURE_GROUP)
2115
2116 def test_group_snap(self):
2117 global snap_name
2118 eq([], list(self.group.list_snaps()))
2119 self.group.create_snap(snap_name)
2120 eq([snap_name], [snap['name'] for snap in self.group.list_snaps()])
2121
2122 for snap in self.image.list_snaps():
2123 eq(rbd.RBD_SNAP_NAMESPACE_TYPE_GROUP, snap['namespace'])
2124 info = snap['group']
2125 eq(group_name, info['group_name'])
2126 eq(snap_name, info['group_snap_name'])
2127
2128 self.group.remove_snap(snap_name)
2129 eq([], list(self.group.list_snaps()))
2130
2131 def test_group_snap_list_many(self):
2132 global snap_name
2133 eq([], list(self.group.list_snaps()))
2134 snap_names = []
2135 for x in range(0, 20):
2136 snap_names.append(snap_name)
2137 self.group.create_snap(snap_name)
2138 snap_name = get_temp_snap_name()
2139
2140 snap_names.sort()
2141 answer = [snap['name'] for snap in self.group.list_snaps()]
2142 answer.sort()
2143 eq(snap_names, answer)
2144
2145 def test_group_snap_namespace(self):
2146 global snap_name
2147 eq([], list(self.group.list_snaps()))
2148 self.group.add_image(ioctx, image_name)
2149 self.group.create_snap(snap_name)
2150 eq(1, len([snap['name'] for snap in self.image.list_snaps()]))
2151 self.group.remove_image(ioctx, image_name)
2152 self.group.remove_snap(snap_name)
2153 eq([], list(self.group.list_snaps()))
2154
2155 def test_group_snap_rename(self):
2156 global snap_name
2157 new_snap_name = "new" + snap_name
2158
2159 eq([], list(self.group.list_snaps()))
2160 self.group.create_snap(snap_name)
2161 eq([snap_name], [snap['name'] for snap in self.group.list_snaps()])
2162 self.group.rename_snap(snap_name, new_snap_name)
2163 eq([new_snap_name], [snap['name'] for snap in self.group.list_snaps()])
2164 self.group.remove_snap(new_snap_name)
2165 eq([], list(self.group.list_snaps()))
2166
2167 def test_group_snap_rollback(self):
2168 eq([], list(self.group.list_images()))
2169 self.group.add_image(ioctx, image_name)
2170 with Image(ioctx, image_name) as image:
2171 image.write(b'\0' * 256, 0)
2172 read = image.read(0, 256)
2173 eq(read, b'\0' * 256)
2174
2175 global snap_name
2176 eq([], list(self.group.list_snaps()))
2177 self.group.create_snap(snap_name)
2178 eq([snap_name], [snap['name'] for snap in self.group.list_snaps()])
2179
2180 with Image(ioctx, image_name) as image:
2181 data = rand_data(256)
2182 image.write(data, 0)
2183 read = image.read(0, 256)
2184 eq(read, data)
2185
2186 self.group.rollback_to_snap(snap_name)
2187 with Image(ioctx, image_name) as image:
2188 read = image.read(0, 256)
2189 eq(read, b'\0' * 256)
2190
2191 self.group.remove_image(ioctx, image_name)
2192 eq([], list(self.group.list_images()))
2193 self.group.remove_snap(snap_name)
2194 eq([], list(self.group.list_snaps()))
2195
2196 @with_setup(create_image, remove_image)
2197 def test_rename():
2198 rbd = RBD()
2199 image_name2 = get_temp_image_name()
2200
2201 class TestMigration(object):
2202
2203 def test_migration(self):
2204 create_image()
2205 RBD().migration_prepare(ioctx, image_name, ioctx, image_name, features=63,
2206 order=23, stripe_unit=1<<23, stripe_count=1,
2207 data_pool=None)
2208
2209 status = RBD().migration_status(ioctx, image_name)
2210 eq(image_name, status['source_image_name'])
2211 eq(image_name, status['dest_image_name'])
2212 eq(RBD_IMAGE_MIGRATION_STATE_PREPARED, status['state'])
2213
2214 RBD().migration_execute(ioctx, image_name)
2215 RBD().migration_commit(ioctx, image_name)
2216 remove_image()
2217
2218 def test_migration_with_progress(self):
2219 d = {'received_callback': False}
2220 def progress_cb(current, total):
2221 d['received_callback'] = True
2222 return 0
2223
2224 create_image()
2225 RBD().migration_prepare(ioctx, image_name, ioctx, image_name, features=63,
2226 order=23, stripe_unit=1<<23, stripe_count=1,
2227 data_pool=None)
2228 RBD().migration_execute(ioctx, image_name, on_progress=progress_cb)
2229 eq(True, d['received_callback'])
2230 d['received_callback'] = False
2231
2232 RBD().migration_commit(ioctx, image_name, on_progress=progress_cb)
2233 eq(True, d['received_callback'])
2234 remove_image()
2235
2236 def test_migrate_abort(self):
2237 create_image()
2238 RBD().migration_prepare(ioctx, image_name, ioctx, image_name, features=63,
2239 order=23, stripe_unit=1<<23, stripe_count=1,
2240 data_pool=None)
2241 RBD().migration_abort(ioctx, image_name)
2242 remove_image()
2243
2244 def test_migrate_abort_with_progress(self):
2245 d = {'received_callback': False}
2246 def progress_cb(current, total):
2247 d['received_callback'] = True
2248 return 0
2249
2250 create_image()
2251 RBD().migration_prepare(ioctx, image_name, ioctx, image_name, features=63,
2252 order=23, stripe_unit=1<<23, stripe_count=1,
2253 data_pool=None)
2254 RBD().migration_abort(ioctx, image_name, on_progress=progress_cb)
2255 eq(True, d['received_callback'])
2256 remove_image()