Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hammer: rbd: snap rollback: restore the link to parent #8535

Merged
1 commit merged into from May 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/librbd/internal.cc
Expand Up @@ -413,6 +413,48 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) {
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)
{
Expand Down Expand Up @@ -444,6 +486,17 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) {
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;
}

Expand Down
10 changes: 8 additions & 2 deletions src/librbd/parent_types.h
Expand Up @@ -14,12 +14,12 @@ namespace librbd {
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);
}
};
Expand All @@ -28,6 +28,12 @@ namespace librbd {
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);
}
};
}

Expand Down
93 changes: 93 additions & 0 deletions src/test/pybind/test_rbd.py
Expand Up @@ -1048,3 +1048,96 @@ def test_follower_write(self):
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)