From: Chandrika Srinivasan <chandrika.srinivasan@citrix.com>
Date: Mon, 6 Nov 2017 09:56:23 +0000
CA-270011: Handle snapshotting a snapshot in a CBT chain

Snapshotting a snapshot is rare but possible. When this happens on a
snapshot that is part of the CBT chain, insert the new child of the
snapshot into the CBT chain.

We are trying to solve the case of snapshotting a snapshot in a CBT chain.
Consider the following CBT chain P1<->P0<->V->NULL.
Under normal circumstances, V is snapshotted and we do the following

1) Rename V to S (snapshot) 
2) Find parent of S (P0 in this case) and create P0<->S links
3) Create new V (child of V is set to NULL, since this is usually the leaf) 
4) Create S<->V links
5) Final chain looks like P1<->P0<->S<->V->NULL

This is very specific to a leaf being snapshotted and this needs to be
generalised for any VDI in the chain being snapshotted. Consider the scenario
where P0 is being snapshotted. The process needs to change to

1) Rename P0 to S (snapshot) 
2) Find parent of S (P1 in this case) and create P1<->S links
3) Create new P0 (child of P0 is still set to NULL at the time of creation)
4) Detect if the original P0 (now S) had a child (V in this case)
5) Link P0 to V
6) Create S<->P0 links
7) Final chain looks like P1<->S<->P0<->V->NULL

Signed-off-by: Chandrika Srinivasan <chandrika.srinivasan@citrix.com>
diff --git a/drivers/VDI.py b/drivers/VDI.py
index 0a23c55..d58145a 100755
--- a/drivers/VDI.py
+++ b/drivers/VDI.py
@@ -781,11 +781,11 @@ class VDI(object):
     def _cbt_snapshot(self, snapshot_uuid, consistency_state):
         """ CBT snapshot"""
         snap_logpath = self._get_cbt_logpath(snapshot_uuid)
-        leaf_logpath = self._get_cbt_logpath(self.uuid)
+        vdi_logpath = self._get_cbt_logpath(self.uuid)
 
-        # Rename leaf leaf.cbtlog to snapshot.cbtlog
+        # Rename vdi vdi.cbtlog to snapshot.cbtlog
         # and mark it consistent
-        self._rename(leaf_logpath, snap_logpath)
+        self._rename(vdi_logpath, snap_logpath)
         self._cbt_op(snapshot_uuid, cbtutil.set_cbt_consistency,
                      snap_logpath, True)
 
@@ -803,15 +803,21 @@ class VDI(object):
         try:
             # Ensure enough space for metadata file
             self._ensure_cbt_space()
-            # Create new leaf.cbtlog
+            # Create new vdi.cbtlog
             self._create_cbt_log()
-            # Set previous leaf node consistency status
+            # Set previous vdi node consistency status
             if not consistency_state:
                 self._cbt_op(self.uuid, cbtutil.set_cbt_consistency,
-                             leaf_logpath, consistency_state)
+                             vdi_logpath, consistency_state)
             # Set relationship pointers
+            # Save the child of the VDI just snapshotted
+            curr_child_uuid = self._cbt_op(snapshot_uuid, cbtutil.get_cbt_child,
+                                           snap_logpath)
             self._cbt_op(self.uuid, cbtutil.set_cbt_parent,
-                         leaf_logpath, snapshot_uuid)
+                         vdi_logpath, snapshot_uuid)
+            # Set child of new vdi to existing child of snapshotted VDI
+            self._cbt_op(self.uuid, cbtutil.set_cbt_child,
+                         vdi_logpath, curr_child_uuid)
             self._cbt_op(snapshot_uuid, cbtutil.set_cbt_child,
                          snap_logpath, self.uuid)
         except Exception as ex:
