]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
hammer: rbd snap rollback: restore the link to parent 8535/head
authorAlexey Sheplyakov <asheplyakov@mirantis.com>
Mon, 11 Apr 2016 11:16:48 +0000 (14:16 +0300)
committerAlexey Sheplyakov <asheplyakov@mirantis.com>
Thu, 14 Apr 2016 09:20:18 +0000 (12:20 +0300)
So snapshot, flatten, rollback of a cloned image does not loose any data

Fixes: #14512
Signed-off-by: Alexey Sheplyakov <asheplyakov@mirantis.com>
src/librbd/internal.cc
src/librbd/parent_types.h
src/test/pybind/test_rbd.py

index 8c6e1b8bd3b3cf2166deb05b1ba043e7c67babf7..f4b110eea8ce0c37c2780e1429cfd527f88e7882 100644 (file)
@@ -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)
   {
@@ -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;
   }
 
index 4dcc452962f951cd25a25a8d1c7aebf607952930..de7e6129a7307be3fa1eec0a4bec95a0797becd0 100644 (file)
@@ -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);
     }
   };
@@ -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);
+    }
   };
 }
 
index 4be09085b5c22a779a40a7f9e435bdc9df23f62f..181a2ac82e2730685c98758bc58c281fe8ca79ac 100644 (file)
@@ -1048,3 +1048,96 @@ class TestExclusiveLock(object):
             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)