@@ -257,6 +257,18 @@ def _update(self, sr_uuid, virt_alloc_delta):
257257 self .physical_utilisation = self ._getutilisation ()
258258 self ._db_update ()
259259
260+ def get_children_of (self , uuid : str ) -> List [str ]:
261+ if not self .vdis :
262+ self ._loadvdis ()
263+
264+ children = []
265+
266+ for vdi in self .vdis .values ():
267+ if vdi .parent == uuid :
268+ children .append (vdi .uuid )
269+
270+ return children
271+
260272 @override
261273 def content_type (self , sr_uuid ) -> str :
262274 return super (FileSR , self ).content_type (sr_uuid )
@@ -755,54 +767,32 @@ def _tap_pause(self, secondary=None):
755767 finally :
756768 blktap2 .VDI .tap_unpause (self .session , self .sr .uuid , self .uuid , secondary )
757769
758-
759770 @override
760- def revert (self , sr_uuid , src_uuid , dest_uuid ):
761- util .SMlog ("---------------------------------------------------------------->" )
762- util .SMlog (f"in: { src_uuid } - { self .uuid } " )
763-
764- if self .label == "base copy" :
765- raise xs_errors .XenError (f"{ self .uuid } is a base copy." )
766-
767- dest = VDI .VDI .from_uuid (self .sr .session , dest_uuid )
768- util .SMlog (f"out: { dest_uuid } - { dest .uuid } " )
769-
770- if self .vdi_type != dest .vdi_type :
771- raise xs_errors .XenError (f"{ self .uuid } and { dest .uuid } has incompatible types { self .vdi_type } != { dest .vdi_type } " )
772-
773-
771+ def _do_revert (self , dest : "FileVDI" , cbtlog : Optional [str ] = None ):
772+ # Sanity checks
774773 if not self ._checkpath (self .path ):
775774 raise xs_errors .XenError (f"Could not find { self .path } " )
776775
777776 if not dest ._checkpath (dest .path ):
778777 raise xs_errors .XenError (f"Could not find { dest .path } " )
779778
780- def get_child_of (sr : FileSR , uuid : str , exclude : Optional [List [str ]] = None ) -> Optional [str ]:
781- if not exclude :
782- exclude = []
783-
784- excluded = set (exclude )
785- sr ._loadvdis ()
786-
787- children = []
788-
789- for vdi in sr .vdis .values ():
790- if vdi .uuid == uuid :
791- continue
792- if vdi .parent == uuid and vdi .uuid not in excluded :
793- children .append (vdi .uuid )
794-
795- if len (children ) != 1 :
796- return None
797-
798- return children [0 ]
779+ self ._ensure_not_max_depth ()
799780
800- common_parent_uuid = self .parent if self .parent == dest .parent else get_child_of (self .sr , self .parent , [self .uuid ])
801- if not common_parent_uuid :
802- raise xs_errors .XenError (f"{ self .uuid } and { dest .uuid } aren't on the same snapshot tree" )
781+ # Get first common parent
782+ if self .parent == dest .parent :
783+ downward_child = None
784+ else :
785+ children = [child for child in self .sr .get_children_of (self .parent ) if child != self .uuid ]
786+ if not children :
787+ downward_child = None
788+ else :
789+ downward_child = VDI .VDI .from_uuid (self .sr .session , children [0 ])
803790
804- self ._ensure_not_max_depth ()
791+ with self ._tap_pause (), dest ._tap_pause ():
792+ self ._revert (dest , downward_child , cbtlog )
805793
794+ def _revert (self , dest : "FileVDI" , downward_child : Optional ["FileVDI" ], cbtlog : Optional [str ]):
795+ """This assumes that self and dest VDIs has been paused"""
806796 dest_tmp_uuid = util .gen_uuid () # Will be replaced by dest.uuid at the end
807797 dest_tmp_path = os .path .join (dest .sr .path , "%s%s" % (dest_tmp_uuid , VDI_TYPE_TO_EXTENSION [dest .vdi_type ]))
808798
@@ -817,90 +807,74 @@ def update_vdi_from_file(vdi: FileVDI, path: str):
817807 if vdi .parent :
818808 vdi .sm_config ['vhd-parent' ] = vdi .parent
819809
820- with self ._tap_pause (), dest ._tap_pause ():
821- # try:
822- util .fistpoint .activate_custom_fn (
823- "FileSR_fail_snap1" ,
824- self .__fist_enospace )
825- util .ioretry (lambda : self ._snap (dest_tmp_path , src_parent .path , False ))
826- self .cowutil .setHidden (dest_tmp_path , False )
827-
828- if common_parent_uuid != dest .parent :
829- # Create a new parent VDI based on src parent
830- src_parent_clone_uuid = util .gen_uuid ()
831- src_parent_clone_path = os .path .join (
832- dest .sr .path ,
833- "%s%s" % (src_parent_clone_uuid , VDI_TYPE_TO_EXTENSION [dest .vdi_type ])
834- )
810+ # try:
811+ util .fistpoint .activate_custom_fn (
812+ "FileSR_fail_snap1" ,
813+ self .__fist_enospace )
814+ util .ioretry (lambda : self ._snap (dest_tmp_path , src_parent .path , False ))
815+ self .cowutil .setHidden (dest_tmp_path , False )
816+
817+ if downward_child :
818+ # Create a new parent VDI based on src parent and attach the rest of the snapshoot tree to it
819+ src_parent_clone_uuid = util .gen_uuid ()
820+ src_parent_clone_path = os .path .join (
821+ dest .sr .path ,
822+ "%s%s" % (src_parent_clone_uuid , VDI_TYPE_TO_EXTENSION [dest .vdi_type ])
823+ )
835824
836- common_parent = VDI .VDI .from_uuid (self .sr .session , common_parent_uuid )
837- common_parent .sm_config = common_parent .session .xenapi .VDI .get_sm_config (common_parent .sr .srcmd .params ['vdi_ref' ])
825+ downward_child .sm_config = downward_child .session .xenapi .VDI .get_sm_config (downward_child .sr .srcmd .params ['vdi_ref' ])
838826
839- util .fistpoint .activate_custom_fn (
840- "FileSR_fail_snap2" ,
841- self .__fist_enospace )
842- util .ioretry (lambda : self ._snap (src_parent_clone_path , src_parent .path , False ))
843- self .cowutil .setHidden (src_parent_clone_path , False )
844-
845- is_raw = self .VDI_TYPE == VdiType .RAW
846- self .cowutil .setParent (src_parent_clone_path , src_parent .path , is_raw )
847- self .cowutil .setParent (common_parent .path , src_parent_clone_path , is_raw )
848- self .cowutil .setParent (self .path , src_parent_clone_path , is_raw )
849-
850- # Introduce new readonly vdi to db
851- src_parent_clone = VDI .VDI (self .sr , src_parent_clone_uuid )
852-
853- # FileVDI emulation for update_vdi_from_file
854- src_parent_clone .path = src_parent_clone_path
855- src_parent_clone .parent = src_parent .uuid
856- src_parent_clone .cowutil = self .cowutil
857-
858- src_parent_clone .label = "base copy"
859- src_parent_clone .read_only = True
860- src_parent_clone .location = src_parent_clone_uuid
861- src_parent_clone .sm_config = {}
862- # TODO: fix the raw snapshot case
863- src_parent_clone .sm_config ["image-format" ] = getImageStringFromVdiType (self .vdi_type )
864- if "key_hash" in common_parent .sm_config :
865- src_parent_clone .sm_config ['key_hash' ] = common_parent .sm_config ['key_hash' ]
866- update_vdi_from_file (src_parent_clone , src_parent_clone_uuid )
867-
868- self .sm_config = self .session .xenapi .VDI .get_sm_config (self .sr .srcmd .params ['vdi_ref' ])
869- update_vdi_from_file (self , self .path )
827+ util .fistpoint .activate_custom_fn (
828+ "FileSR_fail_snap2" ,
829+ self .__fist_enospace )
830+ util .ioretry (lambda : self ._snap (src_parent_clone_path , src_parent .path , False ))
831+ self .cowutil .setHidden (src_parent_clone_path , False )
870832
871- update_vdi_from_file (common_parent , common_parent .path )
833+ is_raw = self .VDI_TYPE == VdiType .RAW
834+ self .cowutil .setParent (src_parent_clone_path , src_parent .path , is_raw )
835+ self .cowutil .setParent (downward_child .path , src_parent_clone_path , is_raw )
836+ self .cowutil .setParent (self .path , src_parent_clone_path , is_raw )
872837
873- src_parent_clone ._db_introduce ()
838+ # Introduce new readonly vdi to db
839+ src_parent_clone = VDI .VDI (self .sr , src_parent_clone_uuid )
874840
875- common_parent ._db_update ()
841+ # FileVDI emulation for update_vdi_from_file
842+ src_parent_clone .path = src_parent_clone_path
843+ src_parent_clone .parent = src_parent .uuid
844+ src_parent_clone .cowutil = self .cowutil
876845
877- self ._db_update ()
846+ src_parent_clone .label = "base copy"
847+ src_parent_clone .read_only = True
848+ src_parent_clone .location = src_parent_clone_uuid
849+ src_parent_clone .sm_config = {}
850+ # TODO: fix the raw snapshot case
851+ src_parent_clone .sm_config ["image-format" ] = getImageStringFromVdiType (self .vdi_type )
852+ if "key_hash" in downward_child .sm_config :
853+ src_parent_clone .sm_config ['key_hash' ] = downward_child .sm_config ['key_hash' ]
854+ update_vdi_from_file (src_parent_clone , src_parent_clone_uuid )
878855
879- self .cowutil .setHidden (src_parent_clone_path , True )
856+ self .sm_config = self .session .xenapi .VDI .get_sm_config (self .sr .srcmd .params ['vdi_ref' ])
857+ update_vdi_from_file (self , self .path )
880858
881- util .ioretry (lambda : self ._rename (dest_tmp_path , dest .path ),
882- errlist = [errno .EIO , errno .EACCES ])
859+ update_vdi_from_file (downward_child , downward_child .path )
883860
884- dest .sm_config = dest .session .xenapi .VDI .get_sm_config (dest .sr .srcmd .params ['vdi_ref' ])
885- update_vdi_from_file (dest , dest .path )
861+ src_parent_clone ._db_introduce ()
886862
887- dest ._db_update ()
863+ downward_child ._db_update ()
888864
889- return
865+ self . _db_update ()
890866
867+ self .cowutil .setHidden (src_parent_clone_path , True )
891868
892- # except util.CommandException as inst:
893- # # TODO: cleanup
894- # # XXX: it might be too late if the base disk has been marked as deleted!
895- # # self._clonecleanup(src, dst, newsrc)
896- # # util.end_log_entry(self.sr.path, self.path, ["error"])
897- # # raise xs_errors.XenError('VDIClone',
898- # # opterr='VDI clone failed error %d' % inst.code)
899- # pass
869+ util .ioretry (lambda : self ._rename (dest_tmp_path , dest .path ),
870+ errlist = [errno .EIO , errno .EACCES ])
900871
901- util . SMlog ( "<---------------------------------------------------------------" )
902- raise xs_errors . XenError ( 'Heloiastrnie' )
872+ dest . sm_config = dest . session . xenapi . VDI . get_sm_config ( dest . sr . srcmd . params [ 'vdi_ref' ] )
873+ update_vdi_from_file ( dest , dest . path )
903874
875+ dest ._db_update ()
876+ # except:
877+ #cleanup
904878
905879 @override
906880 def compose (self , sr_uuid , vdi1 , vdi2 ) -> None :
0 commit comments