Merge "Add script files of test and spdk-ansible for SPDK"
[stor4nfv.git] / src / ceph / src / test / pybind / test_rbd.py
1 # vim: expandtab smarttab shiftwidth=4 softtabstop=4
2 import functools
3 import socket
4 import os
5 import time
6 import sys
7
8 from datetime import datetime
9 from nose import with_setup, SkipTest
10 from nose.tools import eq_ as eq, assert_raises, assert_not_equal
11 from rados import (Rados,
12                    LIBRADOS_OP_FLAG_FADVISE_DONTNEED,
13                    LIBRADOS_OP_FLAG_FADVISE_NOCACHE,
14                    LIBRADOS_OP_FLAG_FADVISE_RANDOM)
15 from rbd import (RBD, Image, ImageNotFound, InvalidArgument, ImageExists,
16                  ImageBusy, ImageHasSnapshots, ReadOnlyImage,
17                  FunctionNotSupported, ArgumentOutOfRange,
18                  DiskQuotaExceeded, ConnectionShutdown, PermissionError,
19                  RBD_FEATURE_LAYERING, RBD_FEATURE_STRIPINGV2,
20                  RBD_FEATURE_EXCLUSIVE_LOCK, RBD_FEATURE_JOURNALING,
21                  RBD_MIRROR_MODE_DISABLED, RBD_MIRROR_MODE_IMAGE,
22                  RBD_MIRROR_MODE_POOL, RBD_MIRROR_IMAGE_ENABLED,
23                  RBD_MIRROR_IMAGE_DISABLED, MIRROR_IMAGE_STATUS_STATE_UNKNOWN,
24                  RBD_LOCK_MODE_EXCLUSIVE)
25
26 rados = None
27 ioctx = None
28 features = None
29 image_idx = 0
30 image_name = None
31 pool_idx = 0
32 pool_name = None
33 IMG_SIZE = 8 << 20 # 8 MiB
34 IMG_ORDER = 22 # 4 MiB objects
35
36 def setup_module():
37     global rados
38     rados = Rados(conffile='')
39     rados.connect()
40     global pool_name
41     pool_name = get_temp_pool_name()
42     rados.create_pool(pool_name)
43     global ioctx
44     ioctx = rados.open_ioctx(pool_name)
45     ioctx.application_enable('rbd')
46     global features
47     features = os.getenv("RBD_FEATURES")
48     features = int(features) if features is not None else 61
49
50 def teardown_module():
51     global ioctx
52     ioctx.close()
53     global rados
54     rados.delete_pool(pool_name)
55     rados.shutdown()
56
57 def get_temp_pool_name():
58     global pool_idx
59     pool_idx += 1
60     return "test-rbd-api-" + socket.gethostname() + '-' + str(os.getpid()) + \
61            '-' + str(pool_idx)
62
63 def get_temp_image_name():
64     global image_idx
65     image_idx += 1
66     return "image" + str(image_idx)
67
68 def create_image():
69     global image_name
70     image_name = get_temp_image_name()
71     if features is not None:
72         RBD().create(ioctx, image_name, IMG_SIZE, IMG_ORDER, old_format=False,
73                      features=int(features))
74     else:
75         RBD().create(ioctx, image_name, IMG_SIZE, IMG_ORDER, old_format=True)
76
77 def remove_image():
78     if image_name is not None:
79         RBD().remove(ioctx, image_name)
80
81 def require_new_format():
82     def wrapper(fn):
83         def _require_new_format(*args, **kwargs):
84             global features
85             if features is None:
86                 raise SkipTest
87             return fn(*args, **kwargs)
88         return functools.wraps(fn)(_require_new_format)
89     return wrapper
90
91 def require_features(required_features):
92     def wrapper(fn):
93         def _require_features(*args, **kwargs):
94             global features
95             if features is None:
96                 raise SkipTest
97             for feature in required_features:
98                 if feature & features != feature:
99                     raise SkipTest
100             return fn(*args, **kwargs)
101         return functools.wraps(fn)(_require_features)
102     return wrapper
103
104 def blacklist_features(blacklisted_features):
105     def wrapper(fn):
106         def _blacklist_features(*args, **kwargs):
107             global features
108             for feature in blacklisted_features:
109                 if features is not None and feature & features == feature:
110                     raise SkipTest
111             return fn(*args, **kwargs)
112         return functools.wraps(fn)(_blacklist_features)
113     return wrapper
114
115 def test_version():
116     RBD().version()
117
118 def test_create():
119     create_image()
120     remove_image()
121
122 def check_default_params(format, order=None, features=None, stripe_count=None,
123                          stripe_unit=None, exception=None):
124     global rados
125     global ioctx
126     orig_vals = {}
127     for k in ['rbd_default_format', 'rbd_default_order', 'rbd_default_features',
128               'rbd_default_stripe_count', 'rbd_default_stripe_unit']:
129         orig_vals[k] = rados.conf_get(k)
130     try:
131         rados.conf_set('rbd_default_format', str(format))
132         if order is not None:
133             rados.conf_set('rbd_default_order', str(order or 0))
134         if features is not None:
135             rados.conf_set('rbd_default_features', str(features or 0))
136         if stripe_count is not None:
137             rados.conf_set('rbd_default_stripe_count', str(stripe_count or 0))
138         if stripe_unit is not None:
139             rados.conf_set('rbd_default_stripe_unit', str(stripe_unit or 0))
140         feature_data_pool = 0
141         datapool = rados.conf_get('rbd_default_data_pool')
142         if not len(datapool) == 0:
143             feature_data_pool = 128
144         image_name = get_temp_image_name()
145         if exception is None:
146             RBD().create(ioctx, image_name, IMG_SIZE)
147             try:
148                 with Image(ioctx, image_name) as image:
149                     eq(format == 1, image.old_format())
150
151                     expected_order = int(rados.conf_get('rbd_default_order'))
152                     actual_order = image.stat()['order']
153                     eq(expected_order, actual_order)
154
155                     expected_features = features
156                     if format == 1:
157                         expected_features = 0
158                     elif expected_features is None:
159                         expected_features = 61 | feature_data_pool
160                     else:
161                         expected_features |= feature_data_pool
162                     eq(expected_features, image.features())
163
164                     expected_stripe_count = stripe_count
165                     if not expected_stripe_count or format == 1 or \
166                            features & RBD_FEATURE_STRIPINGV2 == 0:
167                         expected_stripe_count = 1
168                     eq(expected_stripe_count, image.stripe_count())
169
170                     expected_stripe_unit = stripe_unit
171                     if not expected_stripe_unit or format == 1 or \
172                            features & RBD_FEATURE_STRIPINGV2 == 0:
173                         expected_stripe_unit = 1 << actual_order
174                     eq(expected_stripe_unit, image.stripe_unit())
175             finally:
176                 RBD().remove(ioctx, image_name)
177         else:
178             assert_raises(exception, RBD().create, ioctx, image_name, IMG_SIZE)
179     finally:
180         for k, v in orig_vals.items():
181             rados.conf_set(k, v)
182
183 def test_create_defaults():
184     # basic format 1 and 2
185     check_default_params(1)
186     check_default_params(2)
187     # invalid order
188     check_default_params(1, 0, exception=ArgumentOutOfRange)
189     check_default_params(2, 0, exception=ArgumentOutOfRange)
190     check_default_params(1, 11, exception=ArgumentOutOfRange)
191     check_default_params(2, 11, exception=ArgumentOutOfRange)
192     check_default_params(1, 65, exception=ArgumentOutOfRange)
193     check_default_params(2, 65, exception=ArgumentOutOfRange)
194     # striping and features are ignored for format 1
195     check_default_params(1, 20, 0, 1, 1)
196     check_default_params(1, 20, 3, 1, 1)
197     check_default_params(1, 20, 0, 0, 0)
198     # striping is ignored if stripingv2 is not set
199     check_default_params(2, 20, 0, 1, 1 << 20)
200     check_default_params(2, 20, RBD_FEATURE_LAYERING, 1, 1 << 20)
201     check_default_params(2, 20, 0, 0, 0)
202     # striping with stripingv2 is fine
203     check_default_params(2, 20, RBD_FEATURE_STRIPINGV2, 1, 1 << 16)
204     check_default_params(2, 20, RBD_FEATURE_STRIPINGV2, 10, 1 << 20)
205     check_default_params(2, 20, RBD_FEATURE_STRIPINGV2, 10, 1 << 16)
206     check_default_params(2, 20, 0, 0, 0)
207     # make sure invalid combinations of stripe unit and order are still invalid
208     check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 10, 1 << 50, exception=InvalidArgument)
209     check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 10, 100, exception=InvalidArgument)
210     check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 0, 1, exception=InvalidArgument)
211     check_default_params(2, 22, RBD_FEATURE_STRIPINGV2, 1, 0, exception=InvalidArgument)
212     # 0 stripe unit and count are still ignored
213     check_default_params(2, 22, 0, 0, 0)
214
215 def test_context_manager():
216     with Rados(conffile='') as cluster:
217         with cluster.open_ioctx(pool_name) as ioctx:
218             image_name = get_temp_image_name()
219             RBD().create(ioctx, image_name, IMG_SIZE)
220             with Image(ioctx, image_name) as image:
221                 data = rand_data(256)
222                 image.write(data, 0)
223                 read = image.read(0, 256)
224             RBD().remove(ioctx, image_name)
225             eq(data, read)
226
227 def test_open_read_only():
228     with Rados(conffile='') as cluster:
229         with cluster.open_ioctx(pool_name) as ioctx:
230             image_name = get_temp_image_name()
231             RBD().create(ioctx, image_name, IMG_SIZE)
232             data = rand_data(256)
233             with Image(ioctx, image_name) as image:
234                 image.write(data, 0)
235                 image.create_snap('snap')
236             with Image(ioctx, image_name, read_only=True) as image:
237                 read = image.read(0, 256)
238                 eq(data, read)
239                 assert_raises(ReadOnlyImage, image.write, data, 0)
240                 assert_raises(ReadOnlyImage, image.create_snap, 'test')
241                 assert_raises(ReadOnlyImage, image.remove_snap, 'snap')
242                 assert_raises(ReadOnlyImage, image.rollback_to_snap, 'snap')
243                 assert_raises(ReadOnlyImage, image.protect_snap, 'snap')
244                 assert_raises(ReadOnlyImage, image.unprotect_snap, 'snap')
245                 assert_raises(ReadOnlyImage, image.unprotect_snap, 'snap')
246                 assert_raises(ReadOnlyImage, image.flatten)
247             with Image(ioctx, image_name) as image:
248                 image.remove_snap('snap')
249             RBD().remove(ioctx, image_name)
250             eq(data, read)
251
252 def test_open_dne():
253     for i in range(100):
254         image_name = get_temp_image_name()
255         assert_raises(ImageNotFound, Image, ioctx, image_name + 'dne')
256         assert_raises(ImageNotFound, Image, ioctx, image_name, 'snap')
257
258 def test_open_readonly_dne():
259     for i in range(100):
260         image_name = get_temp_image_name()
261         assert_raises(ImageNotFound, Image, ioctx, image_name + 'dne',
262                       read_only=True)
263         assert_raises(ImageNotFound, Image, ioctx, image_name, 'snap',
264                       read_only=True)
265
266 def test_remove_dne():
267     assert_raises(ImageNotFound, remove_image)
268
269 def test_list_empty():
270     eq([], RBD().list(ioctx))
271
272 @with_setup(create_image, remove_image)
273 def test_list():
274     eq([image_name], RBD().list(ioctx))
275
276 @with_setup(create_image, remove_image)
277 def test_rename():
278     rbd = RBD()
279     image_name2 = get_temp_image_name()
280     rbd.rename(ioctx, image_name, image_name2)
281     eq([image_name2], rbd.list(ioctx))
282     rbd.rename(ioctx, image_name2, image_name)
283     eq([image_name], rbd.list(ioctx))
284
285 def rand_data(size):
286     return os.urandom(size)
287
288 def check_stat(info, size, order):
289     assert 'block_name_prefix' in info
290     eq(info['size'], size)
291     eq(info['order'], order)
292     eq(info['num_objs'], size // (1 << order))
293     eq(info['obj_size'], 1 << order)
294
295 class TestImage(object):
296
297     def setUp(self):
298         self.rbd = RBD()
299         create_image()
300         self.image = Image(ioctx, image_name)
301
302     def tearDown(self):
303         self.image.close()
304         remove_image()
305         self.image = None
306
307     @require_new_format()
308     @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK])
309     def test_update_features(self):
310         features = self.image.features()
311         self.image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, True)
312         eq(features | RBD_FEATURE_EXCLUSIVE_LOCK, self.image.features())
313
314     @require_features([RBD_FEATURE_STRIPINGV2])
315     def test_create_with_params(self):
316         global features
317         image_name = get_temp_image_name()
318         order = 20
319         stripe_unit = 1 << 20
320         stripe_count = 10
321         self.rbd.create(ioctx, image_name, IMG_SIZE, order,
322                         False, features, stripe_unit, stripe_count)
323         image = Image(ioctx, image_name)
324         info = image.stat()
325         check_stat(info, IMG_SIZE, order)
326         eq(image.features(), features)
327         eq(image.stripe_unit(), stripe_unit)
328         eq(image.stripe_count(), stripe_count)
329         image.close()
330         RBD().remove(ioctx, image_name)
331
332     @require_new_format()
333     def test_id(self):
334         assert_not_equal(b'', self.image.id())
335
336     def test_block_name_prefix(self):
337         assert_not_equal(b'', self.image.block_name_prefix())
338
339     def test_create_timestamp(self):
340         timestamp = self.image.create_timestamp()
341         assert_not_equal(0, timestamp.year)
342         assert_not_equal(1970, timestamp.year)
343
344     def test_invalidate_cache(self):
345         self.image.write(b'abc', 0)
346         eq(b'abc', self.image.read(0, 3))
347         self.image.invalidate_cache()
348         eq(b'abc', self.image.read(0, 3))
349
350     def test_stat(self):
351         info = self.image.stat()
352         check_stat(info, IMG_SIZE, IMG_ORDER)
353
354     def test_flags(self):
355         flags = self.image.flags()
356         eq(0, flags)
357
358     def test_image_auto_close(self):
359         image = Image(ioctx, image_name)
360
361     def test_write(self):
362         data = rand_data(256)
363         self.image.write(data, 0)
364
365     def test_write_with_fadvise_flags(self):
366         data = rand_data(256)
367         self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED)
368         self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_NOCACHE)
369
370     def test_read(self):
371         data = self.image.read(0, 20)
372         eq(data, b'\0' * 20)
373
374     def test_read_with_fadvise_flags(self):
375         data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED)
376         eq(data, b'\0' * 20)
377         data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM)
378         eq(data, b'\0' * 20)
379
380     def test_large_write(self):
381         data = rand_data(IMG_SIZE)
382         self.image.write(data, 0)
383
384     def test_large_read(self):
385         data = self.image.read(0, IMG_SIZE)
386         eq(data, b'\0' * IMG_SIZE)
387
388     def test_write_read(self):
389         data = rand_data(256)
390         offset = 50
391         self.image.write(data, offset)
392         read = self.image.read(offset, 256)
393         eq(data, read)
394
395     def test_read_bad_offset(self):
396         assert_raises(InvalidArgument, self.image.read, IMG_SIZE + 1, IMG_SIZE)
397
398     def test_resize(self):
399         new_size = IMG_SIZE * 2
400         self.image.resize(new_size)
401         info = self.image.stat()
402         check_stat(info, new_size, IMG_ORDER)
403
404     def test_size(self):
405         eq(IMG_SIZE, self.image.size())
406         self.image.create_snap('snap1')
407         new_size = IMG_SIZE * 2
408         self.image.resize(new_size)
409         eq(new_size, self.image.size())
410         self.image.create_snap('snap2')
411         self.image.set_snap('snap2')
412         eq(new_size, self.image.size())
413         self.image.set_snap('snap1')
414         eq(IMG_SIZE, self.image.size())
415         self.image.set_snap(None)
416         eq(new_size, self.image.size())
417         self.image.remove_snap('snap1')
418         self.image.remove_snap('snap2')
419
420     def test_resize_down(self):
421         new_size = IMG_SIZE // 2
422         data = rand_data(256)
423         self.image.write(data, IMG_SIZE // 2);
424         self.image.resize(new_size)
425         self.image.resize(IMG_SIZE)
426         read = self.image.read(IMG_SIZE // 2, 256)
427         eq(b'\0' * 256, read)
428
429     def test_resize_bytes(self):
430         new_size = IMG_SIZE // 2 - 5
431         data = rand_data(256)
432         self.image.write(data, IMG_SIZE // 2 - 10);
433         self.image.resize(new_size)
434         self.image.resize(IMG_SIZE)
435         read = self.image.read(IMG_SIZE // 2 - 10, 5)
436         eq(data[:5], read)
437         read = self.image.read(IMG_SIZE // 2 - 5, 251)
438         eq(b'\0' * 251, read)
439
440     def _test_copy(self, features=None, order=None, stripe_unit=None,
441                    stripe_count=None):
442         global ioctx
443         data = rand_data(256)
444         self.image.write(data, 256)
445         image_name = get_temp_image_name()
446         if features is None:
447             self.image.copy(ioctx, image_name)
448         elif order is None:
449             self.image.copy(ioctx, image_name, features)
450         elif stripe_unit is None:
451             self.image.copy(ioctx, image_name, features, order)
452         elif stripe_count is None:
453             self.image.copy(ioctx, image_name, features, order, stripe_unit)
454         else:
455             self.image.copy(ioctx, image_name, features, order, stripe_unit,
456                             stripe_count)
457         assert_raises(ImageExists, self.image.copy, ioctx, image_name)
458         copy = Image(ioctx, image_name)
459         copy_data = copy.read(256, 256)
460         copy.close()
461         self.rbd.remove(ioctx, image_name)
462         eq(data, copy_data)
463
464     def test_copy(self):
465         self._test_copy()
466
467     def test_copy2(self):
468         self._test_copy(self.image.features(), self.image.stat()['order'])
469
470     @require_features([RBD_FEATURE_STRIPINGV2])
471     def test_copy3(self):
472         global features
473         self._test_copy(features, self.image.stat()['order'],
474                         self.image.stripe_unit(), self.image.stripe_count())
475
476     def test_create_snap(self):
477         global ioctx
478         self.image.create_snap('snap1')
479         read = self.image.read(0, 256)
480         eq(read, b'\0' * 256)
481         data = rand_data(256)
482         self.image.write(data, 0)
483         read = self.image.read(0, 256)
484         eq(read, data)
485         at_snapshot = Image(ioctx, image_name, 'snap1')
486         snap_data = at_snapshot.read(0, 256)
487         at_snapshot.close()
488         eq(snap_data, b'\0' * 256)
489         self.image.remove_snap('snap1')
490
491     def test_list_snaps(self):
492         eq([], list(self.image.list_snaps()))
493         self.image.create_snap('snap1')
494         eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
495         self.image.create_snap('snap2')
496         eq(['snap1', 'snap2'], [snap['name'] for snap in self.image.list_snaps()])
497         self.image.remove_snap('snap1')
498         self.image.remove_snap('snap2')
499
500     def test_list_snaps_iterator_auto_close(self):
501         self.image.create_snap('snap1')
502         self.image.list_snaps()
503         self.image.remove_snap('snap1')
504
505     def test_remove_snap(self):
506         eq([], list(self.image.list_snaps()))
507         self.image.create_snap('snap1')
508         eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
509         self.image.remove_snap('snap1')
510         eq([], list(self.image.list_snaps()))
511
512     def test_rename_snap(self):
513         eq([], list(self.image.list_snaps()))
514         self.image.create_snap('snap1')
515         eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
516         self.image.rename_snap("snap1", "snap1-rename")
517         eq(['snap1-rename'], [snap['name'] for snap in self.image.list_snaps()])
518         self.image.remove_snap('snap1-rename')
519         eq([], list(self.image.list_snaps()))
520
521     @require_features([RBD_FEATURE_LAYERING])
522     def test_protect_snap(self):
523         self.image.create_snap('snap1')
524         assert(not self.image.is_protected_snap('snap1'))
525         self.image.protect_snap('snap1')
526         assert(self.image.is_protected_snap('snap1'))
527         assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
528         self.image.unprotect_snap('snap1')
529         assert(not self.image.is_protected_snap('snap1'))
530         self.image.remove_snap('snap1')
531         assert_raises(ImageNotFound, self.image.unprotect_snap, 'snap1')
532         assert_raises(ImageNotFound, self.image.is_protected_snap, 'snap1')
533
534     def test_snap_timestamp(self):
535         self.image.create_snap('snap1')
536         eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
537         for snap in self.image.list_snaps():
538             snap_id = snap["id"]
539         time = self.image.get_snap_timestamp(snap_id)
540         assert_not_equal(b'', time.year)
541         assert_not_equal(0, time.year)
542         assert_not_equal(time.year, '1970')
543         self.image.remove_snap('snap1')
544
545     def test_limit_snaps(self):
546         self.image.set_snap_limit(2)
547         eq(2, self.image.get_snap_limit())
548         self.image.create_snap('snap1')
549         self.image.create_snap('snap2')
550         assert_raises(DiskQuotaExceeded, self.image.create_snap, 'snap3')
551         self.image.remove_snap_limit()
552         self.image.create_snap('snap3')
553
554         self.image.remove_snap('snap1')
555         self.image.remove_snap('snap2')
556         self.image.remove_snap('snap3')
557
558     @require_features([RBD_FEATURE_EXCLUSIVE_LOCK])
559     def test_remove_with_exclusive_lock(self):
560         assert_raises(ImageBusy, remove_image)
561
562     @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK])
563     def test_remove_with_snap(self):
564         self.image.create_snap('snap1')
565         assert_raises(ImageHasSnapshots, remove_image)
566         self.image.remove_snap('snap1')
567
568     @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK])
569     def test_remove_with_watcher(self):
570         data = rand_data(256)
571         self.image.write(data, 0)
572         assert_raises(ImageBusy, remove_image)
573         read = self.image.read(0, 256)
574         eq(read, data)
575
576     def test_rollback_to_snap(self):
577         self.image.write(b'\0' * 256, 0)
578         self.image.create_snap('snap1')
579         read = self.image.read(0, 256)
580         eq(read, b'\0' * 256)
581         data = rand_data(256)
582         self.image.write(data, 0)
583         read = self.image.read(0, 256)
584         eq(read, data)
585         self.image.rollback_to_snap('snap1')
586         read = self.image.read(0, 256)
587         eq(read, b'\0' * 256)
588         self.image.remove_snap('snap1')
589
590     def test_rollback_to_snap_sparse(self):
591         self.image.create_snap('snap1')
592         read = self.image.read(0, 256)
593         eq(read, b'\0' * 256)
594         data = rand_data(256)
595         self.image.write(data, 0)
596         read = self.image.read(0, 256)
597         eq(read, data)
598         self.image.rollback_to_snap('snap1')
599         read = self.image.read(0, 256)
600         eq(read, b'\0' * 256)
601         self.image.remove_snap('snap1')
602
603     def test_rollback_with_resize(self):
604         read = self.image.read(0, 256)
605         eq(read, b'\0' * 256)
606         data = rand_data(256)
607         self.image.write(data, 0)
608         self.image.create_snap('snap1')
609         read = self.image.read(0, 256)
610         eq(read, data)
611         new_size = IMG_SIZE * 2
612         self.image.resize(new_size)
613         check_stat(self.image.stat(), new_size, IMG_ORDER)
614         self.image.write(data, new_size - 256)
615         self.image.create_snap('snap2')
616         read = self.image.read(new_size - 256, 256)
617         eq(read, data)
618         self.image.rollback_to_snap('snap1')
619         check_stat(self.image.stat(), IMG_SIZE, IMG_ORDER)
620         assert_raises(InvalidArgument, self.image.read, new_size - 256, 256)
621         self.image.rollback_to_snap('snap2')
622         check_stat(self.image.stat(), new_size, IMG_ORDER)
623         read = self.image.read(new_size - 256, 256)
624         eq(read, data)
625         self.image.remove_snap('snap1')
626         self.image.remove_snap('snap2')
627
628     def test_set_snap(self):
629         self.image.write(b'\0' * 256, 0)
630         self.image.create_snap('snap1')
631         read = self.image.read(0, 256)
632         eq(read, b'\0' * 256)
633         data = rand_data(256)
634         self.image.write(data, 0)
635         read = self.image.read(0, 256)
636         eq(read, data)
637         self.image.set_snap('snap1')
638         read = self.image.read(0, 256)
639         eq(read, b'\0' * 256)
640         self.image.remove_snap('snap1')
641
642     def test_set_no_snap(self):
643         self.image.write(b'\0' * 256, 0)
644         self.image.create_snap('snap1')
645         read = self.image.read(0, 256)
646         eq(read, b'\0' * 256)
647         data = rand_data(256)
648         self.image.write(data, 0)
649         read = self.image.read(0, 256)
650         eq(read, data)
651         self.image.set_snap('snap1')
652         read = self.image.read(0, 256)
653         eq(read, b'\0' * 256)
654         self.image.set_snap(None)
655         read = self.image.read(0, 256)
656         eq(read, data)
657         self.image.remove_snap('snap1')
658
659     def test_set_snap_sparse(self):
660         self.image.create_snap('snap1')
661         read = self.image.read(0, 256)
662         eq(read, b'\0' * 256)
663         data = rand_data(256)
664         self.image.write(data, 0)
665         read = self.image.read(0, 256)
666         eq(read, data)
667         self.image.set_snap('snap1')
668         read = self.image.read(0, 256)
669         eq(read, b'\0' * 256)
670         self.image.remove_snap('snap1')
671
672     def test_many_snaps(self):
673         num_snaps = 200
674         for i in range(num_snaps):
675             self.image.create_snap(str(i))
676         snaps = sorted(self.image.list_snaps(),
677                        key=lambda snap: int(snap['name']))
678         eq(len(snaps), num_snaps)
679         for i, snap in enumerate(snaps):
680             eq(snap['size'], IMG_SIZE)
681             eq(snap['name'], str(i))
682         for i in range(num_snaps):
683             self.image.remove_snap(str(i))
684
685     def test_set_snap_deleted(self):
686         self.image.write(b'\0' * 256, 0)
687         self.image.create_snap('snap1')
688         read = self.image.read(0, 256)
689         eq(read, b'\0' * 256)
690         data = rand_data(256)
691         self.image.write(data, 0)
692         read = self.image.read(0, 256)
693         eq(read, data)
694         self.image.set_snap('snap1')
695         self.image.remove_snap('snap1')
696         assert_raises(ImageNotFound, self.image.read, 0, 256)
697         self.image.set_snap(None)
698         read = self.image.read(0, 256)
699         eq(read, data)
700
701     def test_set_snap_recreated(self):
702         self.image.write(b'\0' * 256, 0)
703         self.image.create_snap('snap1')
704         read = self.image.read(0, 256)
705         eq(read, b'\0' * 256)
706         data = rand_data(256)
707         self.image.write(data, 0)
708         read = self.image.read(0, 256)
709         eq(read, data)
710         self.image.set_snap('snap1')
711         self.image.remove_snap('snap1')
712         self.image.create_snap('snap1')
713         assert_raises(ImageNotFound, self.image.read, 0, 256)
714         self.image.set_snap(None)
715         read = self.image.read(0, 256)
716         eq(read, data)
717         self.image.remove_snap('snap1')
718
719     def test_lock_unlock(self):
720         assert_raises(ImageNotFound, self.image.unlock, '')
721         self.image.lock_exclusive('')
722         assert_raises(ImageExists, self.image.lock_exclusive, '')
723         assert_raises(ImageBusy, self.image.lock_exclusive, 'test')
724         assert_raises(ImageExists, self.image.lock_shared, '', '')
725         assert_raises(ImageBusy, self.image.lock_shared, 'foo', '')
726         self.image.unlock('')
727
728     def test_list_lockers(self):
729         eq([], self.image.list_lockers())
730         self.image.lock_exclusive('test')
731         lockers = self.image.list_lockers()
732         eq(1, len(lockers['lockers']))
733         _, cookie, _ = lockers['lockers'][0]
734         eq(cookie, 'test')
735         eq('', lockers['tag'])
736         assert lockers['exclusive']
737         self.image.unlock('test')
738         eq([], self.image.list_lockers())
739
740         num_shared = 10
741         for i in range(num_shared):
742             self.image.lock_shared(str(i), 'tag')
743         lockers = self.image.list_lockers()
744         eq('tag', lockers['tag'])
745         assert not lockers['exclusive']
746         eq(num_shared, len(lockers['lockers']))
747         cookies = sorted(map(lambda x: x[1], lockers['lockers']))
748         for i in range(num_shared):
749             eq(str(i), cookies[i])
750             self.image.unlock(str(i))
751         eq([], self.image.list_lockers())
752
753     def test_diff_iterate(self):
754         check_diff(self.image, 0, IMG_SIZE, None, [])
755         self.image.write(b'a' * 256, 0)
756         check_diff(self.image, 0, IMG_SIZE, None, [(0, 256, True)])
757         self.image.write(b'b' * 256, 256)
758         check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)])
759         self.image.discard(128, 256)
760         check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)])
761
762         self.image.create_snap('snap1')
763         self.image.discard(0, 1 << IMG_ORDER)
764         self.image.create_snap('snap2')
765         self.image.set_snap('snap2')
766         check_diff(self.image, 0, IMG_SIZE, 'snap1', [(0, 512, False)])
767         self.image.remove_snap('snap1')
768         self.image.remove_snap('snap2')
769
770     def test_aio_read(self):
771         # this is a list so that the local cb() can modify it
772         retval = [None]
773         def cb(_, buf):
774             retval[0] = buf
775
776         # test1: success case
777         comp = self.image.aio_read(0, 20, cb)
778         comp.wait_for_complete_and_cb()
779         eq(retval[0], b'\0' * 20)
780         eq(comp.get_return_value(), 20)
781         eq(sys.getrefcount(comp), 2)
782
783         # test2: error case
784         retval[0] = 1
785         comp = self.image.aio_read(IMG_SIZE, 20, cb)
786         comp.wait_for_complete_and_cb()
787         eq(None, retval[0])
788         assert(comp.get_return_value() < 0)
789         eq(sys.getrefcount(comp), 2)
790
791     def test_aio_write(self):
792         retval = [None]
793         def cb(comp):
794             retval[0] = comp.get_return_value()
795
796         data = rand_data(256)
797         comp = self.image.aio_write(data, 256, cb)
798         comp.wait_for_complete_and_cb()
799         eq(retval[0], 0)
800         eq(comp.get_return_value(), 0)
801         eq(sys.getrefcount(comp), 2)
802         eq(self.image.read(256, 256), data)
803
804     def test_aio_discard(self):
805         retval = [None]
806         def cb(comp):
807             retval[0] = comp.get_return_value()
808
809         data = rand_data(256)
810         self.image.write(data, 0)
811         comp = self.image.aio_discard(0, 256, cb)
812         comp.wait_for_complete_and_cb()
813         eq(retval[0], 0)
814         eq(comp.get_return_value(), 0)
815         eq(sys.getrefcount(comp), 2)
816         eq(self.image.read(256, 256), b'\0' * 256)
817
818     def test_aio_flush(self):
819         retval = [None]
820         def cb(comp):
821             retval[0] = comp.get_return_value()
822
823         comp = self.image.aio_flush(cb)
824         comp.wait_for_complete_and_cb()
825         eq(retval[0], 0)
826         eq(sys.getrefcount(comp), 2)
827
828     def test_metadata(self):
829         metadata = list(self.image.metadata_list())
830         eq(len(metadata), 0)
831         self.image.metadata_set("key1", "value1")
832         self.image.metadata_set("key2", "value2")
833         value = self.image.metadata_get("key1")
834         eq(value, "value1")
835         value = self.image.metadata_get("key2")
836         eq(value, "value2")
837         metadata = list(self.image.metadata_list())
838         eq(len(metadata), 2)
839         self.image.metadata_remove("key1")
840         metadata = list(self.image.metadata_list())
841         eq(len(metadata), 1)
842         eq(metadata[0], ("key2", "value2"))
843         self.image.metadata_remove("key2")
844         metadata = list(self.image.metadata_list())
845         eq(len(metadata), 0)
846
847         N = 65
848         for i in xrange(N):
849             self.image.metadata_set("key" + str(i), "X" * 1025)
850         metadata = list(self.image.metadata_list())
851         eq(len(metadata), N)
852         for i in xrange(N):
853             self.image.metadata_remove("key" + str(i))
854             metadata = list(self.image.metadata_list())
855             eq(len(metadata), N - i - 1)
856
857 def check_diff(image, offset, length, from_snapshot, expected):
858     extents = []
859     def cb(offset, length, exists):
860         extents.append((offset, length, exists))
861     image.diff_iterate(0, IMG_SIZE, None, cb)
862     eq(extents, expected)
863
864 class TestClone(object):
865
866     @require_features([RBD_FEATURE_LAYERING])
867     def setUp(self):
868         global ioctx
869         global features
870         self.rbd = RBD()
871         create_image()
872         self.image = Image(ioctx, image_name)
873         data = rand_data(256)
874         self.image.write(data, IMG_SIZE // 2)
875         self.image.create_snap('snap1')
876         global features
877         self.image.protect_snap('snap1')
878         self.clone_name = get_temp_image_name()
879         self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name,
880                        features)
881         self.clone = Image(ioctx, self.clone_name)
882
883     def tearDown(self):
884         global ioctx
885         self.clone.close()
886         self.rbd.remove(ioctx, self.clone_name)
887         self.image.unprotect_snap('snap1')
888         self.image.remove_snap('snap1')
889         self.image.close()
890         remove_image()
891
892     def _test_with_params(self, features=None, order=None, stripe_unit=None,
893                           stripe_count=None):
894         self.image.create_snap('snap2')
895         self.image.protect_snap('snap2')
896         clone_name2 = get_temp_image_name()
897         if features is None:
898             self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2)
899         elif order is None:
900             self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
901                            features)
902         elif stripe_unit is None:
903             self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
904                            features, order)
905         elif stripe_count is None:
906             self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
907                            features, order, stripe_unit)
908         else:
909             self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2,
910                            features, order, stripe_unit, stripe_count)
911         self.rbd.remove(ioctx, clone_name2)
912         self.image.unprotect_snap('snap2')
913         self.image.remove_snap('snap2')
914
915     def test_with_params(self):
916         self._test_with_params()
917
918     def test_with_params2(self):
919         global features
920         self._test_with_params(features, self.image.stat()['order'])
921
922     @require_features([RBD_FEATURE_STRIPINGV2])
923     def test_with_params3(self):
924         global features
925         self._test_with_params(features, self.image.stat()['order'],
926                                self.image.stripe_unit(),
927                                self.image.stripe_count())
928
929     def test_unprotected(self):
930         self.image.create_snap('snap2')
931         global features
932         clone_name2 = get_temp_image_name()
933         assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name,
934                       'snap2', ioctx, clone_name2, features)
935         self.image.remove_snap('snap2')
936
937     def test_unprotect_with_children(self):
938         global features
939         # can't remove a snapshot that has dependent clones
940         assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
941
942         # validate parent info of clone created by TestClone.setUp
943         (pool, image, snap) = self.clone.parent_info()
944         eq(pool, pool_name)
945         eq(image, image_name)
946         eq(snap, 'snap1')
947         eq(self.image.id(), self.clone.parent_id())
948
949         # create a new pool...
950         pool_name2 = get_temp_pool_name()
951         rados.create_pool(pool_name2)
952         other_ioctx = rados.open_ioctx(pool_name2)
953         other_ioctx.application_enable('rbd')
954
955         # ...with a clone of the same parent
956         other_clone_name = get_temp_image_name()
957         self.rbd.clone(ioctx, image_name, 'snap1', other_ioctx,
958                        other_clone_name, features)
959         self.other_clone = Image(other_ioctx, other_clone_name)
960         # validate its parent info
961         (pool, image, snap) = self.other_clone.parent_info()
962         eq(pool, pool_name)
963         eq(image, image_name)
964         eq(snap, 'snap1')
965         eq(self.image.id(), self.other_clone.parent_id())
966
967         # can't unprotect snap with children
968         assert_raises(ImageBusy, self.image.unprotect_snap, 'snap1')
969
970         # 2 children, check that cannot remove the parent snap
971         assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
972
973         # close and remove other pool's clone
974         self.other_clone.close()
975         self.rbd.remove(other_ioctx, other_clone_name)
976
977         # check that we cannot yet remove the parent snap
978         assert_raises(ImageBusy, self.image.remove_snap, 'snap1')
979
980         other_ioctx.close()
981         rados.delete_pool(pool_name2)
982
983         # unprotect, remove parent snap happen in cleanup, and should succeed
984
985     def test_stat(self):
986         image_info = self.image.stat()
987         clone_info = self.clone.stat()
988         eq(clone_info['size'], image_info['size'])
989         eq(clone_info['size'], self.clone.overlap())
990
991     def test_resize_stat(self):
992         self.clone.resize(IMG_SIZE // 2)
993         image_info = self.image.stat()
994         clone_info = self.clone.stat()
995         eq(clone_info['size'], IMG_SIZE // 2)
996         eq(image_info['size'], IMG_SIZE)
997         eq(self.clone.overlap(), IMG_SIZE // 2)
998
999         self.clone.resize(IMG_SIZE * 2)
1000         image_info = self.image.stat()
1001         clone_info = self.clone.stat()
1002         eq(clone_info['size'], IMG_SIZE * 2)
1003         eq(image_info['size'], IMG_SIZE)
1004         eq(self.clone.overlap(), IMG_SIZE // 2)
1005
1006     def test_resize_io(self):
1007         parent_data = self.image.read(IMG_SIZE // 2, 256)
1008         self.image.resize(0)
1009         self.clone.resize(IMG_SIZE // 2 + 128)
1010         child_data = self.clone.read(IMG_SIZE // 2, 128)
1011         eq(child_data, parent_data[:128])
1012         self.clone.resize(IMG_SIZE)
1013         child_data = self.clone.read(IMG_SIZE // 2, 256)
1014         eq(child_data, parent_data[:128] + (b'\0' * 128))
1015         self.clone.resize(IMG_SIZE // 2 + 1)
1016         child_data = self.clone.read(IMG_SIZE // 2, 1)
1017         eq(child_data, parent_data[0:1])
1018         self.clone.resize(0)
1019         self.clone.resize(IMG_SIZE)
1020         child_data = self.clone.read(IMG_SIZE // 2, 256)
1021         eq(child_data, b'\0' * 256)
1022
1023     def test_read(self):
1024         parent_data = self.image.read(IMG_SIZE // 2, 256)
1025         child_data = self.clone.read(IMG_SIZE // 2, 256)
1026         eq(child_data, parent_data)
1027
1028     def test_write(self):
1029         parent_data = self.image.read(IMG_SIZE // 2, 256)
1030         new_data = rand_data(256)
1031         self.clone.write(new_data, IMG_SIZE // 2 + 256)
1032         child_data = self.clone.read(IMG_SIZE // 2 + 256, 256)
1033         eq(child_data, new_data)
1034         child_data = self.clone.read(IMG_SIZE // 2, 256)
1035         eq(child_data, parent_data)
1036         parent_data = self.image.read(IMG_SIZE // 2 + 256, 256)
1037         eq(parent_data, b'\0' * 256)
1038
1039     def check_children(self, expected):
1040         actual = self.image.list_children()
1041         # dedup for cache pools until
1042         # http://tracker.ceph.com/issues/8187 is fixed
1043         deduped = set([(pool_name, image[1]) for image in actual])
1044         eq(deduped, set(expected))
1045
1046     def test_list_children(self):
1047         global ioctx
1048         global features
1049         self.image.set_snap('snap1')
1050         self.check_children([(pool_name, self.clone_name)])
1051         self.clone.close()
1052         self.rbd.remove(ioctx, self.clone_name)
1053         eq(self.image.list_children(), [])
1054
1055         clone_name = get_temp_image_name() + '_'
1056         expected_children = []
1057         for i in range(10):
1058             self.rbd.clone(ioctx, image_name, 'snap1', ioctx,
1059                            clone_name + str(i), features)
1060             expected_children.append((pool_name, clone_name + str(i)))
1061             self.check_children(expected_children)
1062
1063         for i in range(10):
1064             self.rbd.remove(ioctx, clone_name + str(i))
1065             expected_children.pop(0)
1066             self.check_children(expected_children)
1067
1068         eq(self.image.list_children(), [])
1069         self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name,
1070                        features)
1071         self.check_children([(pool_name, self.clone_name)])
1072         self.clone = Image(ioctx, self.clone_name)
1073
1074     def test_flatten_errors(self):
1075         # test that we can't flatten a non-clone
1076         assert_raises(InvalidArgument, self.image.flatten)
1077
1078         # test that we can't flatten a snapshot
1079         self.clone.create_snap('snap2')
1080         self.clone.set_snap('snap2')
1081         assert_raises(ReadOnlyImage, self.clone.flatten)
1082         self.clone.remove_snap('snap2')
1083
1084     def check_flatten_with_order(self, new_order):
1085         global ioctx
1086         global features
1087         clone_name2 = get_temp_image_name()
1088         self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1089                        features, new_order)
1090         #with Image(ioctx, 'clone2') as clone:
1091         clone2 = Image(ioctx, clone_name2)
1092         clone2.flatten()
1093         eq(clone2.overlap(), 0)
1094         clone2.close()
1095         self.rbd.remove(ioctx, clone_name2)
1096
1097         # flatten after resizing to non-block size
1098         self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1099                        features, new_order)
1100         with Image(ioctx, clone_name2) as clone:
1101             clone.resize(IMG_SIZE // 2 - 1)
1102             clone.flatten()
1103             eq(0, clone.overlap())
1104         self.rbd.remove(ioctx, clone_name2)
1105
1106         # flatten after resizing to non-block size
1107         self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1108                        features, new_order)
1109         with Image(ioctx, clone_name2) as clone:
1110             clone.resize(IMG_SIZE // 2 + 1)
1111             clone.flatten()
1112             eq(clone.overlap(), 0)
1113         self.rbd.remove(ioctx, clone_name2)
1114
1115     def test_flatten_basic(self):
1116         self.check_flatten_with_order(IMG_ORDER)
1117
1118     def test_flatten_smaller_order(self):
1119         self.check_flatten_with_order(IMG_ORDER - 2)
1120
1121     def test_flatten_larger_order(self):
1122         self.check_flatten_with_order(IMG_ORDER + 2)
1123
1124     def test_flatten_drops_cache(self):
1125         global ioctx
1126         global features
1127         clone_name2 = get_temp_image_name()
1128         self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2,
1129                        features, IMG_ORDER)
1130         with Image(ioctx, clone_name2) as clone:
1131             with Image(ioctx, clone_name2) as clone2:
1132                 # cache object non-existence
1133                 data = clone.read(IMG_SIZE // 2, 256)
1134                 clone2_data = clone2.read(IMG_SIZE // 2, 256)
1135                 eq(data, clone2_data)
1136                 clone.flatten()
1137                 assert_raises(ImageNotFound, clone.parent_info)
1138                 assert_raises(ImageNotFound, clone2.parent_info)
1139                 assert_raises(ImageNotFound, clone.parent_id)
1140                 assert_raises(ImageNotFound, clone2.parent_id)
1141                 after_flatten = clone.read(IMG_SIZE // 2, 256)
1142                 eq(data, after_flatten)
1143                 after_flatten = clone2.read(IMG_SIZE // 2, 256)
1144                 eq(data, after_flatten)
1145         self.rbd.remove(ioctx, clone_name2)
1146
1147     def test_flatten_multi_level(self):
1148         self.clone.create_snap('snap2')
1149         self.clone.protect_snap('snap2')
1150         clone_name3 = get_temp_image_name()
1151         self.rbd.clone(ioctx, self.clone_name, 'snap2', ioctx, clone_name3,
1152                        features)
1153         self.clone.flatten()
1154         with Image(ioctx, clone_name3) as clone3:
1155             clone3.flatten()
1156         self.clone.unprotect_snap('snap2')
1157         self.clone.remove_snap('snap2')
1158         self.rbd.remove(ioctx, clone_name3)
1159
1160     def test_resize_flatten_multi_level(self):
1161         self.clone.create_snap('snap2')
1162         self.clone.protect_snap('snap2')
1163         clone_name3 = get_temp_image_name()
1164         self.rbd.clone(ioctx, self.clone_name, 'snap2', ioctx, clone_name3,
1165                        features)
1166         self.clone.resize(1)
1167         orig_data = self.image.read(0, 256)
1168         with Image(ioctx, clone_name3) as clone3:
1169             clone3_data = clone3.read(0, 256)
1170             eq(orig_data, clone3_data)
1171         self.clone.flatten()
1172         with Image(ioctx, clone_name3) as clone3:
1173             clone3_data = clone3.read(0, 256)
1174             eq(orig_data, clone3_data)
1175         self.rbd.remove(ioctx, clone_name3)
1176         self.clone.unprotect_snap('snap2')
1177         self.clone.remove_snap('snap2')
1178
1179 class TestExclusiveLock(object):
1180
1181     @require_features([RBD_FEATURE_EXCLUSIVE_LOCK])
1182     def setUp(self):
1183         global rados2
1184         rados2 = Rados(conffile='')
1185         rados2.connect()
1186         global ioctx2
1187         ioctx2 = rados2.open_ioctx(pool_name)
1188         create_image()
1189
1190     def tearDown(self):
1191         remove_image()
1192         global ioctx2
1193         ioctx2.close()
1194         global rados2
1195         rados2.shutdown()
1196
1197     def test_ownership(self):
1198         with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1199             image1.write(b'0'*256, 0)
1200             eq(image1.is_exclusive_lock_owner(), True)
1201             eq(image2.is_exclusive_lock_owner(), False)
1202
1203     def test_snapshot_leadership(self):
1204         with Image(ioctx, image_name) as image:
1205             image.create_snap('snap')
1206             eq(image.is_exclusive_lock_owner(), True)
1207         try:
1208             with Image(ioctx, image_name) as image:
1209                 image.write(b'0'*256, 0)
1210                 eq(image.is_exclusive_lock_owner(), True)
1211                 image.set_snap('snap')
1212                 eq(image.is_exclusive_lock_owner(), False)
1213             with Image(ioctx, image_name, snapshot='snap') as image:
1214                 eq(image.is_exclusive_lock_owner(), False)
1215         finally:
1216             with Image(ioctx, image_name) as image:
1217                 image.remove_snap('snap')
1218
1219     def test_read_only_leadership(self):
1220         with Image(ioctx, image_name, read_only=True) as image:
1221             eq(image.is_exclusive_lock_owner(), False)
1222
1223     def test_follower_flatten(self):
1224         with Image(ioctx, image_name) as image:
1225             image.create_snap('snap')
1226             image.protect_snap('snap')
1227         try:
1228             RBD().clone(ioctx, image_name, 'snap', ioctx, 'clone', features)
1229             with Image(ioctx, 'clone') as image1, Image(ioctx2, 'clone') as image2:
1230                 data = rand_data(256)
1231                 image1.write(data, 0)
1232                 image2.flatten()
1233                 assert_raises(ImageNotFound, image1.parent_info)
1234                 assert_raises(ImageNotFound, image1.parent_id)
1235                 parent = True
1236                 for x in range(30):
1237                     try:
1238                         image2.parent_info()
1239                     except ImageNotFound:
1240                         parent = False
1241                         break
1242                 eq(False, parent)
1243         finally:
1244             RBD().remove(ioctx, 'clone')
1245             with Image(ioctx, image_name) as image:
1246                 image.unprotect_snap('snap')
1247                 image.remove_snap('snap')
1248
1249     def test_follower_resize(self):
1250         with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1251             image1.write(b'0'*256, 0)
1252             for new_size in [IMG_SIZE * 2, IMG_SIZE // 2]:
1253                 image2.resize(new_size);
1254                 eq(new_size, image1.size())
1255                 for x in range(30):
1256                     if new_size == image2.size():
1257                         break
1258                     time.sleep(1)
1259                 eq(new_size, image2.size())
1260
1261     def test_follower_snap_create(self):
1262         with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1263             image2.create_snap('snap1')
1264             image1.remove_snap('snap1')
1265
1266     def test_follower_snap_rollback(self):
1267         with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1268             image1.create_snap('snap')
1269             try:
1270                 assert_raises(ReadOnlyImage, image2.rollback_to_snap, 'snap')
1271                 image1.rollback_to_snap('snap')
1272             finally:
1273                 image1.remove_snap('snap')
1274
1275     def test_follower_discard(self):
1276         global rados
1277         with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1278             data = rand_data(256)
1279             image1.write(data, 0)
1280             image2.discard(0, 256)
1281             eq(image1.is_exclusive_lock_owner(), False)
1282             eq(image2.is_exclusive_lock_owner(), True)
1283             read = image2.read(0, 256)
1284             if rados.conf_get('rbd_skip_partial_discard') == 'false':
1285                 eq(256 * b'\0', read)
1286             else:
1287                 eq(data, read)
1288
1289     def test_follower_write(self):
1290         with Image(ioctx, image_name) as image1, Image(ioctx2, image_name) as image2:
1291             data = rand_data(256)
1292             image1.write(data, 0)
1293             image2.write(data, IMG_SIZE // 2)
1294             eq(image1.is_exclusive_lock_owner(), False)
1295             eq(image2.is_exclusive_lock_owner(), True)
1296             for offset in [0, IMG_SIZE // 2]:
1297                 read = image2.read(offset, 256)
1298                 eq(data, read)
1299     def test_acquire_release_lock(self):
1300         with Image(ioctx, image_name) as image:
1301             image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
1302             image.lock_release()
1303
1304     def test_break_lock(self):
1305         blacklist_rados = Rados(conffile='')
1306         blacklist_rados.connect()
1307         try:
1308             blacklist_ioctx = blacklist_rados.open_ioctx(pool_name)
1309             try:
1310                 rados2.conf_set('rbd_blacklist_on_break_lock', 'true')
1311                 with Image(ioctx2, image_name) as image, \
1312                      Image(blacklist_ioctx, image_name) as blacklist_image:
1313                     blacklist_image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
1314                     assert_raises(ReadOnlyImage, image.lock_acquire,
1315                                   RBD_LOCK_MODE_EXCLUSIVE)
1316
1317                     lock_owners = list(image.lock_get_owners())
1318                     eq(1, len(lock_owners))
1319                     eq(RBD_LOCK_MODE_EXCLUSIVE, lock_owners[0]['mode'])
1320                     image.lock_break(RBD_LOCK_MODE_EXCLUSIVE,
1321                                      lock_owners[0]['owner'])
1322
1323                     assert_raises(ConnectionShutdown,
1324                                   blacklist_image.is_exclusive_lock_owner)
1325
1326                     blacklist_rados.wait_for_latest_osdmap()
1327                     data = rand_data(256)
1328                     assert_raises(ConnectionShutdown,
1329                                   blacklist_image.write, data, 0)
1330
1331                     image.lock_acquire(RBD_LOCK_MODE_EXCLUSIVE)
1332
1333                     try:
1334                         blacklist_image.close()
1335                     except ConnectionShutdown:
1336                         pass
1337             finally:
1338                 blacklist_ioctx.close()
1339         finally:
1340             blacklist_rados.shutdown()
1341
1342 class TestMirroring(object):
1343
1344     @staticmethod
1345     def check_info(info, global_id, state, primary=None):
1346         eq(global_id, info['global_id'])
1347         eq(state, info['state'])
1348         if primary is not None:
1349             eq(primary, info['primary'])
1350
1351     def setUp(self):
1352         self.rbd = RBD()
1353         self.initial_mirror_mode = self.rbd.mirror_mode_get(ioctx)
1354         self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL)
1355         create_image()
1356         self.image = Image(ioctx, image_name)
1357
1358     def tearDown(self):
1359         self.image.close()
1360         remove_image()
1361         self.rbd.mirror_mode_set(ioctx, self.initial_mirror_mode)
1362
1363
1364     def test_mirror_peer(self):
1365         eq([], list(self.rbd.mirror_peer_list(ioctx)))
1366         cluster_name = "test_cluster"
1367         client_name = "test_client"
1368         uuid = self.rbd.mirror_peer_add(ioctx, cluster_name, client_name)
1369         assert(uuid)
1370         peer = {
1371             'uuid' : uuid,
1372             'cluster_name' : cluster_name,
1373             'client_name' : client_name,
1374             }
1375         eq([peer], list(self.rbd.mirror_peer_list(ioctx)))
1376         cluster_name = "test_cluster1"
1377         self.rbd.mirror_peer_set_cluster(ioctx, uuid, cluster_name)
1378         client_name = "test_client1"
1379         self.rbd.mirror_peer_set_client(ioctx, uuid, client_name)
1380         peer = {
1381             'uuid' : uuid,
1382             'cluster_name' : cluster_name,
1383             'client_name' : client_name,
1384             }
1385         eq([peer], list(self.rbd.mirror_peer_list(ioctx)))
1386         self.rbd.mirror_peer_remove(ioctx, uuid)
1387         eq([], list(self.rbd.mirror_peer_list(ioctx)))
1388
1389     @require_features([RBD_FEATURE_EXCLUSIVE_LOCK,
1390                        RBD_FEATURE_JOURNALING])
1391     def test_mirror_image(self):
1392
1393         self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE)
1394         self.image.mirror_image_disable(True)
1395         info = self.image.mirror_image_get_info()
1396         self.check_info(info, '', RBD_MIRROR_IMAGE_DISABLED, False)
1397
1398         self.image.mirror_image_enable()
1399         info = self.image.mirror_image_get_info()
1400         global_id = info['global_id']
1401         self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
1402
1403         self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL)
1404         fail = False
1405         try:
1406             self.image.mirror_image_disable(True)
1407         except InvalidArgument:
1408             fail = True
1409         eq(True, fail) # Fails because of mirror mode pool
1410
1411         self.image.mirror_image_demote()
1412         info = self.image.mirror_image_get_info()
1413         self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, False)
1414
1415         self.image.mirror_image_resync()
1416
1417         self.image.mirror_image_promote(True)
1418         info = self.image.mirror_image_get_info()
1419         self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
1420
1421         fail = False
1422         try:
1423             self.image.mirror_image_resync()
1424         except InvalidArgument:
1425             fail = True
1426         eq(True, fail) # Fails because it is primary
1427
1428         status = self.image.mirror_image_get_status()
1429         eq(image_name, status['name'])
1430         eq(False, status['up'])
1431         eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
1432         info = status['info']
1433         self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
1434
1435     @require_features([RBD_FEATURE_EXCLUSIVE_LOCK,
1436                        RBD_FEATURE_JOURNALING])
1437     def test_mirror_image_status(self):
1438         info = self.image.mirror_image_get_info()
1439         global_id = info['global_id']
1440         state = info['state']
1441         primary = info['primary']
1442
1443         status = self.image.mirror_image_get_status()
1444         eq(image_name, status['name'])
1445         eq(False, status['up'])
1446         eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
1447         info = status['info']
1448         self.check_info(info, global_id, state, primary)
1449
1450         images = list(self.rbd.mirror_image_status_list(ioctx))
1451         eq(1, len(images))
1452         status = images[0]
1453         eq(image_name, status['name'])
1454         eq(False, status['up'])
1455         eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
1456         info = status['info']
1457         self.check_info(info, global_id, state)
1458
1459         states = self.rbd.mirror_image_status_summary(ioctx)
1460         eq([(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, 1)], states)
1461
1462         N = 65
1463         for i in range(N):
1464             self.rbd.create(ioctx, image_name + str(i), IMG_SIZE, IMG_ORDER,
1465                             old_format=False, features=int(features))
1466         images = list(self.rbd.mirror_image_status_list(ioctx))
1467         eq(N + 1, len(images))
1468         for i in range(N):
1469             self.rbd.remove(ioctx, image_name + str(i))
1470
1471
1472 class TestTrash(object):
1473
1474     def setUp(self):
1475         global rados2
1476         rados2 = Rados(conffile='')
1477         rados2.connect()
1478         global ioctx2
1479         ioctx2 = rados2.open_ioctx(pool_name)
1480
1481     def tearDown(self):
1482         global ioctx2
1483         ioctx2.close()
1484         global rados2
1485         rados2.shutdown()
1486
1487     def test_move(self):
1488         create_image()
1489         with Image(ioctx, image_name) as image:
1490             image_id = image.id()
1491
1492         RBD().trash_move(ioctx, image_name, 1000)
1493         RBD().trash_remove(ioctx, image_id, True)
1494
1495     def test_remove_denied(self):
1496         create_image()
1497         with Image(ioctx, image_name) as image:
1498             image_id = image.id()
1499
1500         RBD().trash_move(ioctx, image_name, 1000)
1501         assert_raises(PermissionError, RBD().trash_remove, ioctx, image_id)
1502
1503     def test_remove(self):
1504         create_image()
1505         with Image(ioctx, image_name) as image:
1506             image_id = image.id()
1507
1508         RBD().trash_move(ioctx, image_name, 0)
1509         RBD().trash_remove(ioctx, image_id)
1510
1511     def test_get(self):
1512         create_image()
1513         with Image(ioctx, image_name) as image:
1514             image_id = image.id()
1515
1516         RBD().trash_move(ioctx, image_name, 1000)
1517
1518         info = RBD().trash_get(ioctx, image_id)
1519         eq(image_id, info['id'])
1520         eq(image_name, info['name'])
1521         eq('USER', info['source'])
1522         assert(info['deferment_end_time'] > info['deletion_time'])
1523
1524         RBD().trash_remove(ioctx, image_id, True)
1525
1526     def test_list(self):
1527         create_image()
1528         with Image(ioctx, image_name) as image:
1529             image_id1 = image.id()
1530             image_name1 = image_name
1531         RBD().trash_move(ioctx, image_name, 1000)
1532
1533         create_image()
1534         with Image(ioctx, image_name) as image:
1535             image_id2 = image.id()
1536             image_name2 = image_name
1537         RBD().trash_move(ioctx, image_name, 1000)
1538
1539         entries = list(RBD().trash_list(ioctx))
1540         for e in entries:
1541             if e['id'] == image_id1:
1542                 eq(e['name'], image_name1)
1543             elif e['id'] == image_id2:
1544                 eq(e['name'], image_name2)
1545             else:
1546                 assert False
1547             eq(e['source'], 'USER')
1548             assert e['deferment_end_time'] > e['deletion_time']
1549
1550         RBD().trash_remove(ioctx, image_id1, True)
1551         RBD().trash_remove(ioctx, image_id2, True)
1552
1553     def test_restore(self):
1554         create_image()
1555         with Image(ioctx, image_name) as image:
1556             image_id = image.id()
1557         RBD().trash_move(ioctx, image_name, 1000)
1558         RBD().trash_restore(ioctx, image_id, image_name)
1559         remove_image()