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