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