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