]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/pybind/test_rbd.py
98d2a7051a382d644256da017d8c1987ecc26c23
[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)
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_read(self):
605 data = self.image.read(0, 20)
606 eq(data, b'\0' * 20)
607
608 def test_read_with_fadvise_flags(self):
609 data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED)
610 eq(data, b'\0' * 20)
611 data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM)
612 eq(data, b'\0' * 20)
613
614 def test_large_write(self):
615 data = rand_data(IMG_SIZE)
616 self.image.write(data, 0)
617
618 def test_large_read(self):
619 data = self.image.read(0, IMG_SIZE)
620 eq(data, b'\0' * IMG_SIZE)
621
622 def test_write_read(self):
623 data = rand_data(256)
624 offset = 50
625 self.image.write(data, offset)
626 read = self.image.read(offset, 256)
627 eq(data, read)
628
629 def test_read_bad_offset(self):
630 assert_raises(InvalidArgument, self.image.read, IMG_SIZE + 1, IMG_SIZE)
631
632 def test_resize(self):
633 new_size = IMG_SIZE * 2
634 self.image.resize(new_size)
635 info = self.image.stat()
636 check_stat(info, new_size, IMG_ORDER)
637
638 def test_resize_allow_shrink_False(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 assert_raises(InvalidArgument, self.image.resize, IMG_SIZE, False)
644
645 def test_size(self):
646 eq(IMG_SIZE, self.image.size())
647 self.image.create_snap('snap1')
648 new_size = IMG_SIZE * 2
649 self.image.resize(new_size)
650 eq(new_size, self.image.size())
651 self.image.create_snap('snap2')
652 self.image.set_snap('snap2')
653 eq(new_size, self.image.size())
654 self.image.set_snap('snap1')
655 eq(IMG_SIZE, self.image.size())
656 self.image.set_snap(None)
657 eq(new_size, self.image.size())
658 self.image.remove_snap('snap1')
659 self.image.remove_snap('snap2')
660
661 def test_resize_down(self):
662 new_size = IMG_SIZE // 2
663 data = rand_data(256)
664 self.image.write(data, IMG_SIZE // 2);
665 self.image.resize(new_size)
666 self.image.resize(IMG_SIZE)
667 read = self.image.read(IMG_SIZE // 2, 256)
668 eq(b'\0' * 256, read)
669
670 def test_resize_bytes(self):
671 new_size = IMG_SIZE // 2 - 5
672 data = rand_data(256)
673 self.image.write(data, IMG_SIZE // 2 - 10);
674 self.image.resize(new_size)
675 self.image.resize(IMG_SIZE)
676 read = self.image.read(IMG_SIZE // 2 - 10, 5)
677 eq(data[:5], read)
678 read = self.image.read(IMG_SIZE // 2 - 5, 251)
679 eq(b'\0' * 251, read)
680
681 def _test_copy(self, features=None, order=None, stripe_unit=None,
682 stripe_count=None):
683 global ioctx
684 data = rand_data(256)
685 self.image.write(data, 256)
686 image_name = get_temp_image_name()
687 if features is None:
688 self.image.copy(ioctx, image_name)
689 elif order is None:
690 self.image.copy(ioctx, image_name, features)
691 elif stripe_unit is None:
692 self.image.copy(ioctx, image_name, features, order)
693 elif stripe_count is None:
694 self.image.copy(ioctx, image_name, features, order, stripe_unit)
695 else:
696 self.image.copy(ioctx, image_name, features, order, stripe_unit,
697 stripe_count)
698 assert_raises(ImageExists, self.image.copy, ioctx, image_name)
699 copy = Image(ioctx, image_name)
700 copy_data = copy.read(256, 256)
701 copy.close()
702 self.rbd.remove(ioctx, image_name)
703 eq(data, copy_data)
704
705 def test_copy(self):
706 self._test_copy()
707
708 def test_copy2(self):
709 self._test_copy(self.image.features(), self.image.stat()['order'])
710
711 @require_features([RBD_FEATURE_STRIPINGV2])
712 def test_copy3(self):
713 global features
714 self._test_copy(features, self.image.stat()['order'],
715 self.image.stripe_unit(), self.image.stripe_count())
716
717 def test_deep_copy(self):
718 global ioctx
719 global features
720 self.image.write(b'a' * 256, 0)
721 self.image.create_snap('snap1')
722 self.image.write(b'b' * 256, 0)
723 dst_name = get_temp_image_name()
724 self.image.deep_copy(ioctx, dst_name, features=features,
725 order=self.image.stat()['order'],
726 stripe_unit=self.image.stripe_unit(),
727 stripe_count=self.image.stripe_count(),
728 data_pool=None)
729 self.image.remove_snap('snap1')
730 with Image(ioctx, dst_name, 'snap1') as copy:
731 copy_data = copy.read(0, 256)
732 eq(b'a' * 256, copy_data)
733 with Image(ioctx, dst_name) as copy:
734 copy_data = copy.read(0, 256)
735 eq(b'b' * 256, copy_data)
736 copy.remove_snap('snap1')
737 self.rbd.remove(ioctx, dst_name)
738
739 @require_features([RBD_FEATURE_LAYERING])
740 def test_deep_copy_clone(self):
741 global ioctx
742 global features
743 self.image.write(b'a' * 256, 0)
744 self.image.create_snap('snap1')
745 self.image.write(b'b' * 256, 0)
746 self.image.protect_snap('snap1')
747 clone_name = get_temp_image_name()
748 dst_name = get_temp_image_name()
749 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name)
750 with Image(ioctx, clone_name) as child:
751 child.create_snap('snap1')
752 child.deep_copy(ioctx, dst_name, features=features,
753 order=self.image.stat()['order'],
754 stripe_unit=self.image.stripe_unit(),
755 stripe_count=self.image.stripe_count(),
756 data_pool=None)
757 child.remove_snap('snap1')
758
759 with Image(ioctx, dst_name) as copy:
760 copy_data = copy.read(0, 256)
761 eq(b'a' * 256, copy_data)
762 copy.remove_snap('snap1')
763 self.rbd.remove(ioctx, dst_name)
764 self.rbd.remove(ioctx, clone_name)
765 self.image.unprotect_snap('snap1')
766 self.image.remove_snap('snap1')
767
768 def test_create_snap(self):
769 global ioctx
770 self.image.create_snap('snap1')
771 read = self.image.read(0, 256)
772 eq(read, b'\0' * 256)
773 data = rand_data(256)
774 self.image.write(data, 0)
775 read = self.image.read(0, 256)
776 eq(read, data)
777 at_snapshot = Image(ioctx, image_name, 'snap1')
778 snap_data = at_snapshot.read(0, 256)
779 at_snapshot.close()
780 eq(snap_data, b'\0' * 256)
781 self.image.remove_snap('snap1')
782
783 def test_create_snap_exists(self):
784 self.image.create_snap('snap1')
785 assert_raises(ImageExists, self.image.create_snap, 'snap1')
786 self.image.remove_snap('snap1')
787
788 def test_list_snaps(self):
789 eq([], list(self.image.list_snaps()))
790 self.image.create_snap('snap1')
791 eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
792 self.image.create_snap('snap2')
793 eq(['snap1', 'snap2'], [snap['name'] for snap in self.image.list_snaps()])
794 self.image.remove_snap('snap1')
795 self.image.remove_snap('snap2')
796
797 def test_list_snaps_iterator_auto_close(self):
798 self.image.create_snap('snap1')
799 self.image.list_snaps()
800 self.image.remove_snap('snap1')
801
802 def test_remove_snap(self):
803 eq([], list(self.image.list_snaps()))
804 self.image.create_snap('snap1')
805 eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
806 self.image.remove_snap('snap1')
807 eq([], list(self.image.list_snaps()))
808
809 def test_remove_snap_not_found(self):
810 assert_raises(ImageNotFound, self.image.remove_snap, 'snap1')
811
812 @require_features([RBD_FEATURE_LAYERING])
813 def test_remove_snap2(self):
814 self.image.create_snap('snap1')
815 self.image.protect_snap('snap1')
816 assert(self.image.is_protected_snap('snap1'))
817 self.image.remove_snap2('snap1', RBD_SNAP_REMOVE_UNPROTECT)
818 eq([], list(self.image.list_snaps()))
819
820 def test_remove_snap_by_id(self):
821 eq([], list(self.image.list_snaps()))
822 self.image.create_snap('snap1')
823 eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
824 for snap in self.image.list_snaps():
825 snap_id = snap["id"]
826 self.image.remove_snap_by_id(snap_id)
827 eq([], list(self.image.list_snaps()))
828
829 def test_rename_snap(self):
830 eq([], list(self.image.list_snaps()))
831 self.image.create_snap('snap1')
832 eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
833 self.image.rename_snap("snap1", "snap1-rename")
834 eq(['snap1-rename'], [snap['name'] for snap in self.image.list_snaps()])
835 self.image.remove_snap('snap1-rename')
836 eq([], list(self.image.list_snaps()))
837
838 @require_features([RBD_FEATURE_LAYERING])
839 def test_protect_snap(self):
840 self.image.create_snap('snap1')
841 assert(not self.image.is_protected_snap('snap1'))
842 self.image.protect_snap('snap1')
843 assert(self.image.is_protected_snap('snap1'))
844 assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
845 self.image.unprotect_snap('snap1')
846 assert(not self.image.is_protected_snap('snap1'))
847 self.image.remove_snap('snap1')
848 assert_raises(ImageNotFound, self.image.unprotect_snap, 'snap1')
849 assert_raises(ImageNotFound, self.image.is_protected_snap, 'snap1')
850
851 def test_snap_exists(self):
852 self.image.create_snap('snap1')
853 eq(self.image.snap_exists('snap1'), True)
854 self.image.remove_snap('snap1')
855 eq(self.image.snap_exists('snap1'), False)
856
857 def test_snap_timestamp(self):
858 self.image.create_snap('snap1')
859 eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
860 for snap in self.image.list_snaps():
861 snap_id = snap["id"]
862 time = self.image.get_snap_timestamp(snap_id)
863 assert_not_equal(b'', time.year)
864 assert_not_equal(0, time.year)
865 assert_not_equal(time.year, '1970')
866 self.image.remove_snap('snap1')
867
868 def test_limit_snaps(self):
869 self.image.set_snap_limit(2)
870 eq(2, self.image.get_snap_limit())
871 self.image.create_snap('snap1')
872 self.image.create_snap('snap2')
873 assert_raises(DiskQuotaExceeded, self.image.create_snap, 'snap3')
874 self.image.remove_snap_limit()
875 self.image.create_snap('snap3')
876
877 self.image.remove_snap('snap1')
878 self.image.remove_snap('snap2')
879 self.image.remove_snap('snap3')
880
881 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK])
882 def test_remove_with_exclusive_lock(self):
883 assert_raises(ImageBusy, remove_image)
884
885 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK])
886 def test_remove_with_snap(self):
887 self.image.create_snap('snap1')
888 assert_raises(ImageHasSnapshots, remove_image)
889 self.image.remove_snap('snap1')
890
891 @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK])
892 def test_remove_with_watcher(self):
893 data = rand_data(256)
894 self.image.write(data, 0)
895 assert_raises(ImageBusy, remove_image)
896 read = self.image.read(0, 256)
897 eq(read, data)
898
899 def test_rollback_to_snap(self):
900 self.image.write(b'\0' * 256, 0)
901 self.image.create_snap('snap1')
902 read = self.image.read(0, 256)
903 eq(read, b'\0' * 256)
904 data = rand_data(256)
905 self.image.write(data, 0)
906 read = self.image.read(0, 256)
907 eq(read, data)
908 self.image.rollback_to_snap('snap1')
909 read = self.image.read(0, 256)
910 eq(read, b'\0' * 256)
911 self.image.remove_snap('snap1')
912
913 def test_rollback_to_snap_sparse(self):
914 self.image.create_snap('snap1')
915 read = self.image.read(0, 256)
916 eq(read, b'\0' * 256)
917 data = rand_data(256)
918 self.image.write(data, 0)
919 read = self.image.read(0, 256)
920 eq(read, data)
921 self.image.rollback_to_snap('snap1')
922 read = self.image.read(0, 256)
923 eq(read, b'\0' * 256)
924 self.image.remove_snap('snap1')
925
926 def test_rollback_with_resize(self):
927 read = self.image.read(0, 256)
928 eq(read, b'\0' * 256)
929 data = rand_data(256)
930 self.image.write(data, 0)
931 self.image.create_snap('snap1')
932 read = self.image.read(0, 256)
933 eq(read, data)
934 new_size = IMG_SIZE * 2
935 self.image.resize(new_size)
936 check_stat(self.image.stat(), new_size, IMG_ORDER)
937 self.image.write(data, new_size - 256)
938 self.image.create_snap('snap2')
939 read = self.image.read(new_size - 256, 256)
940 eq(read, data)
941 self.image.rollback_to_snap('snap1')
942 check_stat(self.image.stat(), IMG_SIZE, IMG_ORDER)
943 assert_raises(InvalidArgument, self.image.read, new_size - 256, 256)
944 self.image.rollback_to_snap('snap2')
945 check_stat(self.image.stat(), new_size, IMG_ORDER)
946 read = self.image.read(new_size - 256, 256)
947 eq(read, data)
948 self.image.remove_snap('snap1')
949 self.image.remove_snap('snap2')
950
951 def test_set_snap(self):
952 self.image.write(b'\0' * 256, 0)
953 self.image.create_snap('snap1')
954 read = self.image.read(0, 256)
955 eq(read, b'\0' * 256)
956 data = rand_data(256)
957 self.image.write(data, 0)
958 read = self.image.read(0, 256)
959 eq(read, data)
960 self.image.set_snap('snap1')
961 read = self.image.read(0, 256)
962 eq(read, b'\0' * 256)
963 assert_raises(ReadOnlyImage, self.image.write, data, 0)
964 self.image.remove_snap('snap1')
965
966 def test_set_no_snap(self):
967 self.image.write(b'\0' * 256, 0)
968 self.image.create_snap('snap1')
969 read = self.image.read(0, 256)
970 eq(read, b'\0' * 256)
971 data = rand_data(256)
972 self.image.write(data, 0)
973 read = self.image.read(0, 256)
974 eq(read, data)
975 self.image.set_snap('snap1')
976 read = self.image.read(0, 256)
977 eq(read, b'\0' * 256)
978 assert_raises(ReadOnlyImage, self.image.write, data, 0)
979 self.image.set_snap(None)
980 read = self.image.read(0, 256)
981 eq(read, data)
982 self.image.remove_snap('snap1')
983
984 def test_set_snap_by_id(self):
985 self.image.write(b'\0' * 256, 0)
986 self.image.create_snap('snap1')
987 read = self.image.read(0, 256)
988 eq(read, b'\0' * 256)
989 data = rand_data(256)
990 self.image.write(data, 0)
991 read = self.image.read(0, 256)
992 eq(read, data)
993 snaps = list(self.image.list_snaps())
994 self.image.set_snap_by_id(snaps[0]['id'])
995 read = self.image.read(0, 256)
996 eq(read, b'\0' * 256)
997 assert_raises(ReadOnlyImage, self.image.write, data, 0)
998 self.image.set_snap_by_id(None)
999 read = self.image.read(0, 256)
1000 eq(read, data)
1001 self.image.remove_snap('snap1')
1002
1003 def test_snap_get_name(self):
1004 eq([], list(self.image.list_snaps()))
1005 self.image.create_snap('snap1')
1006 self.image.create_snap('snap2')
1007 self.image.create_snap('snap3')
1008
1009 for snap in self.image.list_snaps():
1010 expected_snap_name = self.image.snap_get_name(snap['id'])
1011 eq(expected_snap_name, snap['name'])
1012 self.image.remove_snap('snap1')
1013 self.image.remove_snap('snap2')
1014 self.image.remove_snap('snap3')
1015 eq([], list(self.image.list_snaps()))
1016
1017 assert_raises(ImageNotFound, self.image.snap_get_name, 1)
1018
1019 def test_snap_get_id(self):
1020 eq([], list(self.image.list_snaps()))
1021 self.image.create_snap('snap1')
1022 self.image.create_snap('snap2')
1023 self.image.create_snap('snap3')
1024
1025 for snap in self.image.list_snaps():
1026 expected_snap_id = self.image.snap_get_id(snap['name'])
1027 eq(expected_snap_id, snap['id'])
1028 self.image.remove_snap('snap1')
1029 self.image.remove_snap('snap2')
1030 self.image.remove_snap('snap3')
1031 eq([], list(self.image.list_snaps()))
1032
1033 assert_raises(ImageNotFound, self.image.snap_get_id, 'snap1')
1034
1035 def test_set_snap_sparse(self):
1036 self.image.create_snap('snap1')
1037 read = self.image.read(0, 256)
1038 eq(read, b'\0' * 256)
1039 data = rand_data(256)
1040 self.image.write(data, 0)
1041 read = self.image.read(0, 256)
1042 eq(read, data)
1043 self.image.set_snap('snap1')
1044 read = self.image.read(0, 256)
1045 eq(read, b'\0' * 256)
1046 assert_raises(ReadOnlyImage, self.image.write, data, 0)
1047 self.image.remove_snap('snap1')
1048
1049 def test_many_snaps(self):
1050 num_snaps = 200
1051 for i in range(num_snaps):
1052 self.image.create_snap(str(i))
1053 snaps = sorted(self.image.list_snaps(),
1054 key=lambda snap: int(snap['name']))
1055 eq(len(snaps), num_snaps)
1056 for i, snap in enumerate(snaps):
1057 eq(snap['size'], IMG_SIZE)
1058 eq(snap['name'], str(i))
1059 for i in range(num_snaps):
1060 self.image.remove_snap(str(i))
1061
1062 def test_set_snap_deleted(self):
1063 self.image.write(b'\0' * 256, 0)
1064 self.image.create_snap('snap1')
1065 read = self.image.read(0, 256)
1066 eq(read, b'\0' * 256)
1067 data = rand_data(256)
1068 self.image.write(data, 0)
1069 read = self.image.read(0, 256)
1070 eq(read, data)
1071 self.image.set_snap('snap1')
1072 self.image.remove_snap('snap1')
1073 assert_raises(ImageNotFound, self.image.read, 0, 256)
1074 self.image.set_snap(None)
1075 read = self.image.read(0, 256)
1076 eq(read, data)
1077
1078 def test_set_snap_recreated(self):
1079 self.image.write(b'\0' * 256, 0)
1080 self.image.create_snap('snap1')
1081 read = self.image.read(0, 256)
1082 eq(read, b'\0' * 256)
1083 data = rand_data(256)
1084 self.image.write(data, 0)
1085 read = self.image.read(0, 256)
1086 eq(read, data)
1087 self.image.set_snap('snap1')
1088 self.image.remove_snap('snap1')
1089 self.image.create_snap('snap1')
1090 assert_raises(ImageNotFound, self.image.read, 0, 256)
1091 self.image.set_snap(None)
1092 read = self.image.read(0, 256)
1093 eq(read, data)
1094 self.image.remove_snap('snap1')
1095
1096 def test_lock_unlock(self):
1097 assert_raises(ImageNotFound, self.image.unlock, '')
1098 self.image.lock_exclusive('')
1099 assert_raises(ImageExists, self.image.lock_exclusive, '')
1100 assert_raises(ImageBusy, self.image.lock_exclusive, 'test')
1101 assert_raises(ImageExists, self.image.lock_shared, '', '')
1102 assert_raises(ImageBusy, self.image.lock_shared, 'foo', '')
1103 self.image.unlock('')
1104
1105 def test_list_lockers(self):
1106 eq([], self.image.list_lockers())
1107 self.image.lock_exclusive('test')
1108 lockers = self.image.list_lockers()
1109 eq(1, len(lockers['lockers']))
1110 _, cookie, _ = lockers['lockers'][0]
1111 eq(cookie, 'test')
1112 eq('', lockers['tag'])
1113 assert lockers['exclusive']
1114 self.image.unlock('test')
1115 eq([], self.image.list_lockers())
1116
1117 num_shared = 10
1118 for i in range(num_shared):
1119 self.image.lock_shared(str(i), 'tag')
1120 lockers = self.image.list_lockers()
1121 eq('tag', lockers['tag'])
1122 assert not lockers['exclusive']
1123 eq(num_shared, len(lockers['lockers']))
1124 cookies = sorted(map(lambda x: x[1], lockers['lockers']))
1125 for i in range(num_shared):
1126 eq(str(i), cookies[i])
1127 self.image.unlock(str(i))
1128 eq([], self.image.list_lockers())
1129
1130 def test_diff_iterate(self):
1131 check_diff(self.image, 0, IMG_SIZE, None, [])
1132 self.image.write(b'a' * 256, 0)
1133 check_diff(self.image, 0, IMG_SIZE, None, [(0, 256, True)])
1134 self.image.write(b'b' * 256, 256)
1135 check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)])
1136 self.image.discard(128, 256)
1137 check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)])
1138
1139 self.image.create_snap('snap1')
1140 self.image.discard(0, 1 << IMG_ORDER)
1141 self.image.create_snap('snap2')
1142 self.image.set_snap('snap2')
1143 check_diff(self.image, 0, IMG_SIZE, 'snap1', [(0, 512, False)])
1144 self.image.remove_snap('snap1')
1145 self.image.remove_snap('snap2')
1146
1147 def test_aio_read(self):
1148 # this is a list so that the local cb() can modify it
1149 retval = [None]
1150 def cb(_, buf):
1151 retval[0] = buf
1152
1153 # test1: success case
1154 comp = self.image.aio_read(0, 20, cb)
1155 comp.wait_for_complete_and_cb()
1156 eq(retval[0], b'\0' * 20)
1157 eq(comp.get_return_value(), 20)
1158 eq(sys.getrefcount(comp), 2)
1159
1160 # test2: error case
1161 retval[0] = 1
1162 comp = self.image.aio_read(IMG_SIZE, 20, cb)
1163 comp.wait_for_complete_and_cb()
1164 eq(None, retval[0])
1165 assert(comp.get_return_value() < 0)
1166 eq(sys.getrefcount(comp), 2)
1167
1168 def test_aio_write(self):
1169 retval = [None]
1170 def cb(comp):
1171 retval[0] = comp.get_return_value()
1172
1173 data = rand_data(256)
1174 comp = self.image.aio_write(data, 256, cb)
1175 comp.wait_for_complete_and_cb()
1176 eq(retval[0], 0)
1177 eq(comp.get_return_value(), 0)
1178 eq(sys.getrefcount(comp), 2)
1179 eq(self.image.read(256, 256), data)
1180
1181 def test_aio_discard(self):
1182 retval = [None]
1183 def cb(comp):
1184 retval[0] = comp.get_return_value()
1185
1186 data = rand_data(256)
1187 self.image.write(data, 0)
1188 comp = self.image.aio_discard(0, 256, cb)
1189 comp.wait_for_complete_and_cb()
1190 eq(retval[0], 0)
1191 eq(comp.get_return_value(), 0)
1192 eq(sys.getrefcount(comp), 2)
1193 eq(self.image.read(256, 256), b'\0' * 256)
1194
1195 def test_aio_flush(self):
1196 retval = [None]
1197 def cb(comp):
1198 retval[0] = comp.get_return_value()
1199
1200 comp = self.image.aio_flush(cb)
1201 comp.wait_for_complete_and_cb()
1202 eq(retval[0], 0)
1203 eq(sys.getrefcount(comp), 2)
1204
1205 def test_metadata(self):
1206 metadata = list(self.image.metadata_list())
1207 eq(len(metadata), 0)
1208 assert_raises(KeyError, self.image.metadata_get, "key1")
1209 self.image.metadata_set("key1", "value1")
1210 self.image.metadata_set("key2", "value2")
1211 value = self.image.metadata_get("key1")
1212 eq(value, "value1")
1213 value = self.image.metadata_get("key2")
1214 eq(value, "value2")
1215 metadata = list(self.image.metadata_list())
1216 eq(len(metadata), 2)
1217 self.image.metadata_remove("key1")
1218 metadata = list(self.image.metadata_list())
1219 eq(len(metadata), 1)
1220 eq(metadata[0], ("key2", "value2"))
1221 self.image.metadata_remove("key2")
1222 assert_raises(KeyError, self.image.metadata_remove, "key2")
1223 metadata = list(self.image.metadata_list())
1224 eq(len(metadata), 0)
1225
1226 N = 65
1227 for i in range(N):
1228 self.image.metadata_set("key" + str(i), "X" * 1025)
1229 metadata = list(self.image.metadata_list())
1230 eq(len(metadata), N)
1231 for i in range(N):
1232 self.image.metadata_remove("key" + str(i))
1233 metadata = list(self.image.metadata_list())
1234 eq(len(metadata), N - i - 1)
1235
1236 def test_watchers_list(self):
1237 watchers = list(self.image.watchers_list())
1238 # The image is open (in r/w mode) from setup, so expect there to be one
1239 # watcher.
1240 eq(len(watchers), 1)
1241
1242 def test_config_list(self):
1243 with Image(ioctx, image_name) as image:
1244 for option in image.config_list():
1245 eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
1246
1247 image.metadata_set("conf_rbd_cache", "true")
1248
1249 for option in image.config_list():
1250 if option['name'] == "rbd_cache":
1251 eq(option['source'], RBD_CONFIG_SOURCE_IMAGE)
1252 else:
1253 eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
1254
1255 image.metadata_remove("conf_rbd_cache")
1256
1257 for option in image.config_list():
1258 eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
1259
1260 def test_image_config_set_and_get_and_remove(self):
1261 with Image(ioctx, image_name) as image:
1262 for option in image.config_list():
1263 eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
1264
1265 image.config_set("rbd_request_timed_out_seconds", "100")
1266 modify_value = image.config_get("rbd_request_timed_out_seconds")
1267 eq(modify_value, '100')
1268
1269 image.config_remove("rbd_request_timed_out_seconds")
1270
1271 for option in image.config_list():
1272 eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
1273
1274 def test_sparsify(self):
1275 assert_raises(InvalidArgument, self.image.sparsify, 16)
1276 self.image.sparsify(4096)
1277
1278 class TestImageId(object):
1279
1280 def setUp(self):
1281 self.rbd = RBD()
1282 create_image()
1283 self.image = Image(ioctx, image_name)
1284 self.image2 = Image(ioctx, None, None, False, self.image.id())
1285
1286 def tearDown(self):
1287 self.image.close()
1288 self.image2.close()
1289 remove_image()
1290 self.image = None
1291 self.image2 = None
1292
1293 def test_read(self):
1294 data = self.image2.read(0, 20)
1295 eq(data, b'\0' * 20)
1296
1297 def test_write(self):
1298 data = rand_data(256)
1299 self.image2.write(data, 0)
1300
1301 def test_resize(self):
1302 new_size = IMG_SIZE * 2
1303 self.image2.resize(new_size)
1304 info = self.image2.stat()
1305 check_stat(info, new_size, IMG_ORDER)
1306
1307 def check_diff(image, offset, length, from_snapshot, expected):
1308 extents = []
1309 def cb(offset, length, exists):
1310 extents.append((offset, length, exists))
1311 image.diff_iterate(0, IMG_SIZE, None, cb)
1312 eq(extents, expected)
1313
1314 class TestClone(object):
1315
1316 @require_features([RBD_FEATURE_LAYERING])
1317 def setUp(self):
1318 global ioctx
1319 global features
1320 self.rbd = RBD()
1321 create_image()
1322 self.image = Image(ioctx, image_name)
1323 data = rand_data(256)
1324 self.image.write(data, IMG_SIZE // 2)
1325 self.image.create_snap('snap1')
1326 global features
1327 self.image.protect_snap('snap1')
1328 self.clone_name = get_temp_image_name()
1329 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name,
1330 features)
1331 self.clone = Image(ioctx, self.clone_name)
1332
1333 def tearDown(self):
1334 global ioctx
1335 self.clone.close()
1336 self.rbd.remove(ioctx, self.clone_name)
1337 self.image.unprotect_snap('snap1')
1338 self.image.remove_snap('snap1')
1339 self.image.close()
1340 remove_image()
1341
1342 def _test_with_params(self, features=None, order=None, stripe_unit=None,
1343 stripe_count=None):
1344 self.image.create_snap('snap2')
1345 self.image.protect_snap('snap2')
1346 clone_name2 = get_temp_image_name()
1347 if features is None:
1348 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2)
1349 elif order is None:
1350 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
1351 features)
1352 elif stripe_unit is None:
1353 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
1354 features, order)
1355 elif stripe_count is None:
1356 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
1357 features, order, stripe_unit)
1358 else:
1359 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
1360 features, order, stripe_unit, stripe_count)
1361 self.rbd.remove(ioctx, clone_name2)
1362 self.image.unprotect_snap('snap2')
1363 self.image.remove_snap('snap2')
1364
1365 def test_with_params(self):
1366 self._test_with_params()
1367
1368 def test_with_params2(self):
1369 global features
1370 self._test_with_params(features, self.image.stat()['order'])
1371
1372 @require_features([RBD_FEATURE_STRIPINGV2])
1373 def test_with_params3(self):
1374 global features
1375 self._test_with_params(features, self.image.stat()['order'],
1376 self.image.stripe_unit(),
1377 self.image.stripe_count())
1378
1379 def test_unprotected(self):
1380 self.image.create_snap('snap2')
1381 global features
1382 clone_name2 = get_temp_image_name()
1383 rados.conf_set("rbd_default_clone_format", "1")
1384 assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name,
1385 'snap2', ioctx, clone_name2, features)
1386 rados.conf_set("rbd_default_clone_format", "auto")
1387 self.image.remove_snap('snap2')
1388
1389 def test_unprotect_with_children(self):
1390 global features
1391 # can't remove a snapshot that has dependent clones
1392 assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
1393
1394 # validate parent info of clone created by TestClone.setUp
1395 (pool, image, snap) = self.clone.parent_info()
1396 eq(pool, pool_name)
1397 eq(image, image_name)
1398 eq(snap, 'snap1')
1399 eq(self.image.id(), self.clone.parent_id())
1400
1401 # create a new pool...
1402 pool_name2 = get_temp_pool_name()
1403 rados.create_pool(pool_name2)
1404 other_ioctx = rados.open_ioctx(pool_name2)
1405 other_ioctx.application_enable('rbd')
1406
1407 # ...with a clone of the same parent
1408 other_clone_name = get_temp_image_name()
1409 rados.conf_set("rbd_default_clone_format", "1")
1410 self.rbd.clone(ioctx, image_name, 'snap1', other_ioctx,
1411 other_clone_name, features)
1412 rados.conf_set("rbd_default_clone_format", "auto")
1413 self.other_clone = Image(other_ioctx, other_clone_name)
1414 # validate its parent info
1415 (pool, image, snap) = self.other_clone.parent_info()
1416 eq(pool, pool_name)
1417 eq(image, image_name)
1418 eq(snap, 'snap1')
1419 eq(self.image.id(), self.other_clone.parent_id())
1420
1421 # can't unprotect snap with children
1422 assert_raises(ImageBusy, self.image.unprotect_snap, 'snap1')
1423
1424 # 2 children, check that cannot remove the parent snap
1425 assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
1426
1427 # close and remove other pool's clone
1428 self.other_clone.close()
1429 self.rbd.remove(other_ioctx, other_clone_name)
1430
1431 # check that we cannot yet remove the parent snap
1432 assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
1433
1434 other_ioctx.close()
1435 rados.delete_pool(pool_name2)
1436
1437 # unprotect, remove parent snap happen in cleanup, and should succeed
1438
1439 def test_stat(self):
1440 image_info = self.image.stat()
1441 clone_info = self.clone.stat()
1442 eq(clone_info['size'], image_info['size'])
1443 eq(clone_info['size'], self.clone.overlap())
1444
1445 def test_resize_stat(self):
1446 self.clone.resize(IMG_SIZE // 2)
1447 image_info = self.image.stat()
1448 clone_info = self.clone.stat()
1449 eq(clone_info['size'], IMG_SIZE // 2)
1450 eq(image_info['size'], IMG_SIZE)
1451 eq(self.clone.overlap(), IMG_SIZE // 2)
1452
1453 self.clone.resize(IMG_SIZE * 2)
1454 image_info = self.image.stat()
1455 clone_info = self.clone.stat()
1456 eq(clone_info['size'], IMG_SIZE * 2)
1457 eq(image_info['size'], IMG_SIZE)
1458 eq(self.clone.overlap(), IMG_SIZE // 2)
1459
1460 def test_resize_io(self):
1461 parent_data = self.image.read(IMG_SIZE // 2, 256)
1462 self.image.resize(0)
1463 self.clone.resize(IMG_SIZE // 2 + 128)
1464 child_data = self.clone.read(IMG_SIZE // 2, 128)
1465 eq(child_data, parent_data[:128])
1466 self.clone.resize(IMG_SIZE)
1467 child_data = self.clone.read(IMG_SIZE // 2, 256)
1468 eq(child_data, parent_data[:128] + (b'\0' * 128))
1469 self.clone.resize(IMG_SIZE // 2 + 1)
1470 child_data = self.clone.read(IMG_SIZE // 2, 1)
1471 eq(child_data, parent_data[0:1])
1472 self.clone.resize(0)
1473 self.clone.resize(IMG_SIZE)
1474 child_data = self.clone.read(IMG_SIZE // 2, 256)
1475 eq(child_data, b'\0' * 256)
1476
1477 def test_read(self):
1478 parent_data = self.image.read(IMG_SIZE // 2, 256)
1479 child_data = self.clone.read(IMG_SIZE // 2, 256)
1480 eq(child_data, parent_data)
1481
1482 def test_write(self):
1483 parent_data = self.image.read(IMG_SIZE // 2, 256)
1484 new_data = rand_data(256)
1485 self.clone.write(new_data, IMG_SIZE // 2 + 256)
1486 child_data = self.clone.read(IMG_SIZE // 2 + 256, 256)
1487 eq(child_data, new_data)
1488 child_data = self.clone.read(IMG_SIZE // 2, 256)
1489 eq(child_data, parent_data)
1490 parent_data = self.image.read(IMG_SIZE // 2 + 256, 256)
1491 eq(parent_data, b'\0' * 256)
1492
1493 def check_children(self, expected):
1494 actual = self.image.list_children()
1495 # dedup for cache pools until
1496 # http://tracker.ceph.com/issues/8187 is fixed
1497 deduped = set([(pool_name, image[1]) for image in actual])
1498 eq(deduped, set(expected))
1499
1500 def check_children2(self, expected):
1501 actual = [{k:v for k,v in x.items() if k in expected[0]} \
1502 for x in self.image.list_children2()]
1503 eq(actual, expected)
1504
1505 def check_descendants(self, expected):
1506 eq(list(self.image.list_descendants()), expected)
1507
1508 def get_image_id(self, ioctx, name):
1509 with Image(ioctx, name) as image:
1510 return image.id()
1511
1512 def test_list_children(self):
1513 global ioctx
1514 global features
1515 self.image.set_snap('snap1')
1516 self.check_children([(pool_name, self.clone_name)])
1517 self.check_children2(
1518 [{'pool': pool_name, 'pool_namespace': '',
1519 'image': self.clone_name, 'trash': False,
1520 'id': self.get_image_id(ioctx, self.clone_name)}])
1521 self.check_descendants(
1522 [{'pool': pool_name, 'pool_namespace': '',
1523 'image': self.clone_name, 'trash': False,
1524 'id': self.get_image_id(ioctx, self.clone_name)}])
1525 self.clone.close()
1526 self.rbd.remove(ioctx, self.clone_name)
1527 eq(self.image.list_children(), [])
1528 eq(list(self.image.list_children2()), [])
1529 eq(list(self.image.list_descendants()), [])
1530
1531 clone_name = get_temp_image_name() + '_'
1532 expected_children = []
1533 expected_children2 = []
1534 for i in range(10):
1535 self.rbd.clone(ioctx, image_name, 'snap1', ioctx,
1536 clone_name + str(i), features)
1537 expected_children.append((pool_name, clone_name + str(i)))
1538 expected_children2.append(
1539 {'pool': pool_name, 'pool_namespace': '',
1540 'image': clone_name + str(i), 'trash': False,
1541 'id': self.get_image_id(ioctx, clone_name + str(i))})
1542 self.check_children(expected_children)
1543 self.check_children2(expected_children2)
1544 self.check_descendants(expected_children2)
1545
1546 image6_id = self.get_image_id(ioctx, clone_name + str(5))
1547 RBD().trash_move(ioctx, clone_name + str(5), 0)
1548 expected_children.remove((pool_name, clone_name + str(5)))
1549 for item in expected_children2:
1550 for k, v in item.items():
1551 if v == image6_id:
1552 item["trash"] = True
1553 self.check_children(expected_children)
1554 self.check_children2(expected_children2)
1555 self.check_descendants(expected_children2)
1556
1557 RBD().trash_restore(ioctx, image6_id, clone_name + str(5))
1558 expected_children.append((pool_name, clone_name + str(5)))
1559 for item in expected_children2:
1560 for k, v in item.items():
1561 if v == image6_id:
1562 item["trash"] = False
1563 self.check_children(expected_children)
1564 self.check_children2(expected_children2)
1565 self.check_descendants(expected_children2)
1566
1567 for i in range(10):
1568 self.rbd.remove(ioctx, clone_name + str(i))
1569 expected_children.remove((pool_name, clone_name + str(i)))
1570 expected_children2.pop(0)
1571 self.check_children(expected_children)
1572 self.check_children2(expected_children2)
1573 self.check_descendants(expected_children2)
1574
1575 eq(self.image.list_children(), [])
1576 eq(list(self.image.list_children2()), [])
1577 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name,
1578 features)
1579 self.check_children([(pool_name, self.clone_name)])
1580 self.check_children2(
1581 [{'pool': pool_name, 'pool_namespace': '',
1582 'image': self.clone_name, 'trash': False,
1583 'id': self.get_image_id(ioctx, self.clone_name)}])
1584 self.check_descendants(
1585 [{'pool': pool_name, 'pool_namespace': '',
1586 'image': self.clone_name, 'trash': False,
1587 'id': self.get_image_id(ioctx, self.clone_name)}])
1588 self.clone = Image(ioctx, self.clone_name)
1589
1590 def test_flatten_errors(self):
1591 # test that we can't flatten a non-clone
1592 assert_raises(InvalidArgument, self.image.flatten)
1593
1594 # test that we can't flatten a snapshot
1595 self.clone.create_snap('snap2')
1596 self.clone.set_snap('snap2')
1597 assert_raises(ReadOnlyImage, self.clone.flatten)
1598 self.clone.remove_snap('snap2')
1599
1600 def check_flatten_with_order(self, new_order):
1601 global ioctx
1602 global features
1603 clone_name2 = get_temp_image_name()
1604 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1605 features, new_order)
1606 #with Image(ioctx, 'clone2') as clone:
1607 clone2 = Image(ioctx, clone_name2)
1608 clone2.flatten()
1609 eq(clone2.overlap(), 0)
1610 clone2.close()
1611 self.rbd.remove(ioctx, clone_name2)
1612
1613 # flatten after resizing to non-block size
1614 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1615 features, new_order)
1616 with Image(ioctx, clone_name2) as clone:
1617 clone.resize(IMG_SIZE // 2 - 1)
1618 clone.flatten()
1619 eq(0, clone.overlap())
1620 self.rbd.remove(ioctx, clone_name2)
1621
1622 # flatten after resizing to non-block size
1623 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1624 features, new_order)
1625 with Image(ioctx, clone_name2) as clone:
1626 clone.resize(IMG_SIZE // 2 + 1)
1627 clone.flatten()
1628 eq(clone.overlap(), 0)
1629 self.rbd.remove(ioctx, clone_name2)
1630
1631 def test_flatten_basic(self):
1632 self.check_flatten_with_order(IMG_ORDER)
1633
1634 def test_flatten_smaller_order(self):
1635 self.check_flatten_with_order(IMG_ORDER - 2)
1636
1637 def test_flatten_larger_order(self):
1638 self.check_flatten_with_order(IMG_ORDER + 2)
1639
1640 def test_flatten_drops_cache(self):
1641 global ioctx
1642 global features
1643 clone_name2 = get_temp_image_name()
1644 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1645 features, IMG_ORDER)
1646 with Image(ioctx, clone_name2) as clone:
1647 with Image(ioctx, clone_name2) as clone2:
1648 # cache object non-existence
1649 data = clone.read(IMG_SIZE // 2, 256)
1650 clone2_data = clone2.read(IMG_SIZE // 2, 256)
1651 eq(data, clone2_data)
1652 clone.flatten()
1653 assert_raises(ImageNotFound, clone.parent_info)
1654 assert_raises(ImageNotFound, clone2.parent_info)
1655 assert_raises(ImageNotFound, clone.parent_id)
1656 assert_raises(ImageNotFound, clone2.parent_id)
1657 after_flatten = clone.read(IMG_SIZE // 2, 256)
1658 eq(data, after_flatten)
1659 after_flatten = clone2.read(IMG_SIZE // 2, 256)
1660 eq(data, after_flatten)
1661 self.rbd.remove(ioctx, clone_name2)
1662
1663 def test_flatten_multi_level(self):
1664 self.clone.create_snap('snap2')
1665 self.clone.protect_snap('snap2')
1666 clone_name3 = get_temp_image_name()
1667 self.rbd.clone(ioctx, self.clone_name, 'snap2', ioctx, clone_name3,
1668 features)
1669 self.clone.flatten()
1670 with Image(ioctx, clone_name3) as clone3:
1671 clone3.flatten()
1672 self.clone.unprotect_snap('snap2')
1673 self.clone.remove_snap('snap2')
1674 self.rbd.remove(ioctx, clone_name3)
1675
1676 def test_flatten_with_progress(self):
1677 d = {'received_callback': False}
1678 def progress_cb(current, total):
1679 d['received_callback'] = True
1680 return 0
1681
1682 global ioctx
1683 global features
1684 clone_name = get_temp_image_name()
1685 self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name,
1686 features, 0)
1687 with Image(ioctx, clone_name) as clone:
1688 clone.flatten(on_progress=progress_cb)
1689 self.rbd.remove(ioctx, clone_name)
1690 eq(True, d['received_callback'])
1691
1692 def test_resize_flatten_multi_level(self):
1693 self.clone.create_snap('snap2')
1694 self.clone.protect_snap('snap2')
1695 clone_name3 = get_temp_image_name()
1696 self.rbd.clone(ioctx, self.clone_name, 'snap2', ioctx, clone_name3,
1697 features)
1698 self.clone.resize(1)
1699 orig_data = self.image.read(0, 256)
1700 with Image(ioctx, clone_name3) as clone3:
1701 clone3_data = clone3.read(0, 256)
1702 eq(orig_data, clone3_data)
1703 self.clone.flatten()
1704 with Image(ioctx, clone_name3) as clone3:
1705 clone3_data = clone3.read(0, 256)
1706 eq(orig_data, clone3_data)
1707 self.rbd.remove(ioctx, clone_name3)
1708 self.clone.unprotect_snap('snap2')
1709 self.clone.remove_snap('snap2')
1710
1711 def test_trash_snapshot(self):
1712 self.image.create_snap('snap2')
1713 global features
1714 clone_name = get_temp_image_name()
1715 rados.conf_set("rbd_default_clone_format", "2")
1716 self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name, features)
1717 rados.conf_set("rbd_default_clone_format", "auto")
1718
1719 self.image.remove_snap('snap2')
1720
1721 snaps = [s for s in self.image.list_snaps() if s['name'] != 'snap1']
1722 eq([RBD_SNAP_NAMESPACE_TYPE_TRASH], [s['namespace'] for s in snaps])
1723 eq([{'original_name' : 'snap2'}], [s['trash'] for s in snaps])
1724
1725 self.rbd.remove(ioctx, clone_name)
1726 eq([], [s for s in self.image.list_snaps() if s['name'] != 'snap1'])
1727
1728 class TestExclusiveLock(object):
1729
1730 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK])
1731 def setUp(self):
1732 global rados2
1733 rados2 = Rados(conffile='')
1734 rados2.connect()
1735 global ioctx2
1736 ioctx2 = rados2.open_ioctx(pool_name)
1737 create_image()
1738
1739 def tearDown(self):
1740 remove_image()
1741 global ioctx2
1742 ioctx2.close()
1743 global rados2
1744 rados2.shutdown()
1745
1746 def test_ownership(self):
1747 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1748 image1.write(b'0'*256, 0)
1749 eq(image1.is_exclusive_lock_owner(), True)
1750 eq(image2.is_exclusive_lock_owner(), False)
1751
1752 def test_snapshot_leadership(self):
1753 with Image(ioctx, image_name) as image:
1754 image.create_snap('snap')
1755 eq(image.is_exclusive_lock_owner(), True)
1756 try:
1757 with Image(ioctx, image_name) as image:
1758 image.write(b'0'*256, 0)
1759 eq(image.is_exclusive_lock_owner(), True)
1760 image.set_snap('snap')
1761 eq(image.is_exclusive_lock_owner(), False)
1762 with Image(ioctx, image_name, snapshot='snap') as image:
1763 eq(image.is_exclusive_lock_owner(), False)
1764 finally:
1765 with Image(ioctx, image_name) as image:
1766 image.remove_snap('snap')
1767
1768 def test_read_only_leadership(self):
1769 with Image(ioctx, image_name, read_only=True) as image:
1770 eq(image.is_exclusive_lock_owner(), False)
1771
1772 def test_follower_flatten(self):
1773 with Image(ioctx, image_name) as image:
1774 image.create_snap('snap')
1775 image.protect_snap('snap')
1776 try:
1777 RBD().clone(ioctx, image_name, 'snap', ioctx, 'clone', features)
1778 with Image(ioctx, 'clone') as image1, Image(ioctx2, 'clone') as image2:
1779 data = rand_data(256)
1780 image1.write(data, 0)
1781 image2.flatten()
1782 assert_raises(ImageNotFound, image1.parent_info)
1783 assert_raises(ImageNotFound, image1.parent_id)
1784 parent = True
1785 for x in range(30):
1786 try:
1787 image2.parent_info()
1788 except ImageNotFound:
1789 parent = False
1790 break
1791 eq(False, parent)
1792 finally:
1793 RBD().remove(ioctx, 'clone')
1794 with Image(ioctx, image_name) as image:
1795 image.unprotect_snap('snap')
1796 image.remove_snap('snap')
1797
1798 def test_follower_resize(self):
1799 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1800 image1.write(b'0'*256, 0)
1801 for new_size in [IMG_SIZE * 2, IMG_SIZE // 2]:
1802 image2.resize(new_size);
1803 eq(new_size, image1.size())
1804 for x in range(30):
1805 if new_size == image2.size():
1806 break
1807 time.sleep(1)
1808 eq(new_size, image2.size())
1809
1810 def test_follower_snap_create(self):
1811 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1812 image2.create_snap('snap1')
1813 image1.remove_snap('snap1')
1814
1815 def test_follower_snap_rollback(self):
1816 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1817 image1.create_snap('snap')
1818 try:
1819 assert_raises(ReadOnlyImage, image2.rollback_to_snap, 'snap')
1820 image1.rollback_to_snap('snap')
1821 finally:
1822 image1.remove_snap('snap')
1823
1824 def test_follower_discard(self):
1825 global rados
1826 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1827 data = rand_data(256)
1828 image1.write(data, 0)
1829 image2.discard(0, 256)
1830 eq(image1.is_exclusive_lock_owner(), False)
1831 eq(image2.is_exclusive_lock_owner(), True)
1832 read = image2.read(0, 256)
1833 if rados.conf_get('rbd_skip_partial_discard') == 'false':
1834 eq(256 * b'\0', read)
1835 else:
1836 eq(data, read)
1837
1838 def test_follower_write(self):
1839 with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1840 data = rand_data(256)
1841 image1.write(data, 0)
1842 image2.write(data, IMG_SIZE // 2)
1843 eq(image1.is_exclusive_lock_owner(), False)
1844 eq(image2.is_exclusive_lock_owner(), True)
1845 for offset in [0, IMG_SIZE // 2]:
1846 read = image2.read(offset, 256)
1847 eq(data, read)
1848 def test_acquire_release_lock(self):
1849 with Image(ioctx, image_name) as image:
1850 image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
1851 image.lock_release()
1852
1853 def test_break_lock(self):
1854 blacklist_rados = Rados(conffile='')
1855 blacklist_rados.connect()
1856 try:
1857 blacklist_ioctx = blacklist_rados.open_ioctx(pool_name)
1858 try:
1859 rados2.conf_set('rbd_blacklist_on_break_lock', 'true')
1860 with Image(ioctx2, image_name) as image, \
1861 Image(blacklist_ioctx, image_name) as blacklist_image:
1862
1863 lock_owners = list(image.lock_get_owners())
1864 eq(0, len(lock_owners))
1865
1866 blacklist_image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
1867 assert_raises(ReadOnlyImage, image.lock_acquire,
1868 RBD_LOCK_MODE_EXCLUSIVE)
1869 lock_owners = list(image.lock_get_owners())
1870 eq(1, len(lock_owners))
1871 eq(RBD_LOCK_MODE_EXCLUSIVE, lock_owners[0]['mode'])
1872 image.lock_break(RBD_LOCK_MODE_EXCLUSIVE,
1873 lock_owners[0]['owner'])
1874
1875 assert_raises(ConnectionShutdown,
1876 blacklist_image.is_exclusive_lock_owner)
1877
1878 blacklist_rados.wait_for_latest_osdmap()
1879 data = rand_data(256)
1880 assert_raises(ConnectionShutdown,
1881 blacklist_image.write, data, 0)
1882
1883 image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
1884
1885 try:
1886 blacklist_image.close()
1887 except ConnectionShutdown:
1888 pass
1889 finally:
1890 blacklist_ioctx.close()
1891 finally:
1892 blacklist_rados.shutdown()
1893
1894 class TestMirroring(object):
1895
1896 @staticmethod
1897 def check_info(info, global_id, state, primary=None):
1898 eq(global_id, info['global_id'])
1899 eq(state, info['state'])
1900 if primary is not None:
1901 eq(primary, info['primary'])
1902
1903 def setUp(self):
1904 self.rbd = RBD()
1905 self.initial_mirror_mode = self.rbd.mirror_mode_get(ioctx)
1906 self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL)
1907 create_image()
1908 self.image = Image(ioctx, image_name)
1909
1910 def tearDown(self):
1911 self.image.close()
1912 remove_image()
1913 self.rbd.mirror_mode_set(ioctx, self.initial_mirror_mode)
1914
1915 def test_uuid(self):
1916 mirror_uuid = self.rbd.mirror_uuid_get(ioctx)
1917 assert(mirror_uuid)
1918
1919 def test_site_name(self):
1920 site_name = "us-west-1"
1921 self.rbd.mirror_site_name_set(rados, site_name)
1922 eq(site_name, self.rbd.mirror_site_name_get(rados))
1923 self.rbd.mirror_site_name_set(rados, "")
1924 eq(rados.get_fsid(), self.rbd.mirror_site_name_get(rados))
1925
1926 def test_mirror_peer_bootstrap(self):
1927 eq([], list(self.rbd.mirror_peer_list(ioctx)))
1928
1929 self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_DISABLED)
1930 assert_raises(InvalidArgument, self.rbd.mirror_peer_bootstrap_create,
1931 ioctx);
1932
1933 self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL)
1934 token_b64 = self.rbd.mirror_peer_bootstrap_create(ioctx)
1935 token = base64.b64decode(token_b64)
1936 token_dict = json.loads(token)
1937 eq(sorted(['fsid', 'client_id', 'key', 'mon_host']),
1938 sorted(list(token_dict.keys())))
1939
1940 # requires different cluster
1941 assert_raises(InvalidArgument, self.rbd.mirror_peer_bootstrap_import,
1942 ioctx, RBD_MIRROR_PEER_DIRECTION_RX, token_b64)
1943
1944 def test_mirror_peer(self):
1945 eq([], list(self.rbd.mirror_peer_list(ioctx)))
1946 site_name = "test_site"
1947 client_name = "test_client"
1948 uuid = self.rbd.mirror_peer_add(ioctx, site_name, client_name,
1949 direction=RBD_MIRROR_PEER_DIRECTION_RX_TX)
1950 assert(uuid)
1951 peer = {
1952 'uuid' : uuid,
1953 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX,
1954 'site_name' : site_name,
1955 'cluster_name' : site_name,
1956 'mirror_uuid': '',
1957 'client_name' : client_name,
1958 }
1959 eq([peer], list(self.rbd.mirror_peer_list(ioctx)))
1960 cluster_name = "test_cluster1"
1961 self.rbd.mirror_peer_set_cluster(ioctx, uuid, cluster_name)
1962 client_name = "test_client1"
1963 self.rbd.mirror_peer_set_client(ioctx, uuid, client_name)
1964 peer = {
1965 'uuid' : uuid,
1966 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX,
1967 'site_name' : cluster_name,
1968 'cluster_name' : cluster_name,
1969 'mirror_uuid': '',
1970 'client_name' : client_name,
1971 }
1972 eq([peer], list(self.rbd.mirror_peer_list(ioctx)))
1973
1974 attribs = {
1975 RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST: 'host1',
1976 RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY: 'abc'
1977 }
1978 self.rbd.mirror_peer_set_attributes(ioctx, uuid, attribs)
1979 eq(attribs, self.rbd.mirror_peer_get_attributes(ioctx, uuid))
1980
1981 self.rbd.mirror_peer_remove(ioctx, uuid)
1982 eq([], list(self.rbd.mirror_peer_list(ioctx)))
1983
1984 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK,
1985 RBD_FEATURE_JOURNALING])
1986 def test_mirror_image(self):
1987
1988 self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE)
1989 self.image.mirror_image_disable(True)
1990 info = self.image.mirror_image_get_info()
1991 self.check_info(info, '', RBD_MIRROR_IMAGE_DISABLED, False)
1992
1993 self.image.mirror_image_enable()
1994 info = self.image.mirror_image_get_info()
1995 global_id = info['global_id']
1996 self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
1997
1998 self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL)
1999 fail = False
2000 try:
2001 self.image.mirror_image_disable(True)
2002 except InvalidArgument:
2003 fail = True
2004 eq(True, fail) # Fails because of mirror mode pool
2005
2006 self.image.mirror_image_demote()
2007 info = self.image.mirror_image_get_info()
2008 self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, False)
2009
2010 entries = dict(self.rbd.mirror_image_info_list(ioctx))
2011 info['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL;
2012 eq(info, entries[self.image.id()])
2013
2014 self.image.mirror_image_resync()
2015
2016 self.image.mirror_image_promote(True)
2017 info = self.image.mirror_image_get_info()
2018 self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
2019
2020 entries = dict(self.rbd.mirror_image_info_list(ioctx))
2021 info['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL;
2022 eq(info, entries[self.image.id()])
2023
2024 fail = False
2025 try:
2026 self.image.mirror_image_resync()
2027 except InvalidArgument:
2028 fail = True
2029 eq(True, fail) # Fails because it is primary
2030
2031 status = self.image.mirror_image_get_status()
2032 eq(image_name, status['name'])
2033 eq(False, status['up'])
2034 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
2035 info = status['info']
2036 self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
2037
2038 @require_features([RBD_FEATURE_EXCLUSIVE_LOCK,
2039 RBD_FEATURE_JOURNALING])
2040 def test_mirror_image_status(self):
2041 info = self.image.mirror_image_get_info()
2042 global_id = info['global_id']
2043 state = info['state']
2044 primary = info['primary']
2045
2046 status = self.image.mirror_image_get_status()
2047 eq(image_name, status['name'])
2048 eq(False, status['up'])
2049 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
2050 eq([], status['remote_statuses'])
2051 info = status['info']
2052 self.check_info(info, global_id, state, primary)
2053
2054 images = list(self.rbd.mirror_image_status_list(ioctx))
2055 eq(1, len(images))
2056 status = images[0]
2057 eq(image_name, status['name'])
2058 eq(False, status['up'])
2059 eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
2060 info = status['info']
2061 self.check_info(info, global_id, state)
2062
2063 states = self.rbd.mirror_image_status_summary(ioctx)
2064 eq([(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, 1)], states)
2065
2066 assert_raises(ImageNotFound, self.image.mirror_image_get_instance_id)
2067 instance_ids = list(self.rbd.mirror_image_instance_id_list(ioctx))
2068 eq(0, len(instance_ids))
2069
2070 N = 65
2071 for i in range(N):
2072 self.rbd.create(ioctx, image_name + str(i), IMG_SIZE, IMG_ORDER,
2073 old_format=False, features=int(features))
2074 images = list(self.rbd.mirror_image_status_list(ioctx))
2075 eq(N + 1, len(images))
2076 for i in range(N):
2077 self.rbd.remove(ioctx, image_name + str(i))
2078
2079 def test_mirror_image_create_snapshot(self):
2080 assert_raises(InvalidArgument, self.image.mirror_image_create_snapshot)
2081
2082 peer1_uuid = self.rbd.mirror_peer_add(ioctx, "cluster1", "client")
2083 peer2_uuid = self.rbd.mirror_peer_add(ioctx, "cluster2", "client")
2084 self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE)
2085 self.image.mirror_image_disable(False)
2086 self.image.mirror_image_enable(RBD_MIRROR_IMAGE_MODE_SNAPSHOT)
2087 mode = self.image.mirror_image_get_mode()
2088 eq(RBD_MIRROR_IMAGE_MODE_SNAPSHOT, mode)
2089
2090 snaps = list(self.image.list_snaps())
2091 eq(1, len(snaps))
2092 snap = snaps[0]
2093 eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
2094 eq(RBD_SNAP_MIRROR_STATE_PRIMARY, snap['mirror']['state'])
2095
2096 info = self.image.mirror_image_get_info()
2097 eq(True, info['primary'])
2098 entries = dict(
2099 self.rbd.mirror_image_info_list(ioctx,
2100 RBD_MIRROR_IMAGE_MODE_SNAPSHOT))
2101 info['mode'] = RBD_MIRROR_IMAGE_MODE_SNAPSHOT;
2102 eq(info, entries[self.image.id()])
2103
2104 snap_id = self.image.mirror_image_create_snapshot()
2105
2106 snaps = list(self.image.list_snaps())
2107 eq(2, len(snaps))
2108 snap = snaps[0]
2109 eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
2110 eq(RBD_SNAP_MIRROR_STATE_PRIMARY, snap['mirror']['state'])
2111 snap = snaps[1]
2112 eq(snap['id'], snap_id)
2113 eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
2114 eq(RBD_SNAP_MIRROR_STATE_PRIMARY, snap['mirror']['state'])
2115 eq(sorted([peer1_uuid, peer2_uuid]),
2116 sorted(snap['mirror']['mirror_peer_uuids']))
2117
2118 eq(RBD_SNAP_NAMESPACE_TYPE_MIRROR,
2119 self.image.snap_get_namespace_type(snap_id))
2120 mirror_snap = self.image.snap_get_mirror_namespace(snap_id)
2121 eq(mirror_snap, snap['mirror'])
2122
2123 self.image.mirror_image_demote()
2124
2125 assert_raises(InvalidArgument, self.image.mirror_image_create_snapshot)
2126
2127 snaps = list(self.image.list_snaps())
2128 eq(3, len(snaps))
2129 snap = snaps[0]
2130 eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
2131 snap = snaps[1]
2132 eq(snap['id'], snap_id)
2133 eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
2134 snap = snaps[2]
2135 eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
2136 eq(RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED, snap['mirror']['state'])
2137 eq(sorted([peer1_uuid, peer2_uuid]),
2138 sorted(snap['mirror']['mirror_peer_uuids']))
2139
2140 self.rbd.mirror_peer_remove(ioctx, peer1_uuid)
2141 self.rbd.mirror_peer_remove(ioctx, peer2_uuid)
2142 self.image.mirror_image_promote(False)
2143
2144 class TestTrash(object):
2145
2146 def setUp(self):
2147 global rados2
2148 rados2 = Rados(conffile='')
2149 rados2.connect()
2150 global ioctx2
2151 ioctx2 = rados2.open_ioctx(pool_name)
2152
2153 def tearDown(self):
2154 global ioctx2
2155 ioctx2.close()
2156 global rados2
2157 rados2.shutdown()
2158
2159 def test_move(self):
2160 create_image()
2161 with Image(ioctx, image_name) as image:
2162 image_id = image.id()
2163
2164 RBD().trash_move(ioctx, image_name, 1000)
2165 RBD().trash_remove(ioctx, image_id, True)
2166
2167 def test_purge(self):
2168 create_image()
2169 with Image(ioctx, image_name) as image:
2170 image_name1 = image_name
2171 image_id1 = image.id()
2172
2173 create_image()
2174 with Image(ioctx, image_name) as image:
2175 image_name2 = image_name
2176 image_id2 = image.id()
2177
2178 RBD().trash_move(ioctx, image_name1, 0)
2179 RBD().trash_move(ioctx, image_name2, 1000)
2180 RBD().trash_purge(ioctx, datetime.now())
2181
2182 entries = list(RBD().trash_list(ioctx))
2183 eq([image_id2], [x['id'] for x in entries])
2184 RBD().trash_remove(ioctx, image_id2, True)
2185
2186 def test_remove_denied(self):
2187 create_image()
2188 with Image(ioctx, image_name) as image:
2189 image_id = image.id()
2190
2191 RBD().trash_move(ioctx, image_name, 1000)
2192 assert_raises(PermissionError, RBD().trash_remove, ioctx, image_id)
2193 RBD().trash_remove(ioctx, image_id, True)
2194
2195 def test_remove(self):
2196 create_image()
2197 with Image(ioctx, image_name) as image:
2198 image_id = image.id()
2199
2200 RBD().trash_move(ioctx, image_name, 0)
2201 RBD().trash_remove(ioctx, image_id)
2202
2203 def test_remove_with_progress(self):
2204 d = {'received_callback': False}
2205 def progress_cb(current, total):
2206 d['received_callback'] = True
2207 return 0
2208
2209 create_image()
2210 with Image(ioctx, image_name) as image:
2211 image_id = image.id()
2212
2213 RBD().trash_move(ioctx, image_name, 0)
2214 RBD().trash_remove(ioctx, image_id, on_progress=progress_cb)
2215 eq(True, d['received_callback'])
2216
2217 def test_get(self):
2218 create_image()
2219 with Image(ioctx, image_name) as image:
2220 image_id = image.id()
2221
2222 RBD().trash_move(ioctx, image_name, 1000)
2223
2224 info = RBD().trash_get(ioctx, image_id)
2225 eq(image_id, info['id'])
2226 eq(image_name, info['name'])
2227 eq('USER', info['source'])
2228 assert(info['deferment_end_time'] > info['deletion_time'])
2229
2230 RBD().trash_remove(ioctx, image_id, True)
2231
2232 def test_list(self):
2233 create_image()
2234 with Image(ioctx, image_name) as image:
2235 image_id1 = image.id()
2236 image_name1 = image_name
2237 RBD().trash_move(ioctx, image_name, 1000)
2238
2239 create_image()
2240 with Image(ioctx, image_name) as image:
2241 image_id2 = image.id()
2242 image_name2 = image_name
2243 RBD().trash_move(ioctx, image_name, 1000)
2244
2245 entries = list(RBD().trash_list(ioctx))
2246 for e in entries:
2247 if e['id'] == image_id1:
2248 eq(e['name'], image_name1)
2249 elif e['id'] == image_id2:
2250 eq(e['name'], image_name2)
2251 else:
2252 assert False
2253 eq(e['source'], 'USER')
2254 assert e['deferment_end_time'] > e['deletion_time']
2255
2256 RBD().trash_remove(ioctx, image_id1, True)
2257 RBD().trash_remove(ioctx, image_id2, True)
2258
2259 def test_restore(self):
2260 create_image()
2261 with Image(ioctx, image_name) as image:
2262 image_id = image.id()
2263 RBD().trash_move(ioctx, image_name, 1000)
2264 RBD().trash_restore(ioctx, image_id, image_name)
2265 remove_image()
2266
2267 def test_create_group():
2268 create_group()
2269 remove_group()
2270
2271 def test_rename_group():
2272 create_group()
2273 if group_name is not None:
2274 rename_group()
2275 eq(["new" + group_name], RBD().group_list(ioctx))
2276 RBD().group_remove(ioctx, "new" + group_name)
2277 else:
2278 remove_group()
2279
2280 def test_list_groups_empty():
2281 eq([], RBD().group_list(ioctx))
2282
2283 @with_setup(create_group, remove_group)
2284 def test_list_groups():
2285 eq([group_name], RBD().group_list(ioctx))
2286
2287 @with_setup(create_group)
2288 def test_list_groups_after_removed():
2289 remove_group()
2290 eq([], RBD().group_list(ioctx))
2291
2292 class TestGroups(object):
2293
2294 def setUp(self):
2295 global snap_name
2296 self.rbd = RBD()
2297 create_image()
2298 self.image_names = [image_name]
2299 self.image = Image(ioctx, image_name)
2300
2301 create_group()
2302 snap_name = get_temp_snap_name()
2303 self.group = Group(ioctx, group_name)
2304
2305 def tearDown(self):
2306 remove_group()
2307 self.image = None
2308 for name in self.image_names:
2309 RBD().remove(ioctx, name)
2310
2311 def test_group_image_add(self):
2312 self.group.add_image(ioctx, image_name)
2313
2314 def test_group_image_list_empty(self):
2315 eq([], list(self.group.list_images()))
2316
2317 def test_group_image_list(self):
2318 eq([], list(self.group.list_images()))
2319 self.group.add_image(ioctx, image_name)
2320 eq([image_name], [img['name'] for img in self.group.list_images()])
2321
2322 def test_group_image_list_move_to_trash(self):
2323 eq([], list(self.group.list_images()))
2324 with Image(ioctx, image_name) as image:
2325 image_id = image.id()
2326 self.group.add_image(ioctx, image_name)
2327 eq([image_name], [img['name'] for img in self.group.list_images()])
2328 RBD().trash_move(ioctx, image_name, 0)
2329 eq([], list(self.group.list_images()))
2330 RBD().trash_restore(ioctx, image_id, image_name)
2331
2332 def test_group_image_many_images(self):
2333 eq([], list(self.group.list_images()))
2334 self.group.add_image(ioctx, image_name)
2335
2336 for x in range(0, 20):
2337 create_image()
2338 self.image_names.append(image_name)
2339 self.group.add_image(ioctx, image_name)
2340
2341 self.image_names.sort()
2342 answer = [img['name'] for img in self.group.list_images()]
2343 answer.sort()
2344 eq(self.image_names, answer)
2345
2346 def test_group_image_remove(self):
2347 eq([], list(self.group.list_images()))
2348 self.group.add_image(ioctx, image_name)
2349 with Image(ioctx, image_name) as image:
2350 eq(RBD_OPERATION_FEATURE_GROUP,
2351 image.op_features() & RBD_OPERATION_FEATURE_GROUP)
2352 group = image.group()
2353 eq(group_name, group['name'])
2354
2355 eq([image_name], [img['name'] for img in self.group.list_images()])
2356 self.group.remove_image(ioctx, image_name)
2357 eq([], list(self.group.list_images()))
2358 with Image(ioctx, image_name) as image:
2359 eq(0, image.op_features() & RBD_OPERATION_FEATURE_GROUP)
2360
2361 def test_group_snap(self):
2362 global snap_name
2363 eq([], list(self.group.list_snaps()))
2364 self.group.create_snap(snap_name)
2365 eq([snap_name], [snap['name'] for snap in self.group.list_snaps()])
2366
2367 for snap in self.image.list_snaps():
2368 eq(rbd.RBD_SNAP_NAMESPACE_TYPE_GROUP, snap['namespace'])
2369 info = snap['group']
2370 eq(group_name, info['group_name'])
2371 eq(snap_name, info['group_snap_name'])
2372
2373 self.group.remove_snap(snap_name)
2374 eq([], list(self.group.list_snaps()))
2375
2376 def test_group_snap_list_many(self):
2377 global snap_name
2378 eq([], list(self.group.list_snaps()))
2379 snap_names = []
2380 for x in range(0, 20):
2381 snap_names.append(snap_name)
2382 self.group.create_snap(snap_name)
2383 snap_name = get_temp_snap_name()
2384
2385 snap_names.sort()
2386 answer = [snap['name'] for snap in self.group.list_snaps()]
2387 answer.sort()
2388 eq(snap_names, answer)
2389
2390 def test_group_snap_namespace(self):
2391 global snap_name
2392 eq([], list(self.group.list_snaps()))
2393 self.group.add_image(ioctx, image_name)
2394 self.group.create_snap(snap_name)
2395 eq(1, len([snap['name'] for snap in self.image.list_snaps()]))
2396 self.group.remove_image(ioctx, image_name)
2397 self.group.remove_snap(snap_name)
2398 eq([], list(self.group.list_snaps()))
2399
2400 def test_group_snap_rename(self):
2401 global snap_name
2402 new_snap_name = "new" + snap_name
2403
2404 eq([], list(self.group.list_snaps()))
2405 self.group.create_snap(snap_name)
2406 eq([snap_name], [snap['name'] for snap in self.group.list_snaps()])
2407 self.group.rename_snap(snap_name, new_snap_name)
2408 eq([new_snap_name], [snap['name'] for snap in self.group.list_snaps()])
2409 self.group.remove_snap(new_snap_name)
2410 eq([], list(self.group.list_snaps()))
2411
2412 def test_group_snap_rollback(self):
2413 eq([], list(self.group.list_images()))
2414 self.group.add_image(ioctx, image_name)
2415 with Image(ioctx, image_name) as image:
2416 image.write(b'\0' * 256, 0)
2417 read = image.read(0, 256)
2418 eq(read, b'\0' * 256)
2419
2420 global snap_name
2421 eq([], list(self.group.list_snaps()))
2422 self.group.create_snap(snap_name)
2423 eq([snap_name], [snap['name'] for snap in self.group.list_snaps()])
2424
2425 with Image(ioctx, image_name) as image:
2426 data = rand_data(256)
2427 image.write(data, 0)
2428 read = image.read(0, 256)
2429 eq(read, data)
2430
2431 self.group.rollback_to_snap(snap_name)
2432 with Image(ioctx, image_name) as image:
2433 read = image.read(0, 256)
2434 eq(read, b'\0' * 256)
2435
2436 self.group.remove_image(ioctx, image_name)
2437 eq([], list(self.group.list_images()))
2438 self.group.remove_snap(snap_name)
2439 eq([], list(self.group.list_snaps()))
2440
2441 @with_setup(create_image, remove_image)
2442 def test_rename():
2443 rbd = RBD()
2444 image_name2 = get_temp_image_name()
2445
2446 class TestMigration(object):
2447
2448 def test_migration(self):
2449 create_image()
2450 RBD().migration_prepare(ioctx, image_name, ioctx, image_name, features=63,
2451 order=23, stripe_unit=1<<23, stripe_count=1,
2452 data_pool=None)
2453
2454 status = RBD().migration_status(ioctx, image_name)
2455 eq(image_name, status['source_image_name'])
2456 eq(image_name, status['dest_image_name'])
2457 eq(RBD_IMAGE_MIGRATION_STATE_PREPARED, status['state'])
2458
2459 RBD().migration_execute(ioctx, image_name)
2460 RBD().migration_commit(ioctx, image_name)
2461 remove_image()
2462
2463 def test_migration_with_progress(self):
2464 d = {'received_callback': False}
2465 def progress_cb(current, total):
2466 d['received_callback'] = True
2467 return 0
2468
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 RBD().migration_execute(ioctx, image_name, on_progress=progress_cb)
2474 eq(True, d['received_callback'])
2475 d['received_callback'] = False
2476
2477 RBD().migration_commit(ioctx, image_name, on_progress=progress_cb)
2478 eq(True, d['received_callback'])
2479 remove_image()
2480
2481 def test_migrate_abort(self):
2482 create_image()
2483 RBD().migration_prepare(ioctx, image_name, ioctx, image_name, features=63,
2484 order=23, stripe_unit=1<<23, stripe_count=1,
2485 data_pool=None)
2486 RBD().migration_abort(ioctx, image_name)
2487 remove_image()
2488
2489 def test_migrate_abort_with_progress(self):
2490 d = {'received_callback': False}
2491 def progress_cb(current, total):
2492 d['received_callback'] = True
2493 return 0
2494
2495 create_image()
2496 RBD().migration_prepare(ioctx, image_name, ioctx, image_name, features=63,
2497 order=23, stripe_unit=1<<23, stripe_count=1,
2498 data_pool=None)
2499 RBD().migration_abort(ioctx, image_name, on_progress=progress_cb)
2500 eq(True, d['received_callback'])
2501 remove_image()