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