2020import tempfile
2121import termios
2222from base64 import b64encode
23+ from collections .abc import Callable
2324from dataclasses import dataclass
2425from pathlib import Path
2526from shutil import which
2627from time import sleep
27- from typing import Any , Callable , NoReturn
28+ from typing import Any , NoReturn
2829
2930from virtme_ng .utils import (
3031 CACHE_DIR ,
@@ -897,7 +898,10 @@ class DiskArg:
897898 # I/O driver parameters
898899 "cache" : ("str" , "Cache mode (none|writeback|writethrough|directsync|unsafe)" ),
899900 "aio" : ("str" , "Asynchronous I/O mode (native|threads|io_uring)" ),
900- "discard" : ("bool" , "Pass through TRIM/UNMAP requests (true=unmap, false=ignore)" ),
901+ "discard" : (
902+ "bool" ,
903+ "Pass through TRIM/UNMAP requests (true=unmap, false=ignore)" ,
904+ ),
901905 "detect-zeroes" : ("bool" , "Detect all-zero writes (true=on/unmap, false=off)" ),
902906 "queues" : ("int" , "Number of I/O queues" ),
903907 # topology parameters
@@ -918,15 +922,26 @@ def __post_init__(self):
918922 if self .pop_opt ("topology" , strtobool , False ):
919923 self .opts = self .topology () | self .opts
920924
921- def get_opt (self , name : str , parser : Callable [[str ], Any ] = str , default : Any = None ) -> Any :
925+ def get_opt (
926+ self , name : str , parser : Callable [[str ], Any ] = str , default : Any = None
927+ ) -> Any :
922928 opt = self .opts .get (name , None )
923929 return parser (opt ) if opt is not None else default
924930
925- def pop_opt (self , name : str , parser : Callable [[str ], Any ] = str , default : Any = None ) -> Any :
931+ def pop_opt (
932+ self , name : str , parser : Callable [[str ], Any ] = str , default : Any = None
933+ ) -> Any :
926934 opt = self .opts .pop (name , None )
927935 return parser (opt ) if opt is not None else default
928936
929- def pop_opt_qemu (self , name : str , default : Any = None , * , parser : Callable [[str ], Any ] = str , dest : str | None = None ) -> str | None :
937+ def pop_opt_qemu (
938+ self ,
939+ name : str ,
940+ default : Any = None ,
941+ * ,
942+ parser : Callable [[str ], Any ] = str ,
943+ dest : str | None = None ,
944+ ) -> str | None :
930945 opt = self .pop_opt (name , parser , default )
931946 # return DiskArg.qemu_opt(name=qemu if qemu is not None else name, value=opt)
932947 if opt is None :
@@ -939,19 +954,18 @@ def topology(self) -> dict[str, str]:
939954 # Get the real device name (handles symlinks like /dev/mapper -> /dev/dm-X)
940955 real_path = os .path .realpath (self .path , strict = True )
941956 dev_name = os .path .basename (real_path )
942- sys_base = Path (f' /sys/block/{ dev_name } ' )
957+ sys_base = Path (f" /sys/block/{ dev_name } " )
943958
944959 attributes = {
945960 # 'alignment': ('alignment_offset', int),
946- ' log-sec' : (' queue/logical_block_size' , int ),
947- ' phy-sec' : (' queue/physical_block_size' , int ),
948- ' min-io' : (' queue/minimum_io_size' , int ),
949- ' opt-io' : (' queue/optimal_io_size' , int ),
950- ' rota' : (' queue/rotational' , bool ),
961+ " log-sec" : (" queue/logical_block_size" , int ),
962+ " phy-sec" : (" queue/physical_block_size" , int ),
963+ " min-io" : (" queue/minimum_io_size" , int ),
964+ " opt-io" : (" queue/optimal_io_size" , int ),
965+ " rota" : (" queue/rotational" , bool ),
951966 # 'wzeroes': ('queue/write_zeroes_max_bytes', int),
952-
953967 # 'disc-aln': ('discard_alignment', int),
954- ' disc-gran' : (' queue/discard_granularity' , int ),
968+ " disc-gran" : (" queue/discard_granularity" , int ),
955969 # 'disc-max': ('queue/discard_max_bytes', int),
956970 # 'disc-zero': ('queue/discard_zeroes_data', bool),
957971 }
@@ -973,7 +987,7 @@ def topology(self) -> dict[str, str]:
973987
974988 # Validate name=path arguments from --disk and --blk-disk
975989 @classmethod
976- def parse (cls , func : str , arg : str ) -> ' DiskArg' :
990+ def parse (cls , func : str , arg : str ) -> " DiskArg" :
977991 items = arg .split ("," )
978992
979993 namefile = items [0 ]
@@ -998,13 +1012,17 @@ def parse(cls, func: str, arg: str) -> 'DiskArg':
9981012 opts [key ] = "1"
9991013
10001014 if "help" in opts :
1001- print ("\n " .join ([
1002- f"Possible { func } options:" ,
1003- ] + [
1004- "{:<20} {}" .format (f"{ key } =({ typ } )" , value )
1005- for key , (typ , value ) in
1006- DiskArg ._OPTS_HELP .items ()
1007- ]))
1015+ print (
1016+ "\n " .join (
1017+ [
1018+ f"Possible { func } options:" ,
1019+ ]
1020+ + [
1021+ "{:<20} {}" .format (f"{ key } =({ typ } )" , value )
1022+ for key , (typ , value ) in DiskArg ._OPTS_HELP .items ()
1023+ ]
1024+ )
1025+ )
10081026 sys .exit (0 )
10091027
10101028 return cls (
@@ -1707,56 +1725,69 @@ def do_it() -> int:
17071725
17081726 # we need those parameters multiple times
17091727 discard = disk .pop_opt ("discard" , parser = strtobool , default = None )
1710- detect_zeroes = disk .pop_opt ("detect-zeroes" , parser = strtobool , default = None )
1728+ detect_zeroes = disk .pop_opt (
1729+ "detect-zeroes" , parser = strtobool , default = None
1730+ )
17111731 # we need this parameter both to transform other parameters and as itself later
17121732 # log_sec = disk.get_opt("log-sec", parser=int, default=512)
17131733
1714- drive_opts .extend ([
1715- disk .pop_opt_qemu ("format" , "raw" ),
1716- disk .pop_opt_qemu ("cache" , None ),
1717- disk .pop_opt_qemu ("aio" , None ),
1718- f"discard={ "unmap" if discard else "ignore" } "
1719- if discard is not None else None ,
1720- f"detect-zeroes={ ("unmap" if discard else "on" ) if detect_zeroes else "off" } "
1721- if detect_zeroes is not None else None ,
1722- ])
1723-
1724- device_opts .extend ([
1725- f"discard={ "on" if discard else "off" } "
1726- if discard is not None else None ,
1727- disk .pop_opt_qemu ("disc-gran" , dest = "discard_granularity" ),
1728- disk .pop_opt_qemu ("log-sec" , dest = "logical_block_size" ),
1729- disk .pop_opt_qemu ("phy-sec" , dest = "physical_block_size" ),
1730- # disk.pop_qemu("disc-max", dest="max-discard-sectors", parser=lambda arg: int(arg) / log_sec),
1731- # disk.pop_qemu("wzeroes", dest="max-write-zeroes-sectors", parser=lambda arg: int(arg) / log_sec),
1732- disk .pop_opt_qemu ("min-io" , dest = "min_io_size" ),
1733- disk .pop_opt_qemu ("opt-io" , dest = "opt_io_size" ),
1734- disk .pop_opt_qemu ("queues" , dest = "num-queues" ),
1735- ])
1734+ drive_opts .extend (
1735+ [
1736+ disk .pop_opt_qemu ("format" , "raw" ),
1737+ disk .pop_opt_qemu ("cache" , None ),
1738+ disk .pop_opt_qemu ("aio" , None ),
1739+ f"discard={ 'unmap' if discard else 'ignore' } "
1740+ if discard is not None
1741+ else None ,
1742+ f"detect-zeroes={ ('unmap' if discard else 'on' ) if detect_zeroes else 'off' } "
1743+ if detect_zeroes is not None
1744+ else None ,
1745+ ]
1746+ )
1747+
1748+ device_opts .extend (
1749+ [
1750+ f"discard={ 'on' if discard else 'off' } "
1751+ if discard is not None
1752+ else None ,
1753+ disk .pop_opt_qemu ("disc-gran" , dest = "discard_granularity" ),
1754+ disk .pop_opt_qemu ("log-sec" , dest = "logical_block_size" ),
1755+ disk .pop_opt_qemu ("phy-sec" , dest = "physical_block_size" ),
1756+ # disk.pop_qemu("disc-max", dest="max-discard-sectors", parser=lambda arg: int(arg) / log_sec),
1757+ # disk.pop_qemu("wzeroes", dest="max-write-zeroes-sectors", parser=lambda arg: int(arg) / log_sec),
1758+ disk .pop_opt_qemu ("min-io" , dest = "min_io_size" ),
1759+ disk .pop_opt_qemu ("opt-io" , dest = "opt_io_size" ),
1760+ disk .pop_opt_qemu ("queues" , dest = "num-queues" ),
1761+ ]
1762+ )
17361763 # unused
17371764 disk .opts .pop ("rota" , None )
17381765
17391766 if disk .pop_opt ("iothread" , bool , False ):
17401767 iothreadid = f"iothread{ iothread_index } "
17411768 iothread_index += 1
1742- qemuargs .extend ([
1743- "-object" ,
1744- f"iothread,id={ iothreadid } " ,
1745- ])
1746- device_opts .append (
1747- f"iothread={ iothreadid } "
1769+ qemuargs .extend (
1770+ [
1771+ "-object" ,
1772+ f"iothread,id={ iothreadid } " ,
1773+ ]
17481774 )
1775+ device_opts .append (f"iothread={ iothreadid } " )
17491776
1750- qemuargs .extend ([
1751- "-drive" ,
1752- "," .join (o for o in drive_opts if o is not None ),
1753- "-device" ,
1754- "," .join (o for o in device_opts if o is not None ),
1755- ])
1777+ qemuargs .extend (
1778+ [
1779+ "-drive" ,
1780+ "," .join (o for o in drive_opts if o is not None ),
1781+ "-device" ,
1782+ "," .join (o for o in device_opts if o is not None ),
1783+ ]
1784+ )
17561785
17571786 # any options that were not consumed are errors
17581787 if disk .opts :
1759- raise ValueError (f"invalid --disk parameter: { d !r} \n (keys were not consumed: { disk .opts .keys ()} )" )
1788+ raise ValueError (
1789+ f"invalid --disk parameter: { d !r} \n (keys were not consumed: { disk .opts .keys ()} )"
1790+ )
17601791
17611792 if args .disk :
17621793 for i , d in enumerate (args .disk ):
@@ -1784,70 +1815,88 @@ def do_it() -> int:
17841815 "vendor=virtme" ,
17851816 "product=disk" ,
17861817 f"serial={ disk .name } " ,
1787- f"device_id={ device_id } "
1788- if device_id != disk .name else None ,
1818+ f"device_id={ device_id } " if device_id != disk .name else None ,
17891819 ]
17901820
17911821 # we need those parameters multiple times
17921822 discard = disk .pop_opt ("discard" , parser = strtobool , default = None )
1793- detect_zeroes = disk .pop_opt ("detect-zeroes" , parser = strtobool , default = None )
1823+ detect_zeroes = disk .pop_opt (
1824+ "detect-zeroes" , parser = strtobool , default = None
1825+ )
17941826 # we need this parameter both to transform other parameters and as itself later
17951827 log_sec = disk .get_opt ("log-sec" )
17961828
1797- drive_opts .extend ([
1798- disk .pop_opt_qemu ("format" , "raw" ),
1799- disk .pop_opt_qemu ("cache" , None ),
1800- disk .pop_opt_qemu ("aio" , None ),
1801- f"discard={ "unmap" if discard else "ignore" } "
1802- if discard is not None else None ,
1803- f"detect-zeroes={ ("unmap" if discard else "on" ) if detect_zeroes else "off" } "
1804- if detect_zeroes is not None else None ,
1805- ])
1806-
1807- scsi_opts .extend ([
1808- disk .pop_opt_qemu ("queues" , dest = "num-queues" ),
1809- ])
1810-
1811- device_opts .extend ([
1812- disk .pop_opt_qemu ("disc-gran" , dest = "discard_granularity" ),
1813- disk .pop_opt_qemu ("log-sec" , dest = "logical_block_size" ),
1814- # convenience: QEMU does not automatically adjust physical_block_size
1815- # to be not less than logical_block_size (it errors out instead), so we do it here
1816- disk .pop_opt_qemu ("phy-sec" , dest = "physical_block_size" , default = log_sec ),
1817- # disk.pop_qemu("disc-max", dest="max_unmap_size"),
1818- # disk.pop_qemu("wzeroes", dest="???"),
1819- disk .pop_opt_qemu ("min-io" , dest = "min_io_size" ),
1820- disk .pop_opt_qemu ("opt-io" , dest = "opt_io_size" ),
1821- # sic: set rotation_rate to "1" for non-rotating disks ("1" is a special value
1822- # that means "non-rotating medium"), but set to "0" for rotating disks
1823- # ("0" means "rotation rate not reported").
1824- disk .pop_opt_qemu ("rota" , dest = "rotation_rate" ,
1825- parser = lambda arg : "0" if strtobool (arg ) else "1" ),
1826- ])
1829+ drive_opts .extend (
1830+ [
1831+ disk .pop_opt_qemu ("format" , "raw" ),
1832+ disk .pop_opt_qemu ("cache" , None ),
1833+ disk .pop_opt_qemu ("aio" , None ),
1834+ f"discard={ 'unmap' if discard else 'ignore' } "
1835+ if discard is not None
1836+ else None ,
1837+ f"detect-zeroes={ ('unmap' if discard else 'on' ) if detect_zeroes else 'off' } "
1838+ if detect_zeroes is not None
1839+ else None ,
1840+ ]
1841+ )
1842+
1843+ scsi_opts .extend (
1844+ [
1845+ disk .pop_opt_qemu ("queues" , dest = "num-queues" ),
1846+ ]
1847+ )
1848+
1849+ device_opts .extend (
1850+ [
1851+ disk .pop_opt_qemu ("disc-gran" , dest = "discard_granularity" ),
1852+ disk .pop_opt_qemu ("log-sec" , dest = "logical_block_size" ),
1853+ # convenience: QEMU does not automatically adjust physical_block_size
1854+ # to be not less than logical_block_size (it errors out instead), so we do it here
1855+ disk .pop_opt_qemu (
1856+ "phy-sec" , dest = "physical_block_size" , default = log_sec
1857+ ),
1858+ # disk.pop_qemu("disc-max", dest="max_unmap_size"),
1859+ # disk.pop_qemu("wzeroes", dest="???"),
1860+ disk .pop_opt_qemu ("min-io" , dest = "min_io_size" ),
1861+ disk .pop_opt_qemu ("opt-io" , dest = "opt_io_size" ),
1862+ # sic: set rotation_rate to "1" for non-rotating disks ("1" is a special value
1863+ # that means "non-rotating medium"), but set to "0" for rotating disks
1864+ # ("0" means "rotation rate not reported").
1865+ disk .pop_opt_qemu (
1866+ "rota" ,
1867+ dest = "rotation_rate" ,
1868+ parser = lambda arg : "0" if strtobool (arg ) else "1" ,
1869+ ),
1870+ ]
1871+ )
18271872
18281873 if disk .pop_opt ("iothread" , bool , False ):
18291874 iothreadid = f"iothread{ iothread_index } "
18301875 iothread_index += 1
1831- qemuargs .extend ([
1832- "-object" ,
1833- f"iothread,id={ iothreadid } " ,
1834- ])
1835- scsi_opts .append (
1836- f"iothread={ iothreadid } "
1876+ qemuargs .extend (
1877+ [
1878+ "-object" ,
1879+ f"iothread,id={ iothreadid } " ,
1880+ ]
18371881 )
1882+ scsi_opts .append (f"iothread={ iothreadid } " )
18381883
1839- qemuargs .extend ([
1840- "-drive" ,
1841- "," .join (o for o in drive_opts if o is not None ),
1842- "-device" ,
1843- "," .join (o for o in scsi_opts if o is not None ),
1844- "-device" ,
1845- "," .join (o for o in device_opts if o is not None ),
1846- ])
1884+ qemuargs .extend (
1885+ [
1886+ "-drive" ,
1887+ "," .join (o for o in drive_opts if o is not None ),
1888+ "-device" ,
1889+ "," .join (o for o in scsi_opts if o is not None ),
1890+ "-device" ,
1891+ "," .join (o for o in device_opts if o is not None ),
1892+ ]
1893+ )
18471894
18481895 # any options that were not consumed are errors
18491896 if disk .opts :
1850- raise ValueError (f"invalid --disk parameter: { d !r} \n (keys were not consumed: { disk .opts .keys ()} )" )
1897+ raise ValueError (
1898+ f"invalid --disk parameter: { d !r} \n (keys were not consumed: { disk .opts .keys ()} )"
1899+ )
18511900
18521901 ret_path = None
18531902
0 commit comments