rados_completion->release();
}
+ int rollback_parent(ImageCtx *ictx, uint64_t snap_id)
+ {
+ assert(ictx);
+ assert(ictx->parent_lock.is_locked());
+ assert(ictx->snap_lock.is_locked());
+
+ CephContext *cct = ictx->cct;
+ int r = 0;
+ std::map<librados::snap_t, SnapInfo>::const_iterator it = ictx->snap_info.find(snap_id);
+ if (it == ictx->snap_info.end()) {
+ ldout(cct, 10) << __func__ << ": no such snapshot: " << snap_id << dendl;
+ return -ENOENT;
+ }
+ const SnapInfo& snap_info(it->second);
+ if (ictx->parent_md == snap_info.parent) {
+ ldout(cct, 20) << __func__ << ": nop: head and snapshot have the same parent" << dendl;
+ return 0;
+ }
+ if (ictx->parent_md.spec.pool_id != -1) {
+ // remove the old parent link first, otherwise cls_client::set_parent
+ // will fail with -EEXISTS
+ ldout(cct, 20) << __func__ << ": removing the old parent link" << dendl;
+ r = cls_client::remove_parent(&ictx->md_ctx, ictx->header_oid);
+ if (r < 0) {
+ ldout(cct, 10) << __func__ << ": failed to remove parent link: "
+ << cpp_strerror(r) << dendl;
+ return r;
+ }
+ }
+ if (snap_info.parent.spec.pool_id != -1) {
+ ldout(cct, 20) << __func__ << ": updating the parent link" << dendl;
+ r = cls_client::set_parent(&ictx->md_ctx, ictx->header_oid,
+ snap_info.parent.spec, snap_info.parent.overlap);
+ if (r < 0) {
+ ldout(cct, 10) << __func__ << ": failed to set parent link: "
+ << cpp_strerror(r) << dendl;
+ return r;
+ }
+ }
+ return 0;
+ }
+
int rollback_image(ImageCtx *ictx, uint64_t snap_id,
ProgressContext& prog_ctx)
{
RWLock::WLocker l(ictx->snap_lock);
ictx->object_map.rollback(snap_id);
}
+
+ {
+ RWLock::WLocker snap_locker(ictx->snap_lock);
+ RWLock::WLocker parent_locker(ictx->parent_lock);
+ r = rollback_parent(ictx, snap_id);
+ if (r < 0) {
+ ldout(cct, 10) << __func__ << ": failed to rollback the parent link: "
+ << cpp_strerror(r) << dendl;
+ return r;
+ }
+ }
return 0;
}
parent_spec() : pool_id(-1), snap_id(CEPH_NOSNAP) {}
parent_spec(uint64_t pool_id, string image_id, snapid_t snap_id) :
pool_id(pool_id), image_id(image_id), snap_id(snap_id) {}
- bool operator==(const parent_spec &other) {
+ bool operator==(const parent_spec &other) const {
return ((this->pool_id == other.pool_id) &&
(this->image_id == other.image_id) &&
(this->snap_id == other.snap_id));
}
- bool operator!=(const parent_spec &other) {
+ bool operator!=(const parent_spec &other) const {
return !(*this == other);
}
};
parent_spec spec;
uint64_t overlap;
parent_info() : overlap(0) {}
+ bool operator==(const parent_info &other) const {
+ return (spec == other.spec) && (overlap == other.overlap);
+ }
+ bool operator!=(const parent_info &other) const {
+ return (spec != other.spec) || (overlap != other.overlap);
+ }
};
}
for offset in [0, IMG_SIZE / 2]:
read = image2.read(offset, 256)
eq(data, read)
+
+
+class TestCloneRollback(object):
+
+ @require_features([RBD_FEATURE_LAYERING])
+ def setUp(self):
+ self.rbd = RBD()
+ create_image()
+ self.clone_name = image_name + '_cloned'
+ with Image(ioctx, image_name) as image1:
+ image1.write('FOOBAR', 0)
+ image1.create_snap('FOOBAR')
+ image1.protect_snap('FOOBAR')
+ RBD().clone(ioctx, image_name, 'FOOBAR',
+ ioctx, self.clone_name,
+ features=RBD_FEATURE_LAYERING)
+ with Image(ioctx, self.clone_name) as clone:
+ clone.write('OOPS', IMG_SIZE / 2)
+ clone.create_snap('OOPS')
+
+ def tearDown(self):
+ global ioctx
+ with Image(ioctx, self.clone_name) as clone:
+ clone.remove_snap('OOPS')
+ try:
+ clone.remove_snap('FLATTENED')
+ except:
+ pass
+ RBD().remove(ioctx, self.clone_name)
+ with Image(ioctx, image_name) as image1:
+ image1.unprotect_snap('FOOBAR')
+ image1.remove_snap('FOOBAR')
+ remove_image()
+
+ def test_rollback_flattened_to_parented(self):
+ # target snapshot has a parent, and head does not
+ expected_head, expected_tail = 'FOOBAR', 'OOPS'
+ with Image(ioctx, self.clone_name) as clone:
+ clone.write('HEHE', IMG_SIZE / 2)
+ clone.flatten()
+ clone.rollback_to_snap('OOPS')
+ head = clone.read(0, len(expected_head))
+ tail = clone.read(IMG_SIZE / 2, len(expected_tail))
+ eq(head, expected_head)
+ eq(tail, expected_tail)
+ _, image, snap = clone.parent_info()
+ eq(image, image_name)
+ eq(snap, 'FOOBAR')
+
+ def test_rollback_parented_to_flattened(self):
+ expected_head, expected_tail = 'FOOBAR', 'HEHE'
+ with Image(ioctx, self.clone_name) as clone:
+ clone.write(expected_tail, IMG_SIZE / 2)
+ clone.flatten()
+ clone.create_snap('FLATTENED')
+ clone.rollback_to_snap('OOPS')
+ # head has a parent, and the target snapshot does not
+ _, image, snap = clone.parent_info()
+ eq(image, image_name)
+ eq(snap, 'FOOBAR')
+ clone.rollback_to_snap('FLATTENED')
+ head = clone.read(0, len(expected_head))
+ tail = clone.read(IMG_SIZE / 2, len(expected_tail))
+ assert_raises(ImageNotFound, clone.parent_info)
+ eq(head, expected_head)
+ eq(tail, expected_tail)
+
+ def test_rollback_same_parent(self):
+ expected_head, expected_tail = 'FOOBAR', 'OOPS'
+ with Image(ioctx, self.clone_name) as clone:
+ clone.write('HEHE', IMG_SIZE / 2)
+ clone.rollback_to_snap('OOPS')
+ head = clone.read(0, len(expected_head))
+ tail = clone.read(IMG_SIZE / 2, len(expected_tail))
+ _, image, snap = clone.parent_info()
+ eq(head, expected_head)
+ eq(tail, expected_tail)
+ eq(image, image_name)
+ eq(snap, 'FOOBAR')
+
+ def test_rollback_same_parent_shrinked(self):
+ expected_head, expected_tail = 'FOOBAR', 'OOPS'
+ with Image(ioctx, self.clone_name) as clone:
+ clone.resize(IMG_SIZE / 2)
+ clone.write('HEHE', 0)
+ clone.rollback_to_snap('OOPS')
+ _, image, snap = clone.parent_info()
+ head = clone.read(0, len(expected_head))
+ tail = clone.read(IMG_SIZE / 2, len(expected_tail))
+ eq(image, image_name)
+ eq(snap, 'FOOBAR')
+ eq(head, expected_head)
+ eq(tail, expected_tail)