4
4
import errno
5
5
import os
6
6
import re
7
+ import time
7
8
import struct
8
9
import zlib
9
10
from pathlib import Path
10
11
11
12
import util
12
13
import xs_errors
13
- from lvmcache import LVMCache
14
+ from blktap2 import TapCtl
14
15
from cowutil import CowUtil , CowImageInfo
16
+ from lvmcache import LVMCache
15
17
16
18
MAX_QCOW_CHAIN_LENGTH : Final = 30
17
19
@@ -421,7 +423,7 @@ def getBlockSize(self, path: str) -> int:
421
423
return QCOW_CLUSTER_SIZE
422
424
423
425
@override
424
- def getFooterSize (self , path : str ) -> int :
426
+ def getFooterSize (self ) -> int :
425
427
return 0
426
428
427
429
@override
@@ -479,7 +481,7 @@ def getInfoFromLVM(
479
481
) -> Optional [CowImageInfo ]:
480
482
lvcache = LVMCache (vgName )
481
483
lvcache .refresh ()
482
- if lvName not in self .lvs :
484
+ if lvName not in lvcache .lvs :
483
485
return None
484
486
if not lvcache .is_active (lvName ):
485
487
lvcache .activateNoRefcount (lvName )
@@ -505,7 +507,6 @@ def getAllInfoFromVG(
505
507
result : Dict [str , CowImageInfo ] = dict ()
506
508
#TODO: handle parents, it needs to getinfo from parents also
507
509
#TODO: handle exitOnError
508
- #TODO: If a vgName is given, is it already enabled? Do we need to enable LV on it too? It also only work for FileSR but LvmCowUtil uses it.
509
510
if vgName :
510
511
reg = re .compile (pattern )
511
512
lvcache = LVMCache (vgName )
@@ -516,7 +517,6 @@ def getAllInfoFromVG(
516
517
was_activated = False
517
518
lvinfo = lvcache .lvs [lvName ]
518
519
if reg .match (lvName ):
519
- util .SMlog ("Match {}: {}" .format (lvName , lvinfo ))
520
520
lvcache .refresh ()
521
521
if not lvcache .is_active (lvName ):
522
522
lvcache .activateNoRefcount (lvName )
@@ -529,8 +529,6 @@ def getAllInfoFromVG(
529
529
lvcache .deactivateNoRefcount (lvName )
530
530
except Exception as e :
531
531
raise e
532
- else :
533
- util .SMlog ("NOT {}: {}" .format (lvName , lvinfo ))
534
532
return result
535
533
else :
536
534
pattern_p : Path = Path (pattern )
@@ -696,22 +694,37 @@ def getBlockBitmap(self, path: str) -> bytes:
696
694
697
695
@override
698
696
def coalesce (self , path : str ) -> int :
699
- pid_opener = util .get_openers_pid (path )
700
- if pid_opener is not None :
701
- raise xs_errors .XenError ("LeafGCSkip" , f"We can't coalesce the QCOW2 since it's in use. Openers: { pid_opener } " )
702
-
703
- allocated_blocks = self .getAllocatedSize (path )
704
- # -d on commit make it not empty the original image since we don't intend to keep it
705
- cmd = [QEMU_IMG , "commit" , "-f" , QCOW2_TYPE , path , "-d" ]
706
- ret = cast (str , self ._ioretry (cmd )) #TODO: parse for errors
707
- return allocated_blocks
697
+ pid_openers = util .get_openers_pid (path )
698
+ if pid_openers :
699
+ if len (pid_openers ) > 1 :
700
+ util .SMlog ("Multiple openers for {}" .format (path )) #TODO: There might be multiple PID?
701
+ pid = pid_openers [0 ]
702
+ l = TapCtl .list (pid = pid )
703
+ if len (l ) > 1 : #TODO: There might more than one minor for this blktap?
704
+ raise xs_errors .XenError ("TapdiskAlreadyRunning" , "There is multiple minor for this tapdisk process" )
705
+ minor = l [0 ]["minor" ]
706
+ TapCtl .commit (pid , minor , QCOW2_TYPE , path )
707
+ #We need to wait for query to return finished
708
+ #TODO: We are technically ininterruptible since being interrupted will only stop checking if the job is done
709
+ status , nb , _ = TapCtl .query (pid , minor )
710
+ if status == "undefined" :
711
+ return 0
712
+ while status != "concluded" :
713
+ time .sleep (1 )
714
+ status , nb , _ = TapCtl .query (pid , minor )
715
+ return nb
716
+ else :
717
+ allocated_blocks = self .getAllocatedSize (path )
718
+ # -d on commit make it not empty the original image since we don't intend to keep it
719
+ cmd = [QEMU_IMG , "commit" , "-f" , QCOW2_TYPE , path , "-d" ]
720
+ ret = cast (str , self ._ioretry (cmd )) #TODO: parse for errors
721
+ return allocated_blocks
708
722
709
723
@override
710
724
def create (self , path : str , size : int , static : bool , msize : int = 0 ) -> None :
711
725
cmd = [QEMU_IMG , "create" , "-f" , QCOW2_TYPE , path , str (size )]
712
726
if static :
713
727
cmd .extend (["-o" , "preallocation=full" ])
714
- #TODO: msize is ignored for now, it's used to preallocate metadata for VHD so it can use resize without journal
715
728
self ._ioretry (cmd )
716
729
self .setHidden (path , False ) #We add hidden header at creation
717
730
@@ -727,7 +740,6 @@ def snapshot(
727
740
parent_type = QCOW2_TYPE
728
741
if parentRaw :
729
742
parent_type = RAW_TYPE
730
- #TODO: msize is ignored for now, it's used to preallocate metadata for VHD so it can use resize without journal
731
743
# TODO: checkEmpty? If it is False, then the parent could be empty and should still be used for snapshot
732
744
cmd = [QEMU_IMG , "create" , "-f" , QCOW2_TYPE , "-b" , parent , "-F" , parent_type , path ]
733
745
self ._ioretry (cmd )
0 commit comments