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 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_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_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 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 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 get_rbd_stat(self, rbd_name): try: image = Image(self.ioctx, rbd_name) stat = image.stat() image.close() self.log.info(("stat of rbd image %s" % rbd_name, stat)) return stat except Exception as e: self.log.error("unable to get stat of rbd image (%s). %s" % (rbd_name, e)) return False
def get_rbd_features(self, rbd_name): try: image = Image(self.ioctx, rbd_name) feature = image.features() self.log.info("feature of rbd image %s = %s" % (rbd_name, feature)) image.close() return feature except Exception as e: self.log.error("unable to get feature of rbd image (%s). %s" % (rbd_name, e)) return False
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, IMG_NAME, 'snap1') snap_data = at_snapshot.read(0, 256) at_snapshot.close() eq(snap_data, '\0' * 256)
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_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_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 rbd_lock(dbg, cluster, pool, name): log.debug( "%s: xcpng.librbd.rbd_utils.rbd_lock: Cluster ID: %s Pool: %s Name: %s" % (dbg, cluster.get_fsid(), pool, name)) ioctx = cluster.open_ioctx(pool) image = Image(ioctx, name) try: image.lock_exclusive('xapi-xcpng-lock') return ioctx, image except ImageBusy or ImageExists as e: log.error( "%s: xcpng.librbd.rbd_utils.rbd_lock: Failed to acquire exclusive lock: Cluster ID: %s Pool: %s Name: %s" % (dbg, cluster.get_fsid(), pool, name)) image.close() ioctx.close() raise Exception(e)
def get_rbd_size(self, rbd_name): try: if self.ioctx == None: self.log.error("Pool ioctx is not opened yet.") return False image = Image(self.ioctx, rbd_name) size = image.size() self.log.debug("Size of RBD '%s/%s' in Ceph cluster is %s." % (self.pool_name, rbd_name, size)) return size except Exception as e: self.log.error("Fail to get RBD size. %s" % e) return False finally: image.close()
def get_rbd_size(self, rbd_name): ''' get size of rbd or rbd snapshot ''' try: image = Image(self.ioctx, rbd_name) size = image.size() image.close() if size is False: return False self.log.info("%s has image size %s bytes in pool %s." % (str(rbd_name), size, self.pool_name)) return int(size) except Exception as e: self.log.error("unable to get size of rbd image (%s). %s" % (rbd_name, e)) return False
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 get_snap_info_list(self, rbd_name): try: image = Image(self.ioctx, rbd_name) snaps = image.list_snaps() snap_list = [] for snap in snaps: snap_list.append({ 'id': snap['id'], 'size': snap['size'], 'name': snap['name'] }) self.log.debug( "Snapshot list of RBD '%s/%s' in Ceph cluster:" % (rbd_name, self.pool_name), snap_list) sorted_snap_list = sorted(snap_list, key=lambda k: k['id']) return sorted_snap_list except Exception as e: self.log.error("Fail to get snapshot. %s" % e) return False finally: image.close()
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")
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() 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_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) 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())) @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')
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')
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')
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, 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))
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 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')
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)
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 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 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 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') # 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 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 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 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())
#!/usr/bin/env python # -*- coding: utf-8 -*- import rados from rbd import RBD, Image cluster = rados.Rados(conffile='') cluster.connect() ioctx = cluster.open_ioctx("rbd") rbd = RBD() image = Image(ioctx, "rbd0") print("zzzzzzzzzzz") size = image.size() print("xxxxxxxxxxx") image.close() print size