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