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