def setUp(self): global ioctx global features self.rbd = RBD() create_image() self.image = Image(ioctx, IMG_NAME) data = rand_data(256) self.image.write(data, IMG_SIZE / 2) self.image.create_snap('snap1') global features self.image.protect_snap('snap1') self.rbd.clone(ioctx, IMG_NAME, 'snap1', ioctx, 'clone', features) self.clone = Image(ioctx, 'clone')
def test_rename(): rbd = RBD() image_name2 = get_temp_image_name() rbd.rename(ioctx, image_name, image_name2) eq([image_name2], rbd.list(ioctx)) rbd.rename(ioctx, image_name2, image_name) eq([image_name], rbd.list(ioctx))
def rbd_list(dbg, cluster, pool): log.debug("%s: xcpng.librbd.rbd_utils.get_rbd_list: %s" % (dbg, pool)) ioctx = cluster.open_ioctx(pool) rbd_inst = RBD() try: rbds = rbd_inst.list(ioctx) return rbds except Exception as e: log.error( "%s: xcpng.librbd.rbd_utils.get_rbd_list: Failed to get list of rbds for pool: %s " % (dbg, pool)) raise Exception(e) finally: ioctx.close()
def rbd_remove(dbg, cluster, pool, name): log.debug( "%s: xcpng.librbd.rbd_utils.rbd_remove: Cluster ID: %s Pool %s Name: %s" % (dbg, cluster.get_fsid(), pool, name)) ioctx = cluster.open_ioctx(pool) rbd_inst = RBD() try: rbd_inst.remove(ioctx, name) except Exception as e: log.error( "%s: xcpng.librbd.rbd_utils.rbd_remove: Failed to remove an image: Cluster ID: %s Pool %s Name: %s" % (dbg, cluster.get_fsid(), pool, name)) raise Exception(e) finally: ioctx.close()
def rbd_create(dbg, cluster, pool, name, size): log.debug( "%s: xcpng.librbd.rbd_utils.rbd_create: Cluster ID: %s Pool: %s Name: %s Size: %s" % (dbg, cluster.get_fsid(), pool, name, size)) ioctx = cluster.open_ioctx(pool) rbd_inst = RBD() try: rbd_inst.create(ioctx, name, size, order=RBD_IMAGE_ORDER) except Exception as e: log.error( "%s: xcpng.librbd.rbd_utils.rbd_create: Failed to create an image: Cluster ID: %s Pool %s Name: %s Size: %s" % (dbg, cluster.get_fsid(), pool, name, size)) raise Exception(e) finally: ioctx.close()
def setUp(self): global ioctx global features self.rbd = RBD() create_image() self.image = Image(ioctx, image_name) data = rand_data(256) self.image.write(data, IMG_SIZE // 2) self.image.create_snap('snap1') global features self.image.protect_snap('snap1') self.clone_name = get_temp_image_name() self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name, features) self.clone = Image(ioctx, self.clone_name)
def refresh_rbd_stats_pools(self, pools): self.log.debug('refreshing rbd pools %s' % (pools)) counters_info = self.rbd_stats['counters_info'] for pool_name in pools: try: pool_id = self.rados.pool_lookup(pool_name) with self.rados.open_ioctx(pool_name) as ioctx: if pool_id not in self.rbd_stats['pools']: self.rbd_stats['pools'][pool_id] = {'images' : {}} pool = self.rbd_stats['pools'][pool_id] pool['name'] = pool_name images = {} for image_meta in RBD().list2(ioctx): image = {'n' : image_meta['name']} image_id = image_meta['id'] if image_id in pool['images']: image['c'] = pool['images'][image_id]['c'] else: image['c'] = [[0, 0] for x in counters_info] images[image_id] = image pool['images'] = images except Exception as e: self.log.error('failed listing pool %s: %s' % (pool_name, e)) self.rbd_stats['pools_refresh_time'] = time.time()
def rbd_rename(dbg, cluster, pool, old_name, new_name): log.debug( "%s: xcpng.librbd.rbd_utils.rbd_rename: Cluster ID: %s Pool: %s Old name: %s New name: %s" % (dbg, cluster.get_fsid(), pool, old_name, new_name)) ioctx = cluster.open_ioctx(pool) rbd_inst = RBD() try: rbd_inst.rename(ioctx, old_name, new_name) except Exception as e: log.error( "%s: xcpng.librbd.rbd_utils.rbd_utilisation: Failed to get an image utilization: Cluster ID: %s Pool %s Old Name: %s New Name: %s" % (dbg, cluster.get_fsid(), pool, old_name, new_name)) raise Exception(e) finally: ioctx.close()
def refresh_rbd_stats_pools(self, pools): self.log.debug('refreshing rbd pools %s' % (pools)) rbd = RBD() counters_info = self.rbd_stats['counters_info'] for pool_name, cfg_ns_names in pools.items(): try: pool_id = self.rados.pool_lookup(pool_name) with self.rados.open_ioctx(pool_name) as ioctx: if pool_id not in self.rbd_stats['pools']: self.rbd_stats['pools'][pool_id] = {'images': {}} pool = self.rbd_stats['pools'][pool_id] pool['name'] = pool_name pool['ns_names'] = cfg_ns_names if cfg_ns_names: nspace_names = list(cfg_ns_names) else: nspace_names = [''] + rbd.namespace_list(ioctx) for nspace_name in pool['images']: if nspace_name not in nspace_names: del pool['images'][nspace_name] for nspace_name in nspace_names: if (nspace_name and not rbd.namespace_exists(ioctx, nspace_name)): self.log.debug('unknown namespace %s for pool %s' % (nspace_name, pool_name)) continue ioctx.set_namespace(nspace_name) if nspace_name not in pool['images']: pool['images'][nspace_name] = {} namespace = pool['images'][nspace_name] images = {} for image_meta in RBD().list2(ioctx): image = {'n': image_meta['name']} image_id = image_meta['id'] if image_id in namespace: image['c'] = namespace[image_id]['c'] else: image['c'] = [[0, 0] for x in counters_info] images[image_id] = image pool['images'][nspace_name] = images except Exception as e: self.log.error('failed listing pool %s: %s' % (pool_name, e)) self.rbd_stats['pools_refresh_time'] = time.time()
def master(ioctx): print("starting master") safe_delete_image(ioctx, CLONE_IMG_RENAME) safe_delete_image(ioctx, CLONE_IMG_NAME) safe_delete_image(ioctx, PARENT_IMG_NAME) features = get_features() RBD().create(ioctx, PARENT_IMG_NAME, IMG_SIZE, IMG_ORDER, old_format=False, features=features) with Image(ioctx, PARENT_IMG_NAME) as image: image.create_snap('snap1') image.protect_snap('snap1') features = features & ~(RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF) RBD().clone(ioctx, PARENT_IMG_NAME, 'snap1', ioctx, CLONE_IMG_NAME, features=features) with Image(ioctx, CLONE_IMG_NAME) as image: print("acquiring exclusive lock") offset = 0 data = os.urandom(512) while offset < IMG_SIZE: image.write(data, offset) offset += (1 << IMG_ORDER) image.write(b'1', IMG_SIZE - 1) assert (image.is_exclusive_lock_owner()) print("waiting for slave to complete") while image.is_exclusive_lock_owner(): time.sleep(5) safe_delete_image(ioctx, CLONE_IMG_RENAME) safe_delete_image(ioctx, CLONE_IMG_NAME) delete_image(ioctx, PARENT_IMG_NAME) print("finished")
def delete_image(ioctx, img_name): image = Image(ioctx, img_name) for snap in image.list_snaps(): snap_name = snap['name'] print("removing snapshot: %s@%s" % (img_name, snap_name)) if image.is_protected_snap(snap_name): image.unprotect_snap(snap_name) image.remove_snap(snap_name) image.close() print("removing image: %s" % img_name) RBD().remove(ioctx, img_name)
def slave(ioctx): print("starting slave") while True: try: with Image(ioctx, CLONE_IMG_NAME) as image: if image.list_lockers() != []: break except Exception: pass with Image(ioctx, CLONE_IMG_NAME) as image: print("detected master") print("flatten") image.flatten() assert (not image.is_exclusive_lock_owner()) print("resize") image.resize(IMG_SIZE / 2) assert (not image.is_exclusive_lock_owner()) assert (image.stat()['size'] == IMG_SIZE / 2) print("create_snap") image.create_snap('snap1') assert (not image.is_exclusive_lock_owner()) assert ('snap1' in map(lambda snap: snap['name'], image.list_snaps())) print("protect_snap") image.protect_snap('snap1') assert (not image.is_exclusive_lock_owner()) assert (image.is_protected_snap()) print("unprotect_snap") image.unprotect_snap('snap1') assert (not image.is_exclusive_lock_owner()) assert (not image.is_protected_snap()) print("remove_snap") image.remove_snap('snap1') assert (not image.is_exclusive_lock_owner()) assert (list(image.list_snaps()) == []) print("write") data = os.urandom(512) image.write(data, 0) assert (image.is_exclusive_lock_owner()) print("rename") RBD().rename(ioctx, CLONE_IMG_NAME, CLONE_IMG_RENAME) print("finished")
def setUp(self): global ioctx global features self.rbd = RBD() create_image() self.image = Image(ioctx, IMG_NAME) data = rand_data(256) self.image.write(data, IMG_SIZE / 2) self.image.create_snap("snap1") global features self.image.protect_snap("snap1") self.rbd.clone(ioctx, IMG_NAME, "snap1", ioctx, "clone", features) self.clone = Image(ioctx, "clone")
def test_open_read_only(): with Rados(conffile='') as cluster: with cluster.open_ioctx('rbd') as ioctx: RBD().create(ioctx, IMG_NAME, IMG_SIZE) data = rand_data(256) with Image(ioctx, IMG_NAME) as image: image.write(data, 0) image.create_snap('snap') with Image(ioctx, IMG_NAME, read_only=True) as image: read = image.read(0, 256) eq(data, read) assert_raises(ReadOnlyImage, image.write, data, 0) assert_raises(ReadOnlyImage, image.create_snap, 'test') assert_raises(ReadOnlyImage, image.remove_snap, 'snap') assert_raises(ReadOnlyImage, image.rollback_to_snap, 'snap') assert_raises(ReadOnlyImage, image.protect_snap, 'snap') assert_raises(ReadOnlyImage, image.unprotect_snap, 'snap') assert_raises(ReadOnlyImage, image.unprotect_snap, 'snap') assert_raises(ReadOnlyImage, image.flatten) with Image(ioctx, IMG_NAME) as image: image.remove_snap('snap') RBD().remove(ioctx, IMG_NAME) eq(data, read)
def rbd_clone(dbg, cluster, parent_pool, parent, snapshot, clone_pool, clone): log.debug( "%s: xcpng.librbd.rbd_utils.rbd_clone: Cluster ID: %s Parent Pool: %s Parent: %s Snapshot: %s Clone Pool: %s Clone: %s" % (dbg, cluster.get_fsid(), parent_pool, parent, snapshot, clone_pool, clone)) p_ioctx = cluster.open_ioctx(parent_pool) p_image = Image(p_ioctx, parent) c_ioctx = cluster.open_ioctx(clone_pool) rbd_inst = RBD() try: if not p_image.is_protected_snap(snapshot): p_image.protect_snap(snapshot) rbd_inst.clone(p_ioctx, parent, snapshot, c_ioctx, clone) except Exception as e: log.error( "%s: xcpng.librbd.rbd_utils.rbd_clone: Failed to make a clone: Cluster ID: %s Parent Pool: %s Parent: %s Snapshot: %s Clone Pool: %s Clone: %s" % (dbg, cluster.get_fsid(), parent_pool, parent, snapshot, clone_pool, clone)) raise Exception(e) finally: p_ioctx.close() c_ioctx.close()
def setUp(self): global ioctx global features self.rbd = RBD() create_image() self.image = Image(ioctx, image_name) data = rand_data(256) self.image.write(data, IMG_SIZE // 2) self.image.create_snap("snap1") global features self.image.protect_snap("snap1") self.clone_name = get_temp_image_name() self.rbd.clone(ioctx, image_name, "snap1", ioctx, self.clone_name, features) self.clone = Image(ioctx, self.clone_name)
def test_follower_flatten(self): with Image(ioctx, image_name) as image: image.create_snap('snap') image.protect_snap('snap') try: RBD().clone(ioctx, image_name, 'snap', ioctx, 'clone', features) with Image(ioctx, 'clone') as image1, Image(ioctx2, 'clone') as image2: data = rand_data(256) image1.write(data, 0) image2.flatten() assert_raises(ImageNotFound, image1.parent_info) parent = True for x in range(30): try: image2.parent_info() except ImageNotFound: parent = False break eq(False, parent) finally: RBD().remove(ioctx, 'clone') with Image(ioctx, image_name) as image: image.unprotect_snap('snap') image.remove_snap('snap')
def test_create_with_params(self): global features image_name = get_temp_image_name() order = 20 stripe_unit = 1 << 20 stripe_count = 10 self.rbd.create(ioctx, image_name, IMG_SIZE, order, False, features, stripe_unit, stripe_count) image = Image(ioctx, image_name) info = image.stat() check_stat(info, IMG_SIZE, order) eq(image.features(), features) eq(image.stripe_unit(), stripe_unit) eq(image.stripe_count(), stripe_count) image.close() RBD().remove(ioctx, image_name)
class TestClone(object): @require_features([RBD_FEATURE_LAYERING]) def setUp(self): global ioctx global features self.rbd = RBD() create_image() self.image = Image(ioctx, image_name) data = rand_data(256) self.image.write(data, IMG_SIZE / 2) self.image.create_snap('snap1') global features self.image.protect_snap('snap1') self.clone_name = get_temp_image_name() self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name, features) self.clone = Image(ioctx, self.clone_name) def tearDown(self): global ioctx self.clone.close() self.rbd.remove(ioctx, self.clone_name) self.image.unprotect_snap('snap1') self.image.remove_snap('snap1') self.image.close() remove_image() def test_unprotected(self): self.image.create_snap('snap2') global features clone_name2 = get_temp_image_name() assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name, 'snap2', ioctx, clone_name2, features) self.image.remove_snap('snap2') def test_unprotect_with_children(self): global features # can't remove a snapshot that has dependent clones assert_raises(ImageBusy, self.image.remove_snap, 'snap1') # validate parent info of clone created by TestClone.setUp (pool, image, snap) = self.clone.parent_info() eq(pool, pool_name) eq(image, image_name) eq(snap, 'snap1') # create a new pool... pool_name2 = get_temp_pool_name() rados.create_pool(pool_name2) other_ioctx = rados.open_ioctx(pool_name2) # ...with a clone of the same parent other_clone_name = get_temp_image_name() self.rbd.clone(ioctx, image_name, 'snap1', other_ioctx, other_clone_name, features) self.other_clone = Image(other_ioctx, other_clone_name) # validate its parent info (pool, image, snap) = self.other_clone.parent_info() eq(pool, pool_name) eq(image, image_name) eq(snap, 'snap1') # can't unprotect snap with children assert_raises(ImageBusy, self.image.unprotect_snap, 'snap1') # 2 children, check that cannot remove the parent snap assert_raises(ImageBusy, self.image.remove_snap, 'snap1') # close and remove other pool's clone self.other_clone.close() self.rbd.remove(other_ioctx, other_clone_name) # check that we cannot yet remove the parent snap assert_raises(ImageBusy, self.image.remove_snap, 'snap1') other_ioctx.close() rados.delete_pool(pool_name2) # unprotect, remove parent snap happen in cleanup, and should succeed def test_stat(self): image_info = self.image.stat() clone_info = self.clone.stat() eq(clone_info['size'], image_info['size']) eq(clone_info['size'], self.clone.overlap()) def test_resize_stat(self): self.clone.resize(IMG_SIZE / 2) image_info = self.image.stat() clone_info = self.clone.stat() eq(clone_info['size'], IMG_SIZE / 2) eq(image_info['size'], IMG_SIZE) eq(self.clone.overlap(), IMG_SIZE / 2) self.clone.resize(IMG_SIZE * 2) image_info = self.image.stat() clone_info = self.clone.stat() eq(clone_info['size'], IMG_SIZE * 2) eq(image_info['size'], IMG_SIZE) eq(self.clone.overlap(), IMG_SIZE / 2) def test_resize_io(self): parent_data = self.image.read(IMG_SIZE / 2, 256) self.image.resize(0) self.clone.resize(IMG_SIZE / 2 + 128) child_data = self.clone.read(IMG_SIZE / 2, 128) eq(child_data, parent_data[:128]) self.clone.resize(IMG_SIZE) child_data = self.clone.read(IMG_SIZE / 2, 256) eq(child_data, parent_data[:128] + ('\0' * 128)) self.clone.resize(IMG_SIZE / 2 + 1) child_data = self.clone.read(IMG_SIZE / 2, 1) eq(child_data, parent_data[0]) self.clone.resize(0) self.clone.resize(IMG_SIZE) child_data = self.clone.read(IMG_SIZE / 2, 256) eq(child_data, '\0' * 256) def test_read(self): parent_data = self.image.read(IMG_SIZE / 2, 256) child_data = self.clone.read(IMG_SIZE / 2, 256) eq(child_data, parent_data) def test_write(self): parent_data = self.image.read(IMG_SIZE / 2, 256) new_data = rand_data(256) self.clone.write(new_data, IMG_SIZE / 2 + 256) child_data = self.clone.read(IMG_SIZE / 2 + 256, 256) eq(child_data, new_data) child_data = self.clone.read(IMG_SIZE / 2, 256) eq(child_data, parent_data) parent_data = self.image.read(IMG_SIZE / 2 + 256, 256) eq(parent_data, '\0' * 256) def check_children(self, expected): actual = self.image.list_children() # dedup for cache pools until # http://tracker.ceph.com/issues/8187 is fixed deduped = set([(pool_name, image[1]) for image in actual]) eq(deduped, set(expected)) def test_list_children(self): global ioctx global features self.image.set_snap('snap1') self.check_children([(pool_name, self.clone_name)]) self.clone.close() self.rbd.remove(ioctx, self.clone_name) eq(self.image.list_children(), []) clone_name = get_temp_image_name() + '_' expected_children = [] for i in xrange(10): self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name + str(i), features) expected_children.append((pool_name, clone_name + str(i))) self.check_children(expected_children) for i in xrange(10): self.rbd.remove(ioctx, clone_name + str(i)) expected_children.pop(0) self.check_children(expected_children) eq(self.image.list_children(), []) self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name, features) self.check_children([(pool_name, self.clone_name)]) self.clone = Image(ioctx, self.clone_name) def test_flatten_errors(self): # test that we can't flatten a non-clone assert_raises(InvalidArgument, self.image.flatten) # test that we can't flatten a snapshot self.clone.create_snap('snap2') self.clone.set_snap('snap2') assert_raises(ReadOnlyImage, self.clone.flatten) self.clone.remove_snap('snap2') def check_flatten_with_order(self, new_order): global ioctx global features clone_name2 = get_temp_image_name() self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2, features, new_order) #with Image(ioctx, 'clone2') as clone: clone2 = Image(ioctx, clone_name2) clone2.flatten() eq(clone2.overlap(), 0) clone2.close() self.rbd.remove(ioctx, clone_name2) # flatten after resizing to non-block size self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2, features, new_order) with Image(ioctx, clone_name2) as clone: clone.resize(IMG_SIZE / 2 - 1) clone.flatten() eq(0, clone.overlap()) self.rbd.remove(ioctx, clone_name2) # flatten after resizing to non-block size self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2, features, new_order) with Image(ioctx, clone_name2) as clone: clone.resize(IMG_SIZE / 2 + 1) clone.flatten() eq(clone.overlap(), 0) self.rbd.remove(ioctx, clone_name2) def test_flatten_basic(self): self.check_flatten_with_order(IMG_ORDER) def test_flatten_smaller_order(self): self.check_flatten_with_order(IMG_ORDER - 2) def test_flatten_larger_order(self): self.check_flatten_with_order(IMG_ORDER + 2) def test_flatten_drops_cache(self): global ioctx global features clone_name2 = get_temp_image_name() self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2, features, IMG_ORDER) with Image(ioctx, clone_name2) as clone: with Image(ioctx, clone_name2) as clone2: # cache object non-existence data = clone.read(IMG_SIZE / 2, 256) clone2_data = clone2.read(IMG_SIZE / 2, 256) eq(data, clone2_data) clone.flatten() assert_raises(ImageNotFound, clone.parent_info) assert_raises(ImageNotFound, clone2.parent_info) after_flatten = clone.read(IMG_SIZE / 2, 256) eq(data, after_flatten) after_flatten = clone2.read(IMG_SIZE / 2, 256) eq(data, after_flatten) self.rbd.remove(ioctx, clone_name2) def test_flatten_multi_level(self): self.clone.create_snap('snap2') self.clone.protect_snap('snap2') clone_name3 = get_temp_image_name() self.rbd.clone(ioctx, self.clone_name, 'snap2', ioctx, clone_name3, features) self.clone.flatten() with Image(ioctx, clone_name3) as clone3: clone3.flatten() self.clone.unprotect_snap('snap2') self.clone.remove_snap('snap2') self.rbd.remove(ioctx, clone_name3) def test_resize_flatten_multi_level(self): self.clone.create_snap('snap2') self.clone.protect_snap('snap2') clone_name3 = get_temp_image_name() self.rbd.clone(ioctx, self.clone_name, 'snap2', ioctx, clone_name3, features) self.clone.resize(1) orig_data = self.image.read(0, 256) with Image(ioctx, clone_name3) as clone3: clone3_data = clone3.read(0, 256) eq(orig_data, clone3_data) self.clone.flatten() with Image(ioctx, clone_name3) as clone3: clone3_data = clone3.read(0, 256) eq(orig_data, clone3_data) self.rbd.remove(ioctx, clone_name3) self.clone.unprotect_snap('snap2') self.clone.remove_snap('snap2')
class TestImage(object): def setUp(self): self.rbd = RBD() create_image() self.image = Image(ioctx, IMG_NAME) def tearDown(self): self.image.close() remove_image() def test_stat(self): info = self.image.stat() check_stat(info, IMG_SIZE, IMG_ORDER) def test_write(self): data = rand_data(256) self.image.write(data, 0) def test_read(self): data = self.image.read(0, 20) eq(data, '\0' * 20) def test_large_write(self): data = rand_data(IMG_SIZE) self.image.write(data, 0) def test_large_read(self): data = self.image.read(0, IMG_SIZE) eq(data, '\0' * IMG_SIZE) def test_write_read(self): data = rand_data(256) offset = 50 self.image.write(data, offset) read = self.image.read(offset, 256) eq(data, read) def test_read_bad_offset(self): assert_raises(InvalidArgument, self.image.read, IMG_SIZE + 1, IMG_SIZE) def test_resize(self): new_size = IMG_SIZE * 2 self.image.resize(new_size) info = self.image.stat() check_stat(info, new_size, IMG_ORDER) def test_resize_down(self): new_size = IMG_SIZE / 2 data = rand_data(256) self.image.write(data, IMG_SIZE / 2); self.image.resize(new_size) self.image.resize(IMG_SIZE) read = self.image.read(IMG_SIZE / 2, 256) eq('\0' * 256, read) def test_resize_bytes(self): new_size = IMG_SIZE / 2 - 5 data = rand_data(256) self.image.write(data, IMG_SIZE / 2 - 10); self.image.resize(new_size) self.image.resize(IMG_SIZE) read = self.image.read(IMG_SIZE / 2 - 10, 5) eq(data[:5], read) read = self.image.read(IMG_SIZE / 2 - 5, 251) eq('\0' * 251, read) def test_copy(self): global ioctx data = rand_data(256) self.image.write(data, 256) self.image.copy(ioctx, IMG_NAME + '2') assert_raises(ImageExists, self.image.copy, ioctx, IMG_NAME + '2') copy = Image(ioctx, IMG_NAME + '2') copy_data = copy.read(256, 256) copy.close() self.rbd.remove(ioctx, IMG_NAME + '2') eq(data, copy_data) def test_create_snap(self): global ioctx self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) at_snapshot = Image(ioctx, IMG_NAME, 'snap1') snap_data = at_snapshot.read(0, 256) at_snapshot.close() eq(snap_data, '\0' * 256) self.image.remove_snap('snap1') def test_list_snaps(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.create_snap('snap2') eq(['snap1', 'snap2'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_remove_snap(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.remove_snap('snap1') eq([], list(self.image.list_snaps())) def test_remove_with_snap(self): self.image.create_snap('snap1') assert_raises(ImageHasSnapshots, remove_image) self.image.remove_snap('snap1') def test_remove_with_watcher(self): assert_raises(ImageBusy, remove_image) def test_rollback_to_snap(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_rollback_to_snap_sparse(self): self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_rollback_with_resize(self): read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, data) new_size = IMG_SIZE * 2 self.image.resize(new_size) check_stat(self.image.stat(), new_size, IMG_ORDER) self.image.write(data, new_size - 256) self.image.create_snap('snap2') read = self.image.read(new_size - 256, 256) eq(read, data) self.image.rollback_to_snap('snap1') check_stat(self.image.stat(), IMG_SIZE, IMG_ORDER) assert_raises(InvalidArgument, self.image.read, new_size - 256, 256) self.image.rollback_to_snap('snap2') check_stat(self.image.stat(), new_size, IMG_ORDER) read = self.image.read(new_size - 256, 256) eq(read, data) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_set_snap(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_set_no_snap(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) self.image.remove_snap('snap1') def test_set_snap_sparse(self): self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_many_snaps(self): num_snaps = 200 for i in xrange(num_snaps): self.image.create_snap(str(i)) snaps = sorted(self.image.list_snaps(), key=lambda snap: int(snap['name'])) eq(len(snaps), num_snaps) for i, snap in enumerate(snaps): eq(snap['size'], IMG_SIZE) eq(snap['name'], str(i)) for i in xrange(num_snaps): self.image.remove_snap(str(i)) def test_set_snap_deleted(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') self.image.remove_snap('snap1') assert_raises(ImageNotFound, self.image.read, 0, 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) def test_set_snap_recreated(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') self.image.remove_snap('snap1') self.image.create_snap('snap1') assert_raises(ImageNotFound, self.image.read, 0, 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) self.image.remove_snap('snap1') def test_clone(self): if features is None or (features & RBD_FEATURE_LAYERING) == 0: return 0 self.image.create_snap('snap1') RBD().clone(ioctx, IMG_NAME, 'snap1', ioctx, 'clone', features) with Image(ioctx, 'clone') as clone: image_info = self.image.stat() clone_info = clone.stat() eq(clone_info['size'], image_info['size']) eq(clone_info['size'], clone.overlap()) RBD().remove(ioctx, 'clone') self.image.remove_snap('snap1') def test_clone_resize(self): if features is None or (features & RBD_FEATURE_LAYERING) == 0: return 0 self.image.create_snap('snap1') RBD().clone(ioctx, IMG_NAME, 'snap1', ioctx, 'clone', features) with Image(ioctx, 'clone') as clone: image_info = self.image.stat() clone_info = clone.stat() eq(clone_info['size'], image_info['size']) eq(clone_info['size'], clone.overlap()) clone.resize(IMG_SIZE / 2) image_info = self.image.stat() clone_info = clone.stat() eq(clone_info['size'], IMG_SIZE / 2) eq(image_info['size'], IMG_SIZE) eq(clone.overlap(), IMG_SIZE / 2) clone.resize(IMG_SIZE * 2) image_info = self.image.stat() clone_info = clone.stat() eq(clone_info['size'], IMG_SIZE * 2) eq(image_info['size'], IMG_SIZE) eq(clone.overlap(), IMG_SIZE / 2) RBD().remove(ioctx, 'clone') self.image.remove_snap('snap1')
class TestImage(object): def setUp(self): self.rbd = RBD() create_image() self.image = Image(ioctx, IMG_NAME) def tearDown(self): self.image.close() remove_image() def test_stat(self): info = self.image.stat() check_stat(info, IMG_SIZE, IMG_ORDER) def test_write(self): data = rand_data(256) self.image.write(data, 0) def test_read(self): data = self.image.read(0, 20) eq(data, "\0" * 20) def test_large_write(self): data = rand_data(IMG_SIZE) self.image.write(data, 0) def test_large_read(self): data = self.image.read(0, IMG_SIZE) eq(data, "\0" * IMG_SIZE) def test_write_read(self): data = rand_data(256) offset = 50 self.image.write(data, offset) read = self.image.read(offset, 256) eq(data, read) def test_read_bad_offset(self): assert_raises(InvalidArgument, self.image.read, IMG_SIZE + 1, IMG_SIZE) def test_resize(self): new_size = IMG_SIZE * 2 self.image.resize(new_size) info = self.image.stat() check_stat(info, new_size, IMG_ORDER) def test_size(self): eq(IMG_SIZE, self.image.size()) self.image.create_snap("snap1") new_size = IMG_SIZE * 2 self.image.resize(new_size) eq(new_size, self.image.size()) self.image.create_snap("snap2") self.image.set_snap("snap2") eq(new_size, self.image.size()) self.image.set_snap("snap1") eq(IMG_SIZE, self.image.size()) self.image.set_snap(None) eq(new_size, self.image.size()) self.image.remove_snap("snap1") self.image.remove_snap("snap2") def test_resize_down(self): new_size = IMG_SIZE / 2 data = rand_data(256) self.image.write(data, IMG_SIZE / 2) self.image.resize(new_size) self.image.resize(IMG_SIZE) read = self.image.read(IMG_SIZE / 2, 256) eq("\0" * 256, read) def test_resize_bytes(self): new_size = IMG_SIZE / 2 - 5 data = rand_data(256) self.image.write(data, IMG_SIZE / 2 - 10) self.image.resize(new_size) self.image.resize(IMG_SIZE) read = self.image.read(IMG_SIZE / 2 - 10, 5) eq(data[:5], read) read = self.image.read(IMG_SIZE / 2 - 5, 251) eq("\0" * 251, read) def test_copy(self): global ioctx data = rand_data(256) self.image.write(data, 256) self.image.copy(ioctx, IMG_NAME + "2") assert_raises(ImageExists, self.image.copy, ioctx, IMG_NAME + "2") copy = Image(ioctx, IMG_NAME + "2") copy_data = copy.read(256, 256) copy.close() self.rbd.remove(ioctx, IMG_NAME + "2") eq(data, copy_data) def test_create_snap(self): global ioctx self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, "\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) at_snapshot = Image(ioctx, IMG_NAME, "snap1") snap_data = at_snapshot.read(0, 256) at_snapshot.close() eq(snap_data, "\0" * 256) self.image.remove_snap("snap1") def test_list_snaps(self): eq([], list(self.image.list_snaps())) self.image.create_snap("snap1") eq(["snap1"], map(lambda snap: snap["name"], self.image.list_snaps())) self.image.create_snap("snap2") eq(["snap1", "snap2"], map(lambda snap: snap["name"], self.image.list_snaps())) self.image.remove_snap("snap1") self.image.remove_snap("snap2") def test_remove_snap(self): eq([], list(self.image.list_snaps())) self.image.create_snap("snap1") eq(["snap1"], map(lambda snap: snap["name"], self.image.list_snaps())) self.image.remove_snap("snap1") eq([], list(self.image.list_snaps())) @require_features([RBD_FEATURE_LAYERING]) def test_protect_snap(self): self.image.create_snap("snap1") assert not self.image.is_protected_snap("snap1") self.image.protect_snap("snap1") assert self.image.is_protected_snap("snap1") assert_raises(ImageBusy, self.image.remove_snap, "snap1") self.image.unprotect_snap("snap1") assert not self.image.is_protected_snap("snap1") self.image.remove_snap("snap1") assert_raises(ImageNotFound, self.image.unprotect_snap, "snap1") assert_raises(ImageNotFound, self.image.is_protected_snap, "snap1") def test_remove_with_snap(self): self.image.create_snap("snap1") assert_raises(ImageHasSnapshots, remove_image) self.image.remove_snap("snap1") def test_remove_with_watcher(self): data = rand_data(256) self.image.write(data, 0) assert_raises(ImageBusy, remove_image) read = self.image.read(0, 256) eq(read, data) def test_rollback_to_snap(self): self.image.write("\0" * 256, 0) self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, "\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap("snap1") read = self.image.read(0, 256) eq(read, "\0" * 256) self.image.remove_snap("snap1") def test_rollback_to_snap_sparse(self): self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, "\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap("snap1") read = self.image.read(0, 256) eq(read, "\0" * 256) self.image.remove_snap("snap1") def test_rollback_with_resize(self): read = self.image.read(0, 256) eq(read, "\0" * 256) data = rand_data(256) self.image.write(data, 0) self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, data) new_size = IMG_SIZE * 2 self.image.resize(new_size) check_stat(self.image.stat(), new_size, IMG_ORDER) self.image.write(data, new_size - 256) self.image.create_snap("snap2") read = self.image.read(new_size - 256, 256) eq(read, data) self.image.rollback_to_snap("snap1") check_stat(self.image.stat(), IMG_SIZE, IMG_ORDER) assert_raises(InvalidArgument, self.image.read, new_size - 256, 256) self.image.rollback_to_snap("snap2") check_stat(self.image.stat(), new_size, IMG_ORDER) read = self.image.read(new_size - 256, 256) eq(read, data) self.image.remove_snap("snap1") self.image.remove_snap("snap2") def test_set_snap(self): self.image.write("\0" * 256, 0) self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, "\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap("snap1") read = self.image.read(0, 256) eq(read, "\0" * 256) self.image.remove_snap("snap1") def test_set_no_snap(self): self.image.write("\0" * 256, 0) self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, "\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap("snap1") read = self.image.read(0, 256) eq(read, "\0" * 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) self.image.remove_snap("snap1") def test_set_snap_sparse(self): self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, "\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap("snap1") read = self.image.read(0, 256) eq(read, "\0" * 256) self.image.remove_snap("snap1") def test_many_snaps(self): num_snaps = 200 for i in xrange(num_snaps): self.image.create_snap(str(i)) snaps = sorted(self.image.list_snaps(), key=lambda snap: int(snap["name"])) eq(len(snaps), num_snaps) for i, snap in enumerate(snaps): eq(snap["size"], IMG_SIZE) eq(snap["name"], str(i)) for i in xrange(num_snaps): self.image.remove_snap(str(i)) def test_set_snap_deleted(self): self.image.write("\0" * 256, 0) self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, "\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap("snap1") self.image.remove_snap("snap1") assert_raises(ImageNotFound, self.image.read, 0, 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) def test_set_snap_recreated(self): self.image.write("\0" * 256, 0) self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, "\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap("snap1") self.image.remove_snap("snap1") self.image.create_snap("snap1") assert_raises(ImageNotFound, self.image.read, 0, 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) self.image.remove_snap("snap1") def test_lock_unlock(self): assert_raises(ImageNotFound, self.image.unlock, "") self.image.lock_exclusive("") assert_raises(ImageExists, self.image.lock_exclusive, "") assert_raises(ImageBusy, self.image.lock_exclusive, "test") assert_raises(ImageExists, self.image.lock_shared, "", "") assert_raises(ImageBusy, self.image.lock_shared, "foo", "") self.image.unlock("") def test_list_lockers(self): eq([], self.image.list_lockers()) self.image.lock_exclusive("test") lockers = self.image.list_lockers() eq(1, len(lockers["lockers"])) _, cookie, _ = lockers["lockers"][0] eq(cookie, "test") eq("", lockers["tag"]) assert lockers["exclusive"] self.image.unlock("test") eq([], self.image.list_lockers()) num_shared = 10 for i in xrange(num_shared): self.image.lock_shared(str(i), "tag") lockers = self.image.list_lockers() eq("tag", lockers["tag"]) assert not lockers["exclusive"] eq(num_shared, len(lockers["lockers"])) cookies = sorted(map(lambda x: x[1], lockers["lockers"])) for i in xrange(num_shared): eq(str(i), cookies[i]) self.image.unlock(str(i)) eq([], self.image.list_lockers()) def test_diff_iterate(self): check_diff(self.image, 0, IMG_SIZE, None, []) self.image.write("a" * 256, 0) check_diff(self.image, 0, IMG_SIZE, None, [(0, 256, True)]) self.image.write("b" * 256, 256) check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)]) self.image.discard(128, 256) check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)]) self.image.create_snap("snap1") self.image.discard(0, 1 << IMG_ORDER) self.image.create_snap("snap2") self.image.set_snap("snap2") check_diff(self.image, 0, IMG_SIZE, "snap1", [(0, 512, False)]) self.image.remove_snap("snap1") self.image.remove_snap("snap2")
def test_list(): eq([IMG_NAME], RBD().list(ioctx))
class TestClone(object): @require_features([RBD_FEATURE_LAYERING]) def setUp(self): global ioctx global features self.rbd = RBD() create_image() self.image = Image(ioctx, image_name) data = rand_data(256) self.image.write(data, IMG_SIZE // 2) self.image.create_snap('snap1') global features self.image.protect_snap('snap1') self.clone_name = get_temp_image_name() self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name, features) self.clone = Image(ioctx, self.clone_name) def tearDown(self): global ioctx self.clone.close() self.rbd.remove(ioctx, self.clone_name) self.image.unprotect_snap('snap1') self.image.remove_snap('snap1') self.image.close() remove_image() @require_features([RBD_FEATURE_STRIPINGV2]) def test_with_params(self): global features self.image.create_snap('snap2') self.image.protect_snap('snap2') clone_name2 = get_temp_image_name() self.rbd.clone(ioctx, image_name, 'snap2', ioctx, clone_name2, features, self.image.stat()['order'], self.image.stripe_unit(), self.image.stripe_count()) self.rbd.remove(ioctx, clone_name2) self.image.unprotect_snap('snap2') self.image.remove_snap('snap2') def test_unprotected(self): self.image.create_snap('snap2') global features clone_name2 = get_temp_image_name() assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name, 'snap2', ioctx, clone_name2, features) self.image.remove_snap('snap2') def test_unprotect_with_children(self): global features # can't remove a snapshot that has dependent clones assert_raises(ImageBusy, self.image.remove_snap, 'snap1') # validate parent info of clone created by TestClone.setUp (pool, image, snap) = self.clone.parent_info() eq(pool, pool_name) eq(image, image_name) eq(snap, 'snap1') # create a new pool... pool_name2 = get_temp_pool_name() rados.create_pool(pool_name2) other_ioctx = rados.open_ioctx(pool_name2) # ...with a clone of the same parent other_clone_name = get_temp_image_name() self.rbd.clone(ioctx, image_name, 'snap1', other_ioctx, other_clone_name, features) self.other_clone = Image(other_ioctx, other_clone_name) # validate its parent info (pool, image, snap) = self.other_clone.parent_info() eq(pool, pool_name) eq(image, image_name) eq(snap, 'snap1') # can't unprotect snap with children assert_raises(ImageBusy, self.image.unprotect_snap, 'snap1') # 2 children, check that cannot remove the parent snap assert_raises(ImageBusy, self.image.remove_snap, 'snap1') # close and remove other pool's clone self.other_clone.close() self.rbd.remove(other_ioctx, other_clone_name) # check that we cannot yet remove the parent snap assert_raises(ImageBusy, self.image.remove_snap, 'snap1') other_ioctx.close() rados.delete_pool(pool_name2) # unprotect, remove parent snap happen in cleanup, and should succeed def test_stat(self): image_info = self.image.stat() clone_info = self.clone.stat() eq(clone_info['size'], image_info['size']) eq(clone_info['size'], self.clone.overlap()) def test_resize_stat(self): self.clone.resize(IMG_SIZE // 2) image_info = self.image.stat() clone_info = self.clone.stat() eq(clone_info['size'], IMG_SIZE // 2) eq(image_info['size'], IMG_SIZE) eq(self.clone.overlap(), IMG_SIZE // 2) self.clone.resize(IMG_SIZE * 2) image_info = self.image.stat() clone_info = self.clone.stat() eq(clone_info['size'], IMG_SIZE * 2) eq(image_info['size'], IMG_SIZE) eq(self.clone.overlap(), IMG_SIZE // 2) def test_resize_io(self): parent_data = self.image.read(IMG_SIZE // 2, 256) self.image.resize(0) self.clone.resize(IMG_SIZE // 2 + 128) child_data = self.clone.read(IMG_SIZE // 2, 128) eq(child_data, parent_data[:128]) self.clone.resize(IMG_SIZE) child_data = self.clone.read(IMG_SIZE // 2, 256) eq(child_data, parent_data[:128] + (b'\0' * 128)) self.clone.resize(IMG_SIZE // 2 + 1) child_data = self.clone.read(IMG_SIZE // 2, 1) eq(child_data, parent_data[0:1]) self.clone.resize(0) self.clone.resize(IMG_SIZE) child_data = self.clone.read(IMG_SIZE // 2, 256) eq(child_data, b'\0' * 256) def test_read(self): parent_data = self.image.read(IMG_SIZE // 2, 256) child_data = self.clone.read(IMG_SIZE // 2, 256) eq(child_data, parent_data) def test_write(self): parent_data = self.image.read(IMG_SIZE // 2, 256) new_data = rand_data(256) self.clone.write(new_data, IMG_SIZE // 2 + 256) child_data = self.clone.read(IMG_SIZE // 2 + 256, 256) eq(child_data, new_data) child_data = self.clone.read(IMG_SIZE // 2, 256) eq(child_data, parent_data) parent_data = self.image.read(IMG_SIZE // 2 + 256, 256) eq(parent_data, b'\0' * 256) def check_children(self, expected): actual = self.image.list_children() # dedup for cache pools until # http://tracker.ceph.com/issues/8187 is fixed deduped = set([(pool_name, image[1]) for image in actual]) eq(deduped, set(expected)) def test_list_children(self): global ioctx global features self.image.set_snap('snap1') self.check_children([(pool_name, self.clone_name)]) self.clone.close() self.rbd.remove(ioctx, self.clone_name) eq(self.image.list_children(), []) clone_name = get_temp_image_name() + '_' expected_children = [] for i in range(10): self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name + str(i), features) expected_children.append((pool_name, clone_name + str(i))) self.check_children(expected_children) for i in range(10): self.rbd.remove(ioctx, clone_name + str(i)) expected_children.pop(0) self.check_children(expected_children) eq(self.image.list_children(), []) self.rbd.clone(ioctx, image_name, 'snap1', ioctx, self.clone_name, features) self.check_children([(pool_name, self.clone_name)]) self.clone = Image(ioctx, self.clone_name) def test_flatten_errors(self): # test that we can't flatten a non-clone assert_raises(InvalidArgument, self.image.flatten) # test that we can't flatten a snapshot self.clone.create_snap('snap2') self.clone.set_snap('snap2') assert_raises(ReadOnlyImage, self.clone.flatten) self.clone.remove_snap('snap2') def check_flatten_with_order(self, new_order): global ioctx global features clone_name2 = get_temp_image_name() self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2, features, new_order) #with Image(ioctx, 'clone2') as clone: clone2 = Image(ioctx, clone_name2) clone2.flatten() eq(clone2.overlap(), 0) clone2.close() self.rbd.remove(ioctx, clone_name2) # flatten after resizing to non-block size self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2, features, new_order) with Image(ioctx, clone_name2) as clone: clone.resize(IMG_SIZE // 2 - 1) clone.flatten() eq(0, clone.overlap()) self.rbd.remove(ioctx, clone_name2) # flatten after resizing to non-block size self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2, features, new_order) with Image(ioctx, clone_name2) as clone: clone.resize(IMG_SIZE // 2 + 1) clone.flatten() eq(clone.overlap(), 0) self.rbd.remove(ioctx, clone_name2) def test_flatten_basic(self): self.check_flatten_with_order(IMG_ORDER) def test_flatten_smaller_order(self): self.check_flatten_with_order(IMG_ORDER - 2) def test_flatten_larger_order(self): self.check_flatten_with_order(IMG_ORDER + 2) def test_flatten_drops_cache(self): global ioctx global features clone_name2 = get_temp_image_name() self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name2, features, IMG_ORDER) with Image(ioctx, clone_name2) as clone: with Image(ioctx, clone_name2) as clone2: # cache object non-existence data = clone.read(IMG_SIZE // 2, 256) clone2_data = clone2.read(IMG_SIZE // 2, 256) eq(data, clone2_data) clone.flatten() assert_raises(ImageNotFound, clone.parent_info) assert_raises(ImageNotFound, clone2.parent_info) after_flatten = clone.read(IMG_SIZE // 2, 256) eq(data, after_flatten) after_flatten = clone2.read(IMG_SIZE // 2, 256) eq(data, after_flatten) self.rbd.remove(ioctx, clone_name2) def test_flatten_multi_level(self): self.clone.create_snap('snap2') self.clone.protect_snap('snap2') clone_name3 = get_temp_image_name() self.rbd.clone(ioctx, self.clone_name, 'snap2', ioctx, clone_name3, features) self.clone.flatten() with Image(ioctx, clone_name3) as clone3: clone3.flatten() self.clone.unprotect_snap('snap2') self.clone.remove_snap('snap2') self.rbd.remove(ioctx, clone_name3) def test_resize_flatten_multi_level(self): self.clone.create_snap('snap2') self.clone.protect_snap('snap2') clone_name3 = get_temp_image_name() self.rbd.clone(ioctx, self.clone_name, 'snap2', ioctx, clone_name3, features) self.clone.resize(1) orig_data = self.image.read(0, 256) with Image(ioctx, clone_name3) as clone3: clone3_data = clone3.read(0, 256) eq(orig_data, clone3_data) self.clone.flatten() with Image(ioctx, clone_name3) as clone3: clone3_data = clone3.read(0, 256) eq(orig_data, clone3_data) self.rbd.remove(ioctx, clone_name3) self.clone.unprotect_snap('snap2') self.clone.remove_snap('snap2')
def test_rename(): rbd = RBD() rbd.rename(ioctx, IMG_NAME, IMG_NAME + '2') eq([IMG_NAME + '2'], rbd.list(ioctx)) rbd.rename(ioctx, IMG_NAME + '2', IMG_NAME) eq([IMG_NAME], rbd.list(ioctx))
def test_list(): eq([image_name], RBD().list(ioctx))
class TestImage(object): def setUp(self): self.rbd = RBD() create_image() self.image = Image(ioctx, IMG_NAME) def tearDown(self): self.image.close() remove_image() def test_stat(self): info = self.image.stat() check_stat(info, IMG_SIZE, IMG_ORDER) def test_write(self): data = rand_data(256) self.image.write(data, 0) def test_read(self): data = self.image.read(0, 20) eq(data, '\0' * 20) def test_large_write(self): data = rand_data(IMG_SIZE) self.image.write(data, 0) def test_large_read(self): data = self.image.read(0, IMG_SIZE) eq(data, '\0' * IMG_SIZE) def test_write_read(self): data = rand_data(256) offset = 50 self.image.write(data, offset) read = self.image.read(offset, 256) eq(data, read) def test_read_bad_offset(self): assert_raises(InvalidArgument, self.image.read, IMG_SIZE + 1, IMG_SIZE) def test_resize(self): new_size = IMG_SIZE * 2 self.image.resize(new_size) info = self.image.stat() check_stat(info, new_size, IMG_ORDER) def test_copy(self): global ioctx data = rand_data(256) self.image.write(data, 256) self.image.copy(ioctx, IMG_NAME + '2') assert_raises(ImageExists, self.image.copy, ioctx, IMG_NAME + '2') copy = Image(ioctx, IMG_NAME + '2') copy_data = copy.read(256, 256) copy.close() self.rbd.remove(ioctx, IMG_NAME + '2') eq(data, copy_data) def test_create_snap(self): global ioctx self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) at_snapshot = Image(ioctx, IMG_NAME, 'snap1') snap_data = at_snapshot.read(0, 256) at_snapshot.close() eq(snap_data, '\0' * 256) self.image.remove_snap('snap1') def test_list_snaps(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.create_snap('snap2') eq(['snap1', 'snap2'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_remove_snap(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.remove_snap('snap1') eq([], list(self.image.list_snaps())) def test_remove_with_snap(self): self.image.create_snap('snap1') assert_raises(ImageBusy, remove_image) self.image.remove_snap('snap1') def test_rollback_to_snap(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_rollback_to_snap_sparse(self): self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_rollback_with_resize(self): read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, data) new_size = IMG_SIZE * 2 self.image.resize(new_size) check_stat(self.image.stat(), new_size, IMG_ORDER) self.image.write(data, new_size - 256) self.image.create_snap('snap2') read = self.image.read(new_size - 256, 256) eq(read, data) self.image.rollback_to_snap('snap1') check_stat(self.image.stat(), IMG_SIZE, IMG_ORDER) assert_raises(InvalidArgument, self.image.read, new_size - 256, 256) self.image.rollback_to_snap('snap2') check_stat(self.image.stat(), new_size, IMG_ORDER) read = self.image.read(new_size - 256, 256) eq(read, data) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_set_snap(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_set_no_snap(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) self.image.remove_snap('snap1') def test_set_snap_sparse(self): self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_many_snaps(self): num_snaps = 200 for i in xrange(num_snaps): self.image.create_snap(str(i)) snaps = sorted(self.image.list_snaps(), key=lambda snap: int(snap['name'])) eq(len(snaps), num_snaps) for i, snap in enumerate(snaps): eq(snap['size'], IMG_SIZE) eq(snap['name'], str(i)) for i in xrange(num_snaps): self.image.remove_snap(str(i))
def setUp(self): self.rbd = RBD() create_image() self.image = Image(ioctx, IMG_NAME)
def test_version(): RBD().version()
class TestClone(object): @require_features([RBD_FEATURE_LAYERING]) def setUp(self): global ioctx global features self.rbd = RBD() create_image() self.image = Image(ioctx, IMG_NAME) data = rand_data(256) self.image.write(data, IMG_SIZE / 2) self.image.create_snap("snap1") global features self.image.protect_snap("snap1") self.rbd.clone(ioctx, IMG_NAME, "snap1", ioctx, "clone", features) self.clone = Image(ioctx, "clone") def tearDown(self): global ioctx self.clone.close() self.rbd.remove(ioctx, "clone") self.image.unprotect_snap("snap1") self.image.remove_snap("snap1") self.image.close() remove_image() def test_unprotected(self): self.image.create_snap("snap2") global features assert_raises(InvalidArgument, self.rbd.clone, ioctx, IMG_NAME, "snap2", ioctx, "clone2", features) self.image.remove_snap("snap2") def test_unprotect_with_children(self): global features # can't remove a snapshot that has dependent clones assert_raises(ImageBusy, self.image.remove_snap, "snap1") # validate parent info of clone created by TestClone.setUp (pool, image, snap) = self.clone.parent_info() eq(pool, "rbd") eq(image, IMG_NAME) eq(snap, "snap1") # create a new pool... rados.create_pool("rbd2") other_ioctx = rados.open_ioctx("rbd2") # ...with a clone of the same parent self.rbd.clone(ioctx, IMG_NAME, "snap1", other_ioctx, "other_clone", features) self.other_clone = Image(other_ioctx, "other_clone") # validate its parent info (pool, image, snap) = self.other_clone.parent_info() eq(pool, "rbd") eq(image, IMG_NAME) eq(snap, "snap1") # can't unprotect snap with children assert_raises(ImageBusy, self.image.unprotect_snap, "snap1") # 2 children, check that cannot remove the parent snap assert_raises(ImageBusy, self.image.remove_snap, "snap1") # close and remove other pool's clone self.other_clone.close() self.rbd.remove(other_ioctx, "other_clone") # check that we cannot yet remove the parent snap assert_raises(ImageBusy, self.image.remove_snap, "snap1") other_ioctx.close() rados.delete_pool("rbd2") # unprotect, remove parent snap happen in cleanup, and should succeed def test_stat(self): image_info = self.image.stat() clone_info = self.clone.stat() eq(clone_info["size"], image_info["size"]) eq(clone_info["size"], self.clone.overlap()) def test_resize_stat(self): self.clone.resize(IMG_SIZE / 2) image_info = self.image.stat() clone_info = self.clone.stat() eq(clone_info["size"], IMG_SIZE / 2) eq(image_info["size"], IMG_SIZE) eq(self.clone.overlap(), IMG_SIZE / 2) self.clone.resize(IMG_SIZE * 2) image_info = self.image.stat() clone_info = self.clone.stat() eq(clone_info["size"], IMG_SIZE * 2) eq(image_info["size"], IMG_SIZE) eq(self.clone.overlap(), IMG_SIZE / 2) def test_resize_io(self): parent_data = self.image.read(IMG_SIZE / 2, 256) self.clone.resize(IMG_SIZE / 2 + 128) child_data = self.clone.read(IMG_SIZE / 2, 128) eq(child_data, parent_data[:128]) self.clone.resize(IMG_SIZE) child_data = self.clone.read(IMG_SIZE / 2, 256) eq(child_data, parent_data[:128] + ("\0" * 128)) self.clone.resize(IMG_SIZE / 2 + 1) child_data = self.clone.read(IMG_SIZE / 2, 1) eq(child_data, parent_data[0]) self.clone.resize(0) self.clone.resize(IMG_SIZE) child_data = self.clone.read(IMG_SIZE / 2, 256) eq(child_data, "\0" * 256) def test_read(self): parent_data = self.image.read(IMG_SIZE / 2, 256) child_data = self.clone.read(IMG_SIZE / 2, 256) eq(child_data, parent_data) def test_write(self): parent_data = self.image.read(IMG_SIZE / 2, 256) new_data = rand_data(256) self.clone.write(new_data, IMG_SIZE / 2 + 256) child_data = self.clone.read(IMG_SIZE / 2 + 256, 256) eq(child_data, new_data) child_data = self.clone.read(IMG_SIZE / 2, 256) eq(child_data, parent_data) parent_data = self.image.read(IMG_SIZE / 2 + 256, 256) eq(parent_data, "\0" * 256) def test_list_children(self): global ioctx global features self.image.set_snap("snap1") eq(self.image.list_children(), [("rbd", "clone")]) self.clone.close() self.rbd.remove(ioctx, "clone") eq(self.image.list_children(), []) expected_children = [] for i in xrange(10): self.rbd.clone(ioctx, IMG_NAME, "snap1", ioctx, "clone%d" % i, features) expected_children.append(("rbd", "clone%d" % i)) eq(self.image.list_children(), expected_children) for i in xrange(10): self.rbd.remove(ioctx, "clone%d" % i) expected_children.pop(0) eq(self.image.list_children(), expected_children) eq(self.image.list_children(), []) self.rbd.clone(ioctx, IMG_NAME, "snap1", ioctx, "clone", features) eq(self.image.list_children(), [("rbd", "clone")]) self.clone = Image(ioctx, "clone") def test_flatten_errors(self): # test that we can't flatten a non-clone assert_raises(InvalidArgument, self.image.flatten) # test that we can't flatten a snapshot self.clone.create_snap("snap2") self.clone.set_snap("snap2") assert_raises(ReadOnlyImage, self.clone.flatten) self.clone.remove_snap("snap2") def check_flatten_with_order(self, new_order): global ioctx global features self.rbd.clone(ioctx, IMG_NAME, "snap1", ioctx, "clone2", features, new_order) # with Image(ioctx, 'clone2') as clone: clone2 = Image(ioctx, "clone2") clone2.flatten() eq(clone2.overlap(), 0) clone2.close() self.rbd.remove(ioctx, "clone2") # flatten after resizing to non-block size self.rbd.clone(ioctx, IMG_NAME, "snap1", ioctx, "clone2", features, new_order) with Image(ioctx, "clone2") as clone: clone.resize(IMG_SIZE / 2 - 1) clone.flatten() eq(0, clone.overlap()) self.rbd.remove(ioctx, "clone2") # flatten after resizing to non-block size self.rbd.clone(ioctx, IMG_NAME, "snap1", ioctx, "clone2", features, new_order) with Image(ioctx, "clone2") as clone: clone.resize(IMG_SIZE / 2 + 1) clone.flatten() eq(clone.overlap(), 0) self.rbd.remove(ioctx, "clone2") def test_flatten_basic(self): self.check_flatten_with_order(IMG_ORDER) def test_flatten_smaller_order(self): self.check_flatten_with_order(IMG_ORDER - 2) def test_flatten_larger_order(self): self.check_flatten_with_order(IMG_ORDER + 2) def test_flatten_drops_cache(self): global ioctx global features self.rbd.clone(ioctx, IMG_NAME, "snap1", ioctx, "clone2", features, IMG_ORDER) with Image(ioctx, "clone2") as clone: with Image(ioctx, "clone2") as clone2: # cache object non-existence data = clone.read(IMG_SIZE / 2, 256) clone2_data = clone2.read(IMG_SIZE / 2, 256) eq(data, clone2_data) clone.flatten() assert_raises(ImageNotFound, clone.parent_info) assert_raises(ImageNotFound, clone2.parent_info) after_flatten = clone.read(IMG_SIZE / 2, 256) eq(data, after_flatten) after_flatten = clone2.read(IMG_SIZE / 2, 256) eq(data, after_flatten) self.rbd.remove(ioctx, "clone2")
class TestMirroring(object): @staticmethod def check_info(info, global_id, state, primary=None): eq(global_id, info['global_id']) eq(state, info['state']) if primary is not None: eq(primary, info['primary']) def setUp(self): self.rbd = RBD() self.initial_mirror_mode = self.rbd.mirror_mode_get(ioctx) self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL) create_image() self.image = Image(ioctx, image_name) def tearDown(self): self.image.close() remove_image() self.rbd.mirror_mode_set(ioctx, self.initial_mirror_mode) def test_mirror_peer(self): eq([], list(self.rbd.mirror_peer_list(ioctx))) cluster_name = "test_cluster" client_name = "test_client" uuid = self.rbd.mirror_peer_add(ioctx, cluster_name, client_name) assert(uuid) peer = { 'uuid' : uuid, 'cluster_name' : cluster_name, 'client_name' : client_name, } eq([peer], list(self.rbd.mirror_peer_list(ioctx))) cluster_name = "test_cluster1" self.rbd.mirror_peer_set_cluster(ioctx, uuid, cluster_name) client_name = "test_client1" self.rbd.mirror_peer_set_client(ioctx, uuid, client_name) peer = { 'uuid' : uuid, 'cluster_name' : cluster_name, 'client_name' : client_name, } eq([peer], list(self.rbd.mirror_peer_list(ioctx))) self.rbd.mirror_peer_remove(ioctx, uuid) eq([], list(self.rbd.mirror_peer_list(ioctx))) @require_features([RBD_FEATURE_EXCLUSIVE_LOCK, RBD_FEATURE_JOURNALING]) def test_mirror_image(self): self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE) self.image.mirror_image_disable(True) info = self.image.mirror_image_get_info() self.check_info(info, '', RBD_MIRROR_IMAGE_DISABLED, False) self.image.mirror_image_enable() info = self.image.mirror_image_get_info() global_id = info['global_id'] self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True) self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL) fail = False try: self.image.mirror_image_disable(True) except InvalidArgument: fail = True eq(True, fail) # Fails because of mirror mode pool self.image.mirror_image_demote() info = self.image.mirror_image_get_info() self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, False) self.image.mirror_image_resync() self.image.mirror_image_promote(True) info = self.image.mirror_image_get_info() self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True) fail = False try: self.image.mirror_image_resync() except InvalidArgument: fail = True eq(True, fail) # Fails because it is primary status = self.image.mirror_image_get_status() eq(image_name, status['name']) eq(False, status['up']) eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state']) info = status['info'] self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True) @require_features([RBD_FEATURE_EXCLUSIVE_LOCK, RBD_FEATURE_JOURNALING]) def test_mirror_image_status(self): info = self.image.mirror_image_get_info() global_id = info['global_id'] state = info['state'] primary = info['primary'] status = self.image.mirror_image_get_status() eq(image_name, status['name']) eq(False, status['up']) eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state']) info = status['info'] self.check_info(info, global_id, state, primary) images = list(self.rbd.mirror_image_status_list(ioctx)) eq(1, len(images)) status = images[0] eq(image_name, status['name']) eq(False, status['up']) eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state']) info = status['info'] self.check_info(info, global_id, state) states = self.rbd.mirror_image_status_summary(ioctx) eq([(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, 1)], states) N = 65 for i in range(N): self.rbd.create(ioctx, image_name + str(i), IMG_SIZE, IMG_ORDER, old_format=False, features=int(features)) images = list(self.rbd.mirror_image_status_list(ioctx)) eq(N + 1, len(images)) for i in range(N): self.rbd.remove(ioctx, image_name + str(i))
class TestClone(object): @require_features([RBD_FEATURE_LAYERING]) def setUp(self): global ioctx global features self.rbd = RBD() create_image() self.image = Image(ioctx, IMG_NAME) data = rand_data(256) self.image.write(data, IMG_SIZE / 2) self.image.create_snap('snap1') global features self.image.protect_snap('snap1') self.rbd.clone(ioctx, IMG_NAME, 'snap1', ioctx, 'clone', features) self.clone = Image(ioctx, 'clone') def tearDown(self): global ioctx self.clone.close() self.rbd.remove(ioctx, 'clone') self.image.unprotect_snap('snap1') self.image.remove_snap('snap1') self.image.close() remove_image() def test_unprotected(self): self.image.create_snap('snap2') global features assert_raises(InvalidArgument, self.rbd.clone, ioctx, IMG_NAME, 'snap2', ioctx, 'clone2', features) self.image.remove_snap('snap2') def test_unprotect_with_children(self): global features # can't remove a snapshot that has dependent clones assert_raises(ImageBusy, self.image.remove_snap, 'snap1') # validate parent info of clone created by TestClone.setUp (pool, image, snap) = self.clone.parent_info() eq(pool, 'rbd') eq(image, IMG_NAME) eq(snap, 'snap1') # create a new pool... rados.create_pool('rbd2') other_ioctx = rados.open_ioctx('rbd2') # ...with a clone of the same parent self.rbd.clone(ioctx, IMG_NAME, 'snap1', other_ioctx, 'other_clone', features) self.other_clone = Image(other_ioctx, 'other_clone') # validate its parent info (pool, image, snap) = self.other_clone.parent_info() eq(pool, 'rbd') eq(image, IMG_NAME) eq(snap, 'snap1') # can't unprotect snap with children assert_raises(ImageBusy, self.image.unprotect_snap, 'snap1') # 2 children, check that cannot remove the parent snap assert_raises(ImageBusy, self.image.remove_snap, 'snap1') # close and remove other pool's clone self.other_clone.close() self.rbd.remove(other_ioctx, 'other_clone') # check that we cannot yet remove the parent snap assert_raises(ImageBusy, self.image.remove_snap, 'snap1') other_ioctx.close() rados.delete_pool('rbd2') # unprotect, remove parent snap happen in cleanup, and should succeed def test_stat(self): image_info = self.image.stat() clone_info = self.clone.stat() eq(clone_info['size'], image_info['size']) eq(clone_info['size'], self.clone.overlap()) def test_resize_stat(self): self.clone.resize(IMG_SIZE / 2) image_info = self.image.stat() clone_info = self.clone.stat() eq(clone_info['size'], IMG_SIZE / 2) eq(image_info['size'], IMG_SIZE) eq(self.clone.overlap(), IMG_SIZE / 2) self.clone.resize(IMG_SIZE * 2) image_info = self.image.stat() clone_info = self.clone.stat() eq(clone_info['size'], IMG_SIZE * 2) eq(image_info['size'], IMG_SIZE) eq(self.clone.overlap(), IMG_SIZE / 2) def test_resize_io(self): parent_data = self.image.read(IMG_SIZE / 2, 256) self.clone.resize(IMG_SIZE / 2 + 128) child_data = self.clone.read(IMG_SIZE / 2, 128) eq(child_data, parent_data[:128]) self.clone.resize(IMG_SIZE) child_data = self.clone.read(IMG_SIZE / 2, 256) eq(child_data, parent_data[:128] + ('\0' * 128)) self.clone.resize(IMG_SIZE / 2 + 1) child_data = self.clone.read(IMG_SIZE / 2, 1) eq(child_data, parent_data[0]) self.clone.resize(0) self.clone.resize(IMG_SIZE) child_data = self.clone.read(IMG_SIZE / 2, 256) eq(child_data, '\0' * 256) def test_read(self): parent_data = self.image.read(IMG_SIZE / 2, 256) child_data = self.clone.read(IMG_SIZE / 2, 256) eq(child_data, parent_data) def test_write(self): parent_data = self.image.read(IMG_SIZE / 2, 256) new_data = rand_data(256) self.clone.write(new_data, IMG_SIZE / 2 + 256) child_data = self.clone.read(IMG_SIZE / 2 + 256, 256) eq(child_data, new_data) child_data = self.clone.read(IMG_SIZE / 2, 256) eq(child_data, parent_data) parent_data = self.image.read(IMG_SIZE / 2 + 256, 256) eq(parent_data, '\0' * 256) def test_list_children(self): global ioctx global features self.image.set_snap('snap1') eq(self.image.list_children(), [('rbd', 'clone')]) self.rbd.remove(ioctx, 'clone') eq(self.image.list_children(), []) expected_children = [] for i in xrange(10): self.rbd.clone(ioctx, IMG_NAME, 'snap1', ioctx, 'clone%d' % i, features) expected_children.append(('rbd', 'clone%d' % i)) eq(self.image.list_children(), expected_children) for i in xrange(10): self.rbd.remove(ioctx, 'clone%d' % i) expected_children.pop(0) eq(self.image.list_children(), expected_children) eq(self.image.list_children(), []) self.rbd.clone(ioctx, IMG_NAME, 'snap1', ioctx, 'clone', features) eq(self.image.list_children(), [('rbd', 'clone')])
class TestImage(object): def setUp(self): self.rbd = RBD() create_image() self.image = Image(ioctx, IMG_NAME) def tearDown(self): self.image.close() remove_image() def test_stat(self): info = self.image.stat() check_stat(info, IMG_SIZE, IMG_ORDER) def test_write(self): data = rand_data(256) self.image.write(data, 0) def test_read(self): data = self.image.read(0, 20) eq(data, '\0' * 20) def test_large_write(self): data = rand_data(IMG_SIZE) self.image.write(data, 0) def test_large_read(self): data = self.image.read(0, IMG_SIZE) eq(data, '\0' * IMG_SIZE) def test_write_read(self): data = rand_data(256) offset = 50 self.image.write(data, offset) read = self.image.read(offset, 256) eq(data, read) def test_read_bad_offset(self): assert_raises(InvalidArgument, self.image.read, IMG_SIZE + 1, IMG_SIZE) def test_resize(self): new_size = IMG_SIZE * 2 self.image.resize(new_size) info = self.image.stat() check_stat(info, new_size, IMG_ORDER) def test_size(self): eq(IMG_SIZE, self.image.size()) self.image.create_snap('snap1') new_size = IMG_SIZE * 2 self.image.resize(new_size) eq(new_size, self.image.size()) self.image.create_snap('snap2') self.image.set_snap('snap2') eq(new_size, self.image.size()) self.image.set_snap('snap1') eq(IMG_SIZE, self.image.size()) self.image.set_snap(None) eq(new_size, self.image.size()) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_resize_down(self): new_size = IMG_SIZE / 2 data = rand_data(256) self.image.write(data, IMG_SIZE / 2); self.image.resize(new_size) self.image.resize(IMG_SIZE) read = self.image.read(IMG_SIZE / 2, 256) eq('\0' * 256, read) def test_resize_bytes(self): new_size = IMG_SIZE / 2 - 5 data = rand_data(256) self.image.write(data, IMG_SIZE / 2 - 10); self.image.resize(new_size) self.image.resize(IMG_SIZE) read = self.image.read(IMG_SIZE / 2 - 10, 5) eq(data[:5], read) read = self.image.read(IMG_SIZE / 2 - 5, 251) eq('\0' * 251, read) def test_copy(self): global ioctx data = rand_data(256) self.image.write(data, 256) self.image.copy(ioctx, IMG_NAME + '2') assert_raises(ImageExists, self.image.copy, ioctx, IMG_NAME + '2') copy = Image(ioctx, IMG_NAME + '2') copy_data = copy.read(256, 256) copy.close() self.rbd.remove(ioctx, IMG_NAME + '2') eq(data, copy_data) def test_create_snap(self): global ioctx self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) at_snapshot = Image(ioctx, IMG_NAME, 'snap1') snap_data = at_snapshot.read(0, 256) at_snapshot.close() eq(snap_data, '\0' * 256) self.image.remove_snap('snap1') def test_list_snaps(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.create_snap('snap2') eq(['snap1', 'snap2'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_remove_snap(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.remove_snap('snap1') eq([], list(self.image.list_snaps())) @require_features([RBD_FEATURE_LAYERING]) def test_protect_snap(self): self.image.create_snap('snap1') assert(not self.image.is_protected_snap('snap1')) self.image.protect_snap('snap1') assert(self.image.is_protected_snap('snap1')) assert_raises(ImageBusy, self.image.remove_snap, 'snap1') self.image.unprotect_snap('snap1') assert(not self.image.is_protected_snap('snap1')) self.image.remove_snap('snap1') assert_raises(ImageNotFound, self.image.unprotect_snap, 'snap1') assert_raises(ImageNotFound, self.image.is_protected_snap, 'snap1') def test_remove_with_snap(self): self.image.create_snap('snap1') assert_raises(ImageHasSnapshots, remove_image) self.image.remove_snap('snap1') def test_remove_with_watcher(self): assert_raises(ImageBusy, remove_image) def test_rollback_to_snap(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_rollback_to_snap_sparse(self): self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_rollback_with_resize(self): read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, data) new_size = IMG_SIZE * 2 self.image.resize(new_size) check_stat(self.image.stat(), new_size, IMG_ORDER) self.image.write(data, new_size - 256) self.image.create_snap('snap2') read = self.image.read(new_size - 256, 256) eq(read, data) self.image.rollback_to_snap('snap1') check_stat(self.image.stat(), IMG_SIZE, IMG_ORDER) assert_raises(InvalidArgument, self.image.read, new_size - 256, 256) self.image.rollback_to_snap('snap2') check_stat(self.image.stat(), new_size, IMG_ORDER) read = self.image.read(new_size - 256, 256) eq(read, data) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_set_snap(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_set_no_snap(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) self.image.remove_snap('snap1') def test_set_snap_sparse(self): self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_many_snaps(self): num_snaps = 200 for i in xrange(num_snaps): self.image.create_snap(str(i)) snaps = sorted(self.image.list_snaps(), key=lambda snap: int(snap['name'])) eq(len(snaps), num_snaps) for i, snap in enumerate(snaps): eq(snap['size'], IMG_SIZE) eq(snap['name'], str(i)) for i in xrange(num_snaps): self.image.remove_snap(str(i)) def test_set_snap_deleted(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') self.image.remove_snap('snap1') assert_raises(ImageNotFound, self.image.read, 0, 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) def test_set_snap_recreated(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') self.image.remove_snap('snap1') self.image.create_snap('snap1') assert_raises(ImageNotFound, self.image.read, 0, 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) self.image.remove_snap('snap1') def test_lock_unlock(self): assert_raises(ImageNotFound, self.image.unlock, '') self.image.lock_exclusive('') assert_raises(ImageExists, self.image.lock_exclusive, '') assert_raises(ImageBusy, self.image.lock_exclusive, 'test') assert_raises(ImageExists, self.image.lock_shared, '', '') assert_raises(ImageBusy, self.image.lock_shared, 'foo', '') self.image.unlock('') def test_list_lockers(self): eq([], self.image.list_lockers()) self.image.lock_exclusive('test') lockers = self.image.list_lockers() eq(1, len(lockers['lockers'])) _, cookie, _ = lockers['lockers'][0] eq(cookie, 'test') eq('', lockers['tag']) assert lockers['exclusive'] self.image.unlock('test') eq([], self.image.list_lockers()) num_shared = 10 for i in xrange(num_shared): self.image.lock_shared(str(i), 'tag') lockers = self.image.list_lockers() eq('tag', lockers['tag']) assert not lockers['exclusive'] eq(num_shared, len(lockers['lockers'])) cookies = sorted(map(lambda x: x[1], lockers['lockers'])) for i in xrange(num_shared): eq(str(i), cookies[i]) self.image.unlock(str(i)) eq([], self.image.list_lockers())
def setUp(self): self.rbd = RBD() self.initial_mirror_mode = self.rbd.mirror_mode_get(ioctx) self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL) create_image() self.image = Image(ioctx, image_name)
def test_list_empty(): eq([], RBD().list(ioctx))
class TestImage(object): def setUp(self): self.rbd = RBD() create_image() self.image = Image(ioctx, image_name) def tearDown(self): self.image.close() remove_image() @require_new_format() @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_update_features(self): features = self.image.features() self.image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, True) eq(features | RBD_FEATURE_EXCLUSIVE_LOCK, self.image.features()) @require_features([RBD_FEATURE_STRIPINGV2]) def test_create_with_params(self): global features image_name = get_temp_image_name() order = 20 stripe_unit = 1 << 20 stripe_count = 10 self.rbd.create(ioctx, image_name, IMG_SIZE, order, False, features, stripe_unit, stripe_count) image = Image(ioctx, image_name) info = image.stat() check_stat(info, IMG_SIZE, order) eq(image.features(), features) eq(image.stripe_unit(), stripe_unit) eq(image.stripe_count(), stripe_count) image.close() RBD().remove(ioctx, image_name) def test_invalidate_cache(self): self.image.write(b'abc', 0) eq(b'abc', self.image.read(0, 3)) self.image.invalidate_cache() eq(b'abc', self.image.read(0, 3)) def test_stat(self): info = self.image.stat() check_stat(info, IMG_SIZE, IMG_ORDER) def test_flags(self): flags = self.image.flags() eq(0, flags) def test_write(self): data = rand_data(256) self.image.write(data, 0) def test_write_with_fadvise_flags(self): data = rand_data(256) self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED) self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_NOCACHE) def test_read(self): data = self.image.read(0, 20) eq(data, b'\0' * 20) def test_read_with_fadvise_flags(self): data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED) eq(data, b'\0' * 20) data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM) eq(data, b'\0' * 20) def test_large_write(self): data = rand_data(IMG_SIZE) self.image.write(data, 0) def test_large_read(self): data = self.image.read(0, IMG_SIZE) eq(data, b'\0' * IMG_SIZE) def test_write_read(self): data = rand_data(256) offset = 50 self.image.write(data, offset) read = self.image.read(offset, 256) eq(data, read) def test_read_bad_offset(self): assert_raises(InvalidArgument, self.image.read, IMG_SIZE + 1, IMG_SIZE) def test_resize(self): new_size = IMG_SIZE * 2 self.image.resize(new_size) info = self.image.stat() check_stat(info, new_size, IMG_ORDER) def test_size(self): eq(IMG_SIZE, self.image.size()) self.image.create_snap('snap1') new_size = IMG_SIZE * 2 self.image.resize(new_size) eq(new_size, self.image.size()) self.image.create_snap('snap2') self.image.set_snap('snap2') eq(new_size, self.image.size()) self.image.set_snap('snap1') eq(IMG_SIZE, self.image.size()) self.image.set_snap(None) eq(new_size, self.image.size()) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_resize_down(self): new_size = IMG_SIZE // 2 data = rand_data(256) self.image.write(data, IMG_SIZE // 2) self.image.resize(new_size) self.image.resize(IMG_SIZE) read = self.image.read(IMG_SIZE // 2, 256) eq(b'\0' * 256, read) def test_resize_bytes(self): new_size = IMG_SIZE // 2 - 5 data = rand_data(256) self.image.write(data, IMG_SIZE // 2 - 10) self.image.resize(new_size) self.image.resize(IMG_SIZE) read = self.image.read(IMG_SIZE // 2 - 10, 5) eq(data[:5], read) read = self.image.read(IMG_SIZE // 2 - 5, 251) eq(b'\0' * 251, read) def _test_copy(self, features=None, order=None, stripe_unit=None, stripe_count=None): global ioctx data = rand_data(256) self.image.write(data, 256) image_name = get_temp_image_name() if features is None: self.image.copy(ioctx, image_name) elif order is None: self.image.copy(ioctx, image_name, features) elif stripe_unit is None: self.image.copy(ioctx, image_name, features, order) elif stripe_count is None: self.image.copy(ioctx, image_name, features, order, stripe_unit) else: self.image.copy(ioctx, image_name, features, order, stripe_unit, stripe_count) assert_raises(ImageExists, self.image.copy, ioctx, image_name) copy = Image(ioctx, image_name) copy_data = copy.read(256, 256) copy.close() self.rbd.remove(ioctx, image_name) eq(data, copy_data) def test_copy(self): self._test_copy() def test_copy2(self): self._test_copy(self.image.features(), self.image.stat()['order']) @require_features([RBD_FEATURE_STRIPINGV2]) def test_copy3(self): global features self._test_copy(features, self.image.stat()['order'], self.image.stripe_unit(), self.image.stripe_count()) def test_create_snap(self): global ioctx self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) at_snapshot = Image(ioctx, image_name, 'snap1') snap_data = at_snapshot.read(0, 256) at_snapshot.close() eq(snap_data, b'\0' * 256) self.image.remove_snap('snap1') def test_list_snaps(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()]) self.image.create_snap('snap2') eq(['snap1', 'snap2'], [snap['name'] for snap in self.image.list_snaps()]) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_remove_snap(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()]) self.image.remove_snap('snap1') eq([], list(self.image.list_snaps())) def test_rename_snap(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()]) self.image.rename_snap("snap1", "snap1-rename") eq(['snap1-rename'], [snap['name'] for snap in self.image.list_snaps()]) self.image.remove_snap('snap1-rename') eq([], list(self.image.list_snaps())) @require_features([RBD_FEATURE_LAYERING]) def test_protect_snap(self): self.image.create_snap('snap1') assert (not self.image.is_protected_snap('snap1')) self.image.protect_snap('snap1') assert (self.image.is_protected_snap('snap1')) assert_raises(ImageBusy, self.image.remove_snap, 'snap1') self.image.unprotect_snap('snap1') assert (not self.image.is_protected_snap('snap1')) self.image.remove_snap('snap1') assert_raises(ImageNotFound, self.image.unprotect_snap, 'snap1') assert_raises(ImageNotFound, self.image.is_protected_snap, 'snap1') @require_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_remove_with_exclusive_lock(self): assert_raises(ImageBusy, remove_image) @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_remove_with_snap(self): self.image.create_snap('snap1') assert_raises(ImageHasSnapshots, remove_image) self.image.remove_snap('snap1') @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_remove_with_watcher(self): data = rand_data(256) self.image.write(data, 0) assert_raises(ImageBusy, remove_image) read = self.image.read(0, 256) eq(read, data) def test_rollback_to_snap(self): self.image.write(b'\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) self.image.remove_snap('snap1') def test_rollback_to_snap_sparse(self): self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) self.image.remove_snap('snap1') def test_rollback_with_resize(self): read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, data) new_size = IMG_SIZE * 2 self.image.resize(new_size) check_stat(self.image.stat(), new_size, IMG_ORDER) self.image.write(data, new_size - 256) self.image.create_snap('snap2') read = self.image.read(new_size - 256, 256) eq(read, data) self.image.rollback_to_snap('snap1') check_stat(self.image.stat(), IMG_SIZE, IMG_ORDER) assert_raises(InvalidArgument, self.image.read, new_size - 256, 256) self.image.rollback_to_snap('snap2') check_stat(self.image.stat(), new_size, IMG_ORDER) read = self.image.read(new_size - 256, 256) eq(read, data) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_set_snap(self): self.image.write(b'\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) self.image.remove_snap('snap1') def test_set_no_snap(self): self.image.write(b'\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) self.image.remove_snap('snap1') def test_set_snap_sparse(self): self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) self.image.remove_snap('snap1') def test_many_snaps(self): num_snaps = 200 for i in range(num_snaps): self.image.create_snap(str(i)) snaps = sorted(self.image.list_snaps(), key=lambda snap: int(snap['name'])) eq(len(snaps), num_snaps) for i, snap in enumerate(snaps): eq(snap['size'], IMG_SIZE) eq(snap['name'], str(i)) for i in range(num_snaps): self.image.remove_snap(str(i)) def test_set_snap_deleted(self): self.image.write(b'\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') self.image.remove_snap('snap1') assert_raises(ImageNotFound, self.image.read, 0, 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) def test_set_snap_recreated(self): self.image.write(b'\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') self.image.remove_snap('snap1') self.image.create_snap('snap1') assert_raises(ImageNotFound, self.image.read, 0, 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) self.image.remove_snap('snap1') def test_lock_unlock(self): assert_raises(ImageNotFound, self.image.unlock, '') self.image.lock_exclusive('') assert_raises(ImageExists, self.image.lock_exclusive, '') assert_raises(ImageBusy, self.image.lock_exclusive, 'test') assert_raises(ImageExists, self.image.lock_shared, '', '') assert_raises(ImageBusy, self.image.lock_shared, 'foo', '') self.image.unlock('') def test_list_lockers(self): eq([], self.image.list_lockers()) self.image.lock_exclusive('test') lockers = self.image.list_lockers() eq(1, len(lockers['lockers'])) _, cookie, _ = lockers['lockers'][0] eq(cookie, 'test') eq('', lockers['tag']) assert lockers['exclusive'] self.image.unlock('test') eq([], self.image.list_lockers()) num_shared = 10 for i in range(num_shared): self.image.lock_shared(str(i), 'tag') lockers = self.image.list_lockers() eq('tag', lockers['tag']) assert not lockers['exclusive'] eq(num_shared, len(lockers['lockers'])) cookies = sorted(map(lambda x: x[1], lockers['lockers'])) for i in range(num_shared): eq(str(i), cookies[i]) self.image.unlock(str(i)) eq([], self.image.list_lockers()) def test_diff_iterate(self): check_diff(self.image, 0, IMG_SIZE, None, []) self.image.write(b'a' * 256, 0) check_diff(self.image, 0, IMG_SIZE, None, [(0, 256, True)]) self.image.write(b'b' * 256, 256) check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)]) self.image.discard(128, 256) check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)]) self.image.create_snap('snap1') self.image.discard(0, 1 << IMG_ORDER) self.image.create_snap('snap2') self.image.set_snap('snap2') check_diff(self.image, 0, IMG_SIZE, 'snap1', [(0, 512, False)]) self.image.remove_snap('snap1') self.image.remove_snap('snap2')
def setUp(self): self.rbd = RBD() create_image() self.image = Image(ioctx, image_name)
def remove_image(): if image_name is not None: RBD().remove(ioctx, image_name)
class TestImage(object): def setUp(self): self.rbd = RBD() create_image() self.image = Image(ioctx, IMG_NAME) def tearDown(self): self.image.close() remove_image() def test_stat(self): info = self.image.stat() check_stat(info, IMG_SIZE, IMG_ORDER) def test_write(self): data = rand_data(256) self.image.write(data, 0) def test_read(self): data = self.image.read(0, 20) eq(data, '\0' * 20) def test_large_write(self): data = rand_data(IMG_SIZE) self.image.write(data, 0) def test_large_read(self): data = self.image.read(0, IMG_SIZE) eq(data, '\0' * IMG_SIZE) def test_write_read(self): data = rand_data(256) offset = 50 self.image.write(data, offset) read = self.image.read(offset, 256) eq(data, read) def test_read_bad_offset(self): assert_raises(InvalidArgument, self.image.read, IMG_SIZE + 1, IMG_SIZE) def test_resize(self): new_size = IMG_SIZE * 2 self.image.resize(new_size) info = self.image.stat() check_stat(info, new_size, IMG_ORDER) def test_copy(self): global ioctx data = rand_data(256) self.image.write(data, 256) bytes_copied = self.image.copy(ioctx, IMG_NAME + '2') copy = Image(ioctx, IMG_NAME + '2') copy_data = copy.read(256, 256) copy.close() self.rbd.remove(ioctx, IMG_NAME + '2') eq(bytes_copied, IMG_SIZE) eq(data, copy_data) def test_create_snap(self): global ioctx self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) at_snapshot = Image(ioctx, IMG_NAME, 'snap1') snap_data = at_snapshot.read(0, 256) at_snapshot.close() eq(snap_data, '\0' * 256) def test_list_snaps(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.create_snap('snap2') eq(['snap1', 'snap2'], map(lambda snap: snap['name'], self.image.list_snaps())) def test_remove_snap(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.remove_snap('snap1') eq([], list(self.image.list_snaps())) def test_rollback_to_snap(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) def test_rollback_to_snap_sparse(self): self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) def test_set_snap(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) def test_set_no_snap(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) def test_set_snap_sparse(self): self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256)
class TestImage(object): def setUp(self): self.rbd = RBD() create_image() self.image = Image(ioctx, image_name) def tearDown(self): self.image.close() remove_image() @require_new_format() @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_update_features(self): features = self.image.features() self.image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, True) eq(features | RBD_FEATURE_EXCLUSIVE_LOCK, self.image.features()) @require_features([RBD_FEATURE_STRIPINGV2]) def test_create_with_params(self): global features image_name = get_temp_image_name() order = 20 stripe_unit = 1 << 20 stripe_count = 10 self.rbd.create(ioctx, image_name, IMG_SIZE, order, False, features, stripe_unit, stripe_count) image = Image(ioctx, image_name) info = image.stat() check_stat(info, IMG_SIZE, order) eq(image.features(), features) eq(image.stripe_unit(), stripe_unit) eq(image.stripe_count(), stripe_count) image.close() RBD().remove(ioctx, image_name) def test_invalidate_cache(self): self.image.write(b"abc", 0) eq(b"abc", self.image.read(0, 3)) self.image.invalidate_cache() eq(b"abc", self.image.read(0, 3)) def test_stat(self): info = self.image.stat() check_stat(info, IMG_SIZE, IMG_ORDER) def test_flags(self): flags = self.image.flags() eq(0, flags) def test_image_auto_close(self): image = Image(ioctx, image_name) def test_write(self): data = rand_data(256) self.image.write(data, 0) def test_write_with_fadvise_flags(self): data = rand_data(256) self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED) self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_NOCACHE) def test_read(self): data = self.image.read(0, 20) eq(data, b"\0" * 20) def test_read_with_fadvise_flags(self): data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED) eq(data, b"\0" * 20) data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM) eq(data, b"\0" * 20) def test_large_write(self): data = rand_data(IMG_SIZE) self.image.write(data, 0) def test_large_read(self): data = self.image.read(0, IMG_SIZE) eq(data, b"\0" * IMG_SIZE) def test_write_read(self): data = rand_data(256) offset = 50 self.image.write(data, offset) read = self.image.read(offset, 256) eq(data, read) def test_read_bad_offset(self): assert_raises(InvalidArgument, self.image.read, IMG_SIZE + 1, IMG_SIZE) def test_resize(self): new_size = IMG_SIZE * 2 self.image.resize(new_size) info = self.image.stat() check_stat(info, new_size, IMG_ORDER) def test_size(self): eq(IMG_SIZE, self.image.size()) self.image.create_snap("snap1") new_size = IMG_SIZE * 2 self.image.resize(new_size) eq(new_size, self.image.size()) self.image.create_snap("snap2") self.image.set_snap("snap2") eq(new_size, self.image.size()) self.image.set_snap("snap1") eq(IMG_SIZE, self.image.size()) self.image.set_snap(None) eq(new_size, self.image.size()) self.image.remove_snap("snap1") self.image.remove_snap("snap2") def test_resize_down(self): new_size = IMG_SIZE // 2 data = rand_data(256) self.image.write(data, IMG_SIZE // 2) self.image.resize(new_size) self.image.resize(IMG_SIZE) read = self.image.read(IMG_SIZE // 2, 256) eq(b"\0" * 256, read) def test_resize_bytes(self): new_size = IMG_SIZE // 2 - 5 data = rand_data(256) self.image.write(data, IMG_SIZE // 2 - 10) self.image.resize(new_size) self.image.resize(IMG_SIZE) read = self.image.read(IMG_SIZE // 2 - 10, 5) eq(data[:5], read) read = self.image.read(IMG_SIZE // 2 - 5, 251) eq(b"\0" * 251, read) def _test_copy(self, features=None, order=None, stripe_unit=None, stripe_count=None): global ioctx data = rand_data(256) self.image.write(data, 256) image_name = get_temp_image_name() if features is None: self.image.copy(ioctx, image_name) elif order is None: self.image.copy(ioctx, image_name, features) elif stripe_unit is None: self.image.copy(ioctx, image_name, features, order) elif stripe_count is None: self.image.copy(ioctx, image_name, features, order, stripe_unit) else: self.image.copy(ioctx, image_name, features, order, stripe_unit, stripe_count) assert_raises(ImageExists, self.image.copy, ioctx, image_name) copy = Image(ioctx, image_name) copy_data = copy.read(256, 256) copy.close() self.rbd.remove(ioctx, image_name) eq(data, copy_data) def test_copy(self): self._test_copy() def test_copy2(self): self._test_copy(self.image.features(), self.image.stat()["order"]) @require_features([RBD_FEATURE_STRIPINGV2]) def test_copy3(self): global features self._test_copy(features, self.image.stat()["order"], self.image.stripe_unit(), self.image.stripe_count()) def test_create_snap(self): global ioctx self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, b"\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) at_snapshot = Image(ioctx, image_name, "snap1") snap_data = at_snapshot.read(0, 256) at_snapshot.close() eq(snap_data, b"\0" * 256) self.image.remove_snap("snap1") def test_list_snaps(self): eq([], list(self.image.list_snaps())) self.image.create_snap("snap1") eq(["snap1"], [snap["name"] for snap in self.image.list_snaps()]) self.image.create_snap("snap2") eq(["snap1", "snap2"], [snap["name"] for snap in self.image.list_snaps()]) self.image.remove_snap("snap1") self.image.remove_snap("snap2") def test_list_snaps_iterator_auto_close(self): self.image.create_snap("snap1") self.image.list_snaps() self.image.remove_snap("snap1") def test_remove_snap(self): eq([], list(self.image.list_snaps())) self.image.create_snap("snap1") eq(["snap1"], [snap["name"] for snap in self.image.list_snaps()]) self.image.remove_snap("snap1") eq([], list(self.image.list_snaps())) def test_rename_snap(self): eq([], list(self.image.list_snaps())) self.image.create_snap("snap1") eq(["snap1"], [snap["name"] for snap in self.image.list_snaps()]) self.image.rename_snap("snap1", "snap1-rename") eq(["snap1-rename"], [snap["name"] for snap in self.image.list_snaps()]) self.image.remove_snap("snap1-rename") eq([], list(self.image.list_snaps())) @require_features([RBD_FEATURE_LAYERING]) def test_protect_snap(self): self.image.create_snap("snap1") assert not self.image.is_protected_snap("snap1") self.image.protect_snap("snap1") assert self.image.is_protected_snap("snap1") assert_raises(ImageBusy, self.image.remove_snap, "snap1") self.image.unprotect_snap("snap1") assert not self.image.is_protected_snap("snap1") self.image.remove_snap("snap1") assert_raises(ImageNotFound, self.image.unprotect_snap, "snap1") assert_raises(ImageNotFound, self.image.is_protected_snap, "snap1") @require_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_remove_with_exclusive_lock(self): assert_raises(ImageBusy, remove_image) @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_remove_with_snap(self): self.image.create_snap("snap1") assert_raises(ImageHasSnapshots, remove_image) self.image.remove_snap("snap1") @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_remove_with_watcher(self): data = rand_data(256) self.image.write(data, 0) assert_raises(ImageBusy, remove_image) read = self.image.read(0, 256) eq(read, data) def test_rollback_to_snap(self): self.image.write(b"\0" * 256, 0) self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, b"\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap("snap1") read = self.image.read(0, 256) eq(read, b"\0" * 256) self.image.remove_snap("snap1") def test_rollback_to_snap_sparse(self): self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, b"\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap("snap1") read = self.image.read(0, 256) eq(read, b"\0" * 256) self.image.remove_snap("snap1") def test_rollback_with_resize(self): read = self.image.read(0, 256) eq(read, b"\0" * 256) data = rand_data(256) self.image.write(data, 0) self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, data) new_size = IMG_SIZE * 2 self.image.resize(new_size) check_stat(self.image.stat(), new_size, IMG_ORDER) self.image.write(data, new_size - 256) self.image.create_snap("snap2") read = self.image.read(new_size - 256, 256) eq(read, data) self.image.rollback_to_snap("snap1") check_stat(self.image.stat(), IMG_SIZE, IMG_ORDER) assert_raises(InvalidArgument, self.image.read, new_size - 256, 256) self.image.rollback_to_snap("snap2") check_stat(self.image.stat(), new_size, IMG_ORDER) read = self.image.read(new_size - 256, 256) eq(read, data) self.image.remove_snap("snap1") self.image.remove_snap("snap2") def test_set_snap(self): self.image.write(b"\0" * 256, 0) self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, b"\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap("snap1") read = self.image.read(0, 256) eq(read, b"\0" * 256) self.image.remove_snap("snap1") def test_set_no_snap(self): self.image.write(b"\0" * 256, 0) self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, b"\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap("snap1") read = self.image.read(0, 256) eq(read, b"\0" * 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) self.image.remove_snap("snap1") def test_set_snap_sparse(self): self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, b"\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap("snap1") read = self.image.read(0, 256) eq(read, b"\0" * 256) self.image.remove_snap("snap1") def test_many_snaps(self): num_snaps = 200 for i in range(num_snaps): self.image.create_snap(str(i)) snaps = sorted(self.image.list_snaps(), key=lambda snap: int(snap["name"])) eq(len(snaps), num_snaps) for i, snap in enumerate(snaps): eq(snap["size"], IMG_SIZE) eq(snap["name"], str(i)) for i in range(num_snaps): self.image.remove_snap(str(i)) def test_set_snap_deleted(self): self.image.write(b"\0" * 256, 0) self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, b"\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap("snap1") self.image.remove_snap("snap1") assert_raises(ImageNotFound, self.image.read, 0, 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) def test_set_snap_recreated(self): self.image.write(b"\0" * 256, 0) self.image.create_snap("snap1") read = self.image.read(0, 256) eq(read, b"\0" * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap("snap1") self.image.remove_snap("snap1") self.image.create_snap("snap1") assert_raises(ImageNotFound, self.image.read, 0, 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) self.image.remove_snap("snap1") def test_lock_unlock(self): assert_raises(ImageNotFound, self.image.unlock, "") self.image.lock_exclusive("") assert_raises(ImageExists, self.image.lock_exclusive, "") assert_raises(ImageBusy, self.image.lock_exclusive, "test") assert_raises(ImageExists, self.image.lock_shared, "", "") assert_raises(ImageBusy, self.image.lock_shared, "foo", "") self.image.unlock("") def test_list_lockers(self): eq([], self.image.list_lockers()) self.image.lock_exclusive("test") lockers = self.image.list_lockers() eq(1, len(lockers["lockers"])) _, cookie, _ = lockers["lockers"][0] eq(cookie, "test") eq("", lockers["tag"]) assert lockers["exclusive"] self.image.unlock("test") eq([], self.image.list_lockers()) num_shared = 10 for i in range(num_shared): self.image.lock_shared(str(i), "tag") lockers = self.image.list_lockers() eq("tag", lockers["tag"]) assert not lockers["exclusive"] eq(num_shared, len(lockers["lockers"])) cookies = sorted(map(lambda x: x[1], lockers["lockers"])) for i in range(num_shared): eq(str(i), cookies[i]) self.image.unlock(str(i)) eq([], self.image.list_lockers()) def test_diff_iterate(self): check_diff(self.image, 0, IMG_SIZE, None, []) self.image.write(b"a" * 256, 0) check_diff(self.image, 0, IMG_SIZE, None, [(0, 256, True)]) self.image.write(b"b" * 256, 256) check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)]) self.image.discard(128, 256) check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)]) self.image.create_snap("snap1") self.image.discard(0, 1 << IMG_ORDER) self.image.create_snap("snap2") self.image.set_snap("snap2") check_diff(self.image, 0, IMG_SIZE, "snap1", [(0, 512, False)]) self.image.remove_snap("snap1") self.image.remove_snap("snap2")
class TestImage(object): def setUp(self): self.rbd = RBD() create_image() self.image = Image(ioctx, image_name) def tearDown(self): self.image.close() remove_image() @require_new_format() @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_update_features(self): features = self.image.features() self.image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, True) eq(features | RBD_FEATURE_EXCLUSIVE_LOCK, self.image.features()) def test_invalidate_cache(self): self.image.write('abc', 0) eq('abc', self.image.read(0, 3)) self.image.invalidate_cache() eq('abc', self.image.read(0, 3)) def test_stat(self): info = self.image.stat() check_stat(info, IMG_SIZE, IMG_ORDER) def test_flags(self): flags = self.image.flags() eq(0, flags) def test_write(self): data = rand_data(256) self.image.write(data, 0) def test_write_with_fadvise_flags(self): data = rand_data(256) self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED) self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_NOCACHE) def test_read(self): data = self.image.read(0, 20) eq(data, '\0' * 20) def test_read_with_fadvise_flags(self): data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED) eq(data, '\0' * 20) data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM) eq(data, '\0' * 20) def test_large_write(self): data = rand_data(IMG_SIZE) self.image.write(data, 0) def test_large_read(self): data = self.image.read(0, IMG_SIZE) eq(data, '\0' * IMG_SIZE) def test_write_read(self): data = rand_data(256) offset = 50 self.image.write(data, offset) read = self.image.read(offset, 256) eq(data, read) def test_read_bad_offset(self): assert_raises(InvalidArgument, self.image.read, IMG_SIZE + 1, IMG_SIZE) def test_resize(self): new_size = IMG_SIZE * 2 self.image.resize(new_size) info = self.image.stat() check_stat(info, new_size, IMG_ORDER) def test_size(self): eq(IMG_SIZE, self.image.size()) self.image.create_snap('snap1') new_size = IMG_SIZE * 2 self.image.resize(new_size) eq(new_size, self.image.size()) self.image.create_snap('snap2') self.image.set_snap('snap2') eq(new_size, self.image.size()) self.image.set_snap('snap1') eq(IMG_SIZE, self.image.size()) self.image.set_snap(None) eq(new_size, self.image.size()) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_resize_down(self): new_size = IMG_SIZE / 2 data = rand_data(256) self.image.write(data, IMG_SIZE / 2); self.image.resize(new_size) self.image.resize(IMG_SIZE) read = self.image.read(IMG_SIZE / 2, 256) eq('\0' * 256, read) def test_resize_bytes(self): new_size = IMG_SIZE / 2 - 5 data = rand_data(256) self.image.write(data, IMG_SIZE / 2 - 10); self.image.resize(new_size) self.image.resize(IMG_SIZE) read = self.image.read(IMG_SIZE / 2 - 10, 5) eq(data[:5], read) read = self.image.read(IMG_SIZE / 2 - 5, 251) eq('\0' * 251, read) def test_copy(self): global ioctx data = rand_data(256) self.image.write(data, 256) image_name = get_temp_image_name() self.image.copy(ioctx, image_name) assert_raises(ImageExists, self.image.copy, ioctx, image_name) copy = Image(ioctx, image_name) copy_data = copy.read(256, 256) copy.close() self.rbd.remove(ioctx, image_name) eq(data, copy_data) def test_create_snap(self): global ioctx self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) at_snapshot = Image(ioctx, image_name, 'snap1') snap_data = at_snapshot.read(0, 256) at_snapshot.close() eq(snap_data, '\0' * 256) self.image.remove_snap('snap1') def test_list_snaps(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.create_snap('snap2') eq(['snap1', 'snap2'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_remove_snap(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.remove_snap('snap1') eq([], list(self.image.list_snaps())) def test_rename_snap(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.rename_snap("snap1", "snap1-rename") eq(['snap1-rename'], map(lambda snap: snap['name'], self.image.list_snaps())) self.image.remove_snap('snap1-rename') eq([], list(self.image.list_snaps())) @require_features([RBD_FEATURE_LAYERING]) def test_protect_snap(self): self.image.create_snap('snap1') assert(not self.image.is_protected_snap('snap1')) self.image.protect_snap('snap1') assert(self.image.is_protected_snap('snap1')) assert_raises(ImageBusy, self.image.remove_snap, 'snap1') self.image.unprotect_snap('snap1') assert(not self.image.is_protected_snap('snap1')) self.image.remove_snap('snap1') assert_raises(ImageNotFound, self.image.unprotect_snap, 'snap1') assert_raises(ImageNotFound, self.image.is_protected_snap, 'snap1') @require_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_remove_with_exclusive_lock(self): assert_raises(ImageBusy, remove_image) @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_remove_with_snap(self): self.image.create_snap('snap1') assert_raises(ImageHasSnapshots, remove_image) self.image.remove_snap('snap1') @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_remove_with_watcher(self): data = rand_data(256) self.image.write(data, 0) assert_raises(ImageBusy, remove_image) read = self.image.read(0, 256) eq(read, data) def test_rollback_to_snap(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_rollback_to_snap_sparse(self): self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_rollback_with_resize(self): read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, data) new_size = IMG_SIZE * 2 self.image.resize(new_size) check_stat(self.image.stat(), new_size, IMG_ORDER) self.image.write(data, new_size - 256) self.image.create_snap('snap2') read = self.image.read(new_size - 256, 256) eq(read, data) self.image.rollback_to_snap('snap1') check_stat(self.image.stat(), IMG_SIZE, IMG_ORDER) assert_raises(InvalidArgument, self.image.read, new_size - 256, 256) self.image.rollback_to_snap('snap2') check_stat(self.image.stat(), new_size, IMG_ORDER) read = self.image.read(new_size - 256, 256) eq(read, data) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_set_snap(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_set_no_snap(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) self.image.remove_snap('snap1') def test_set_snap_sparse(self): self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) self.image.remove_snap('snap1') def test_many_snaps(self): num_snaps = 200 for i in xrange(num_snaps): self.image.create_snap(str(i)) snaps = sorted(self.image.list_snaps(), key=lambda snap: int(snap['name'])) eq(len(snaps), num_snaps) for i, snap in enumerate(snaps): eq(snap['size'], IMG_SIZE) eq(snap['name'], str(i)) for i in xrange(num_snaps): self.image.remove_snap(str(i)) def test_set_snap_deleted(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') self.image.remove_snap('snap1') assert_raises(ImageNotFound, self.image.read, 0, 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) def test_set_snap_recreated(self): self.image.write('\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, '\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') self.image.remove_snap('snap1') self.image.create_snap('snap1') assert_raises(ImageNotFound, self.image.read, 0, 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) self.image.remove_snap('snap1') def test_lock_unlock(self): assert_raises(ImageNotFound, self.image.unlock, '') self.image.lock_exclusive('') assert_raises(ImageExists, self.image.lock_exclusive, '') assert_raises(ImageBusy, self.image.lock_exclusive, 'test') assert_raises(ImageExists, self.image.lock_shared, '', '') assert_raises(ImageBusy, self.image.lock_shared, 'foo', '') self.image.unlock('') def test_list_lockers(self): eq([], self.image.list_lockers()) self.image.lock_exclusive('test') lockers = self.image.list_lockers() eq(1, len(lockers['lockers'])) _, cookie, _ = lockers['lockers'][0] eq(cookie, 'test') eq('', lockers['tag']) assert lockers['exclusive'] self.image.unlock('test') eq([], self.image.list_lockers()) num_shared = 10 for i in xrange(num_shared): self.image.lock_shared(str(i), 'tag') lockers = self.image.list_lockers() eq('tag', lockers['tag']) assert not lockers['exclusive'] eq(num_shared, len(lockers['lockers'])) cookies = sorted(map(lambda x: x[1], lockers['lockers'])) for i in xrange(num_shared): eq(str(i), cookies[i]) self.image.unlock(str(i)) eq([], self.image.list_lockers()) def test_diff_iterate(self): check_diff(self.image, 0, IMG_SIZE, None, []) self.image.write('a' * 256, 0) check_diff(self.image, 0, IMG_SIZE, None, [(0, 256, True)]) self.image.write('b' * 256, 256) check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)]) self.image.discard(128, 256) check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)]) self.image.create_snap('snap1') self.image.discard(0, 1 << IMG_ORDER) self.image.create_snap('snap2') self.image.set_snap('snap2') check_diff(self.image, 0, IMG_SIZE, 'snap1', [(0, 512, False)]) self.image.remove_snap('snap1') self.image.remove_snap('snap2')
def test_rename(): rbd = RBD() rbd.rename(ioctx, IMG_NAME, IMG_NAME + "2") eq([IMG_NAME + "2"], rbd.list(ioctx)) rbd.rename(ioctx, IMG_NAME + "2", IMG_NAME) eq([IMG_NAME], rbd.list(ioctx))
class TestImage(object): def setUp(self): self.rbd = RBD() create_image() self.image = Image(ioctx, image_name) def tearDown(self): self.image.close() remove_image() self.image = None @require_new_format() @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_update_features(self): features = self.image.features() self.image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, True) eq(features | RBD_FEATURE_EXCLUSIVE_LOCK, self.image.features()) @require_features([RBD_FEATURE_STRIPINGV2]) def test_create_with_params(self): global features image_name = get_temp_image_name() order = 20 stripe_unit = 1 << 20 stripe_count = 10 self.rbd.create(ioctx, image_name, IMG_SIZE, order, False, features, stripe_unit, stripe_count) image = Image(ioctx, image_name) info = image.stat() check_stat(info, IMG_SIZE, order) eq(image.features(), features) eq(image.stripe_unit(), stripe_unit) eq(image.stripe_count(), stripe_count) image.close() RBD().remove(ioctx, image_name) @require_new_format() def test_id(self): assert_not_equal(b'', self.image.id()) def test_block_name_prefix(self): assert_not_equal(b'', self.image.block_name_prefix()) def test_invalidate_cache(self): self.image.write(b'abc', 0) eq(b'abc', self.image.read(0, 3)) self.image.invalidate_cache() eq(b'abc', self.image.read(0, 3)) def test_stat(self): info = self.image.stat() check_stat(info, IMG_SIZE, IMG_ORDER) def test_flags(self): flags = self.image.flags() eq(0, flags) def test_image_auto_close(self): image = Image(ioctx, image_name) def test_write(self): data = rand_data(256) self.image.write(data, 0) def test_write_with_fadvise_flags(self): data = rand_data(256) self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_DONTNEED) self.image.write(data, 0, LIBRADOS_OP_FLAG_FADVISE_NOCACHE) def test_read(self): data = self.image.read(0, 20) eq(data, b'\0' * 20) def test_read_with_fadvise_flags(self): data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_DONTNEED) eq(data, b'\0' * 20) data = self.image.read(0, 20, LIBRADOS_OP_FLAG_FADVISE_RANDOM) eq(data, b'\0' * 20) def test_large_write(self): data = rand_data(IMG_SIZE) self.image.write(data, 0) def test_large_read(self): data = self.image.read(0, IMG_SIZE) eq(data, b'\0' * IMG_SIZE) def test_write_read(self): data = rand_data(256) offset = 50 self.image.write(data, offset) read = self.image.read(offset, 256) eq(data, read) def test_read_bad_offset(self): assert_raises(InvalidArgument, self.image.read, IMG_SIZE + 1, IMG_SIZE) def test_resize(self): new_size = IMG_SIZE * 2 self.image.resize(new_size) info = self.image.stat() check_stat(info, new_size, IMG_ORDER) def test_size(self): eq(IMG_SIZE, self.image.size()) self.image.create_snap('snap1') new_size = IMG_SIZE * 2 self.image.resize(new_size) eq(new_size, self.image.size()) self.image.create_snap('snap2') self.image.set_snap('snap2') eq(new_size, self.image.size()) self.image.set_snap('snap1') eq(IMG_SIZE, self.image.size()) self.image.set_snap(None) eq(new_size, self.image.size()) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_resize_down(self): new_size = IMG_SIZE // 2 data = rand_data(256) self.image.write(data, IMG_SIZE // 2); self.image.resize(new_size) self.image.resize(IMG_SIZE) read = self.image.read(IMG_SIZE // 2, 256) eq(b'\0' * 256, read) def test_resize_bytes(self): new_size = IMG_SIZE // 2 - 5 data = rand_data(256) self.image.write(data, IMG_SIZE // 2 - 10); self.image.resize(new_size) self.image.resize(IMG_SIZE) read = self.image.read(IMG_SIZE // 2 - 10, 5) eq(data[:5], read) read = self.image.read(IMG_SIZE // 2 - 5, 251) eq(b'\0' * 251, read) def _test_copy(self, features=None, order=None, stripe_unit=None, stripe_count=None): global ioctx data = rand_data(256) self.image.write(data, 256) image_name = get_temp_image_name() if features is None: self.image.copy(ioctx, image_name) elif order is None: self.image.copy(ioctx, image_name, features) elif stripe_unit is None: self.image.copy(ioctx, image_name, features, order) elif stripe_count is None: self.image.copy(ioctx, image_name, features, order, stripe_unit) else: self.image.copy(ioctx, image_name, features, order, stripe_unit, stripe_count) assert_raises(ImageExists, self.image.copy, ioctx, image_name) copy = Image(ioctx, image_name) copy_data = copy.read(256, 256) copy.close() self.rbd.remove(ioctx, image_name) eq(data, copy_data) def test_copy(self): self._test_copy() def test_copy2(self): self._test_copy(self.image.features(), self.image.stat()['order']) @require_features([RBD_FEATURE_STRIPINGV2]) def test_copy3(self): global features self._test_copy(features, self.image.stat()['order'], self.image.stripe_unit(), self.image.stripe_count()) def test_create_snap(self): global ioctx self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) at_snapshot = Image(ioctx, image_name, 'snap1') snap_data = at_snapshot.read(0, 256) at_snapshot.close() eq(snap_data, b'\0' * 256) self.image.remove_snap('snap1') def test_list_snaps(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()]) self.image.create_snap('snap2') eq(['snap1', 'snap2'], [snap['name'] for snap in self.image.list_snaps()]) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_list_snaps_iterator_auto_close(self): self.image.create_snap('snap1') self.image.list_snaps() self.image.remove_snap('snap1') def test_remove_snap(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()]) self.image.remove_snap('snap1') eq([], list(self.image.list_snaps())) def test_rename_snap(self): eq([], list(self.image.list_snaps())) self.image.create_snap('snap1') eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()]) self.image.rename_snap("snap1", "snap1-rename") eq(['snap1-rename'], [snap['name'] for snap in self.image.list_snaps()]) self.image.remove_snap('snap1-rename') eq([], list(self.image.list_snaps())) @require_features([RBD_FEATURE_LAYERING]) def test_protect_snap(self): self.image.create_snap('snap1') assert(not self.image.is_protected_snap('snap1')) self.image.protect_snap('snap1') assert(self.image.is_protected_snap('snap1')) assert_raises(ImageBusy, self.image.remove_snap, 'snap1') self.image.unprotect_snap('snap1') assert(not self.image.is_protected_snap('snap1')) self.image.remove_snap('snap1') assert_raises(ImageNotFound, self.image.unprotect_snap, 'snap1') assert_raises(ImageNotFound, self.image.is_protected_snap, 'snap1') def test_limit_snaps(self): self.image.set_snap_limit(2) eq(2, self.image.get_snap_limit()) self.image.create_snap('snap1') self.image.create_snap('snap2') assert_raises(DiskQuotaExceeded, self.image.create_snap, 'snap3') self.image.remove_snap_limit() self.image.create_snap('snap3') self.image.remove_snap('snap1') self.image.remove_snap('snap2') self.image.remove_snap('snap3') @require_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_remove_with_exclusive_lock(self): assert_raises(ImageBusy, remove_image) @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_remove_with_snap(self): self.image.create_snap('snap1') assert_raises(ImageHasSnapshots, remove_image) self.image.remove_snap('snap1') @blacklist_features([RBD_FEATURE_EXCLUSIVE_LOCK]) def test_remove_with_watcher(self): data = rand_data(256) self.image.write(data, 0) assert_raises(ImageBusy, remove_image) read = self.image.read(0, 256) eq(read, data) def test_rollback_to_snap(self): self.image.write(b'\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) self.image.remove_snap('snap1') def test_rollback_to_snap_sparse(self): self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.rollback_to_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) self.image.remove_snap('snap1') def test_rollback_with_resize(self): read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, data) new_size = IMG_SIZE * 2 self.image.resize(new_size) check_stat(self.image.stat(), new_size, IMG_ORDER) self.image.write(data, new_size - 256) self.image.create_snap('snap2') read = self.image.read(new_size - 256, 256) eq(read, data) self.image.rollback_to_snap('snap1') check_stat(self.image.stat(), IMG_SIZE, IMG_ORDER) assert_raises(InvalidArgument, self.image.read, new_size - 256, 256) self.image.rollback_to_snap('snap2') check_stat(self.image.stat(), new_size, IMG_ORDER) read = self.image.read(new_size - 256, 256) eq(read, data) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_set_snap(self): self.image.write(b'\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) self.image.remove_snap('snap1') def test_set_no_snap(self): self.image.write(b'\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) self.image.remove_snap('snap1') def test_set_snap_sparse(self): self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) self.image.remove_snap('snap1') def test_many_snaps(self): num_snaps = 200 for i in range(num_snaps): self.image.create_snap(str(i)) snaps = sorted(self.image.list_snaps(), key=lambda snap: int(snap['name'])) eq(len(snaps), num_snaps) for i, snap in enumerate(snaps): eq(snap['size'], IMG_SIZE) eq(snap['name'], str(i)) for i in range(num_snaps): self.image.remove_snap(str(i)) def test_set_snap_deleted(self): self.image.write(b'\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') self.image.remove_snap('snap1') assert_raises(ImageNotFound, self.image.read, 0, 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) def test_set_snap_recreated(self): self.image.write(b'\0' * 256, 0) self.image.create_snap('snap1') read = self.image.read(0, 256) eq(read, b'\0' * 256) data = rand_data(256) self.image.write(data, 0) read = self.image.read(0, 256) eq(read, data) self.image.set_snap('snap1') self.image.remove_snap('snap1') self.image.create_snap('snap1') assert_raises(ImageNotFound, self.image.read, 0, 256) self.image.set_snap(None) read = self.image.read(0, 256) eq(read, data) self.image.remove_snap('snap1') def test_lock_unlock(self): assert_raises(ImageNotFound, self.image.unlock, '') self.image.lock_exclusive('') assert_raises(ImageExists, self.image.lock_exclusive, '') assert_raises(ImageBusy, self.image.lock_exclusive, 'test') assert_raises(ImageExists, self.image.lock_shared, '', '') assert_raises(ImageBusy, self.image.lock_shared, 'foo', '') self.image.unlock('') def test_list_lockers(self): eq([], self.image.list_lockers()) self.image.lock_exclusive('test') lockers = self.image.list_lockers() eq(1, len(lockers['lockers'])) _, cookie, _ = lockers['lockers'][0] eq(cookie, 'test') eq('', lockers['tag']) assert lockers['exclusive'] self.image.unlock('test') eq([], self.image.list_lockers()) num_shared = 10 for i in range(num_shared): self.image.lock_shared(str(i), 'tag') lockers = self.image.list_lockers() eq('tag', lockers['tag']) assert not lockers['exclusive'] eq(num_shared, len(lockers['lockers'])) cookies = sorted(map(lambda x: x[1], lockers['lockers'])) for i in range(num_shared): eq(str(i), cookies[i]) self.image.unlock(str(i)) eq([], self.image.list_lockers()) def test_diff_iterate(self): check_diff(self.image, 0, IMG_SIZE, None, []) self.image.write(b'a' * 256, 0) check_diff(self.image, 0, IMG_SIZE, None, [(0, 256, True)]) self.image.write(b'b' * 256, 256) check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)]) self.image.discard(128, 256) check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)]) self.image.create_snap('snap1') self.image.discard(0, 1 << IMG_ORDER) self.image.create_snap('snap2') self.image.set_snap('snap2') check_diff(self.image, 0, IMG_SIZE, 'snap1', [(0, 512, False)]) self.image.remove_snap('snap1') self.image.remove_snap('snap2') def test_aio_read(self): # this is a list so that the local cb() can modify it retval = [None] def cb(_, buf): retval[0] = buf # test1: success case comp = self.image.aio_read(0, 20, cb) comp.wait_for_complete_and_cb() eq(retval[0], b'\0' * 20) eq(comp.get_return_value(), 20) eq(sys.getrefcount(comp), 2) # test2: error case retval[0] = 1 comp = self.image.aio_read(IMG_SIZE, 20, cb) comp.wait_for_complete_and_cb() eq(None, retval[0]) assert(comp.get_return_value() < 0) eq(sys.getrefcount(comp), 2) def test_aio_write(self): retval = [None] def cb(comp): retval[0] = comp.get_return_value() data = rand_data(256) comp = self.image.aio_write(data, 256, cb) comp.wait_for_complete_and_cb() eq(retval[0], 0) eq(comp.get_return_value(), 0) eq(sys.getrefcount(comp), 2) eq(self.image.read(256, 256), data) def test_aio_discard(self): retval = [None] def cb(comp): retval[0] = comp.get_return_value() data = rand_data(256) self.image.write(data, 0) comp = self.image.aio_discard(0, 256, cb) comp.wait_for_complete_and_cb() eq(retval[0], 0) eq(comp.get_return_value(), 0) eq(sys.getrefcount(comp), 2) eq(self.image.read(256, 256), b'\0' * 256) def test_aio_flush(self): retval = [None] def cb(comp): retval[0] = comp.get_return_value() comp = self.image.aio_flush(cb) comp.wait_for_complete_and_cb() eq(retval[0], 0) eq(sys.getrefcount(comp), 2)
def slave(ioctx): print("starting slave") while True: try: with Image(ioctx, CLONE_IMG_NAME) as image: if (image.list_lockers() != [] and image.read(IMG_SIZE - 1, 1) == b'1'): break except Exception: pass print("detected master") print("rename") RBD().rename(ioctx, CLONE_IMG_NAME, CLONE_IMG_RENAME); assert(not image.is_exclusive_lock_owner()) with Image(ioctx, CLONE_IMG_RENAME) as image: print("flatten") image.flatten() assert(not image.is_exclusive_lock_owner()) print("resize") image.resize(IMG_SIZE // 2) assert(not image.is_exclusive_lock_owner()) assert(image.stat()['size'] == IMG_SIZE // 2) print("create_snap") image.create_snap('snap1') assert(not image.is_exclusive_lock_owner()) assert(any(snap['name'] == 'snap1' for snap in image.list_snaps())) print("protect_snap") image.protect_snap('snap1') assert(not image.is_exclusive_lock_owner()) assert(image.is_protected_snap('snap1')) print("unprotect_snap") image.unprotect_snap('snap1') assert(not image.is_exclusive_lock_owner()) assert(not image.is_protected_snap('snap1')) print("rename_snap") image.rename_snap('snap1', 'snap1-new') assert(not image.is_exclusive_lock_owner()) assert(any(snap['name'] == 'snap1-new' for snap in image.list_snaps())) print("remove_snap") image.remove_snap('snap1-new') assert(not image.is_exclusive_lock_owner()) assert(list(image.list_snaps()) == []) print("rebuild object map") image.update_features(RBD_FEATURE_OBJECT_MAP | RBD_FEATURE_FAST_DIFF, False) image.update_features(RBD_FEATURE_OBJECT_MAP, True) assert((image.flags() & RBD_FLAG_OBJECT_MAP_INVALID) != 0) image.rebuild_object_map() assert(not image.is_exclusive_lock_owner()) assert((image.flags() & RBD_FLAG_OBJECT_MAP_INVALID) == 0) print("write") data = os.urandom(512) image.write(data, 0) assert(image.is_exclusive_lock_owner()) print("finished")