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