@@ -757,7 +757,7 @@ def getTreeHeight(self):
757
757
758
758
return maxChildHeight + 1
759
759
760
- def getAllLeaves (self ):
760
+ def getAllLeaves (self ) -> List [ "VDI" ] :
761
761
"Get all leaf nodes in the subtree rooted at self"
762
762
if len (self .children ) == 0 :
763
763
return [self ]
@@ -828,6 +828,49 @@ def _clear(self):
828
828
def _clearRef (self ):
829
829
self ._vdiRef = None
830
830
831
+ def _call_plugin_coalesce (self , hostRef ):
832
+ args = {"path" : self .path , "vdi_type" : self .vdi_type }
833
+ self .sr .xapi .session .xenapi .host .call_plugin ( \
834
+ hostRef , XAPI .PLUGIN_ON_SLAVE , "commit_tapdisk" , args )
835
+
836
+ def _doCoalesceOnHost (self , hostRef ):
837
+ self .validate ()
838
+ self .parent .validate (True )
839
+ self .parent ._increaseSizeVirt (self .sizeVirt )
840
+ self .sr ._updateSlavesOnResize (self .parent )
841
+ #TODO: We might need to make the LV RW on the slave directly for coalesce?
842
+
843
+ def abortTest ():
844
+ file = self .sr ._gc_running_file (self )
845
+ try :
846
+ with open (file , "r" ) as f :
847
+ if not f .read ():
848
+ return True
849
+ except OSError as e :
850
+ if e .errno == errno .ENOENT :
851
+ util .SMlog ("File {} does not exist" .format (file ))
852
+ else :
853
+ util .SMlog ("IOError: {}" .format (e ))
854
+ return True
855
+ return False
856
+
857
+ Util .runAbortable (lambda : self ._call_plugin_coalesce (hostRef ),
858
+ None , self .sr .uuid , abortTest , VDI .POLL_INTERVAL , 0 )
859
+
860
+ self .parent .validate (True )
861
+ #self._verifyContents(0)
862
+ self .parent .updateBlockInfo ()
863
+
864
+ def _isOpenOnHosts (self ) -> Optional [str ]:
865
+ for pbdRecord in self .sr .xapi .getAttachedPBDs ():
866
+ hostRef = pbdRecord ["host" ]
867
+ args = {"path" : self .path }
868
+ is_openers = util .strtobool (self .sr .xapi .session .xenapi .host .call_plugin ( \
869
+ hostRef , XAPI .PLUGIN_ON_SLAVE , "is_openers" , args ))
870
+ if is_openers :
871
+ return hostRef
872
+ return None
873
+
831
874
def _doCoalesce (self ) -> None :
832
875
"""Coalesce self onto parent. Only perform the actual coalescing of
833
876
an image, but not the subsequent relinking. We'll do that as the next step,
@@ -914,7 +957,7 @@ def coalesce(self) -> int:
914
957
return self .cowutil .coalesce (self .path )
915
958
916
959
@staticmethod
917
- def _doCoalesceCowImage (vdi ):
960
+ def _doCoalesceCowImage (vdi : "VDI" ):
918
961
try :
919
962
startTime = time .time ()
920
963
allocated_size = vdi .getAllocatedSize ()
@@ -2383,7 +2426,17 @@ def cleanupJournals(self, dryRun=False):
2383
2426
def cleanupCache (self , maxAge = - 1 ) -> int :
2384
2427
return 0
2385
2428
2386
- def _coalesce (self , vdi ):
2429
+ def _hasLeavesAttachedOn (self , vdi : VDI ):
2430
+ leaves = vdi .getAllLeaves ()
2431
+ leaves_vdi = [leaf .uuid for leaf in leaves ]
2432
+ return util .get_hosts_attached_on (self .xapi .session , leaves_vdi )
2433
+
2434
+ def _gc_running_file (self , vdi ):
2435
+ run_file = "gc_running_{}" .format (vdi .uuid )
2436
+ return os .path .join (NON_PERSISTENT_DIR , str (self .uuid ), run_file )
2437
+
2438
+ def _coalesce (self , vdi : VDI ):
2439
+ skipRelink = False
2387
2440
if self .journaler .get (vdi .JRN_RELINK , vdi .uuid ):
2388
2441
# this means we had done the actual coalescing already and just
2389
2442
# need to finish relinking and/or refreshing the children
@@ -2393,8 +2446,35 @@ def _coalesce(self, vdi):
2393
2446
# order to decide whether to abort the coalesce. We remove the
2394
2447
# journal as soon as the COW coalesce step is done, because we
2395
2448
# don't expect the rest of the process to take long
2449
+
2450
+ #TODO: Create `gc_running` in `/run/nonpersistent/sm/<sr uuid>/`
2451
+ if os .path .exists (self ._gc_running_file (vdi )):
2452
+ util .SMlog ("gc_running already exist for {}. Ignoring..." .format (self .uuid ))
2453
+
2454
+ with open (self ._gc_running_file (vdi ), "w" ) as f :
2455
+ f .write ("1" )
2456
+
2396
2457
self .journaler .create (vdi .JRN_COALESCE , vdi .uuid , "1" )
2397
- vdi ._doCoalesce ()
2458
+ host_refs = self ._hasLeavesAttachedOn (vdi )
2459
+ #TODO: this check of multiple host_refs should be done earlier in `is_coalesceable` to avoid stopping this late every time
2460
+ if len (host_refs ) > 1 :
2461
+ util .SMlog ("Not coalesceable, chain activated more than once" )
2462
+ raise Exception ("Not coalesceable, chain activated more than once" ) #TODO: Use correct error
2463
+
2464
+ try :
2465
+ if host_refs and vdi .cowutil .coalesceOnRemote :
2466
+ #Leaf opened on another host, we need to call online coalesce
2467
+ vdi ._doCoalesceOnHost (host_refs [0 ])
2468
+ skipRelink = True
2469
+ else :
2470
+ vdi ._doCoalesce ()
2471
+ except :
2472
+ os .unlink (self ._gc_running_file (vdi ))
2473
+ raise
2474
+ """
2475
+ vdi._doCoalesce will call vdi._coalesceCowImage (after doing other things).
2476
+ It will then call VDI._doCoalesceCowImage in a runAbortable context
2477
+ """
2398
2478
self .journaler .remove (vdi .JRN_COALESCE , vdi .uuid )
2399
2479
2400
2480
util .fistpoint .activate ("LVHDRT_before_create_relink_journal" , self .uuid )
@@ -2403,19 +2483,22 @@ def _coalesce(self, vdi):
2403
2483
# like SM.clone from manipulating the VDIs we'll be relinking and
2404
2484
# rescan the SR first in case the children changed since the last
2405
2485
# scan
2406
- self .journaler .create (vdi .JRN_RELINK , vdi .uuid , "1" )
2486
+ if not skipRelink :
2487
+ self .journaler .create (vdi .JRN_RELINK , vdi .uuid , "1" )
2407
2488
2408
- self .lock ()
2409
- try :
2410
- vdi .parent ._tagChildrenForRelink ()
2411
- self .scan ()
2412
- vdi ._relinkSkip ()
2413
- finally :
2414
- self .unlock ()
2415
- # Reload the children to leave things consistent
2416
- vdi .parent ._reloadChildren (vdi )
2489
+ if not skipRelink :
2490
+ self .lock ()
2491
+ try :
2492
+ vdi .parent ._tagChildrenForRelink ()
2493
+ self .scan ()
2494
+ vdi ._relinkSkip ()
2495
+ finally :
2496
+ self .unlock ()
2497
+ # Reload the children to leave things consistent
2498
+ vdi .parent ._reloadChildren (vdi )
2499
+ self .journaler .remove (vdi .JRN_RELINK , vdi .uuid )
2417
2500
2418
- self .journaler . remove (vdi . JRN_RELINK , vdi . uuid )
2501
+ os . unlink ( self ._gc_running_file (vdi ) )
2419
2502
self .deleteVDI (vdi )
2420
2503
2421
2504
class CoalesceTracker :
@@ -3718,8 +3801,7 @@ def _gc_init_file(sr_uuid):
3718
3801
3719
3802
def _create_init_file (sr_uuid ):
3720
3803
util .makedirs (os .path .join (NON_PERSISTENT_DIR , str (sr_uuid )))
3721
- with open (os .path .join (
3722
- NON_PERSISTENT_DIR , str (sr_uuid ), 'gc_init' ), 'w+' ) as f :
3804
+ with open (os .path .join (_gc_init_file (sr_uuid )), 'w+' ) as f :
3723
3805
f .write ('1' )
3724
3806
3725
3807
@@ -3748,7 +3830,7 @@ def abortTest():
3748
3830
Util .log ("GC active, quiet period ended" )
3749
3831
3750
3832
3751
- def _gcLoop (sr , dryRun = False , immediate = False ):
3833
+ def _gcLoop (sr : SR , dryRun = False , immediate = False ):
3752
3834
if not lockGCActive .acquireNoblock ():
3753
3835
Util .log ("Another GC instance already active, exiting" )
3754
3836
return
0 commit comments