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