@@ -452,6 +452,12 @@ def format_attr(attr, value):
452452
453453def run_command (cmd , check = True , ** kwargs ):
454454 import subprocess
455+ from test .support import has_subprocess_support
456+
457+ if not has_subprocess_support :
458+ # subprocess is not supported by the current platform
459+ return ''
460+
455461 timeout = COMMAND_TIMEOUT
456462
457463 cmd_str = ' ' .join (cmd )
@@ -963,6 +969,24 @@ def winreg_query(path):
963969 return None
964970
965971
972+ def wmi_query (query ):
973+ try :
974+ import _wmi
975+ except ImportError :
976+ return {}
977+
978+ try :
979+ data = _wmi .exec_query (query )
980+ except OSError :
981+ return {}
982+
983+ dict_data = {}
984+ for item in data .split ("\0 " ):
985+ key , _ , value = item .partition ("=" )
986+ dict_data [key ] = value
987+ return dict_data
988+
989+
966990def collect_windows (info_add ):
967991 if not MS_WINDOWS :
968992 # Code specific to Windows
@@ -1009,22 +1033,14 @@ def collect_windows(info_add):
10091033 call_func (info_add , 'windows.ansi_code_page' , _winapi , 'GetACP' )
10101034 call_func (info_add , 'windows.oem_code_page' , _winapi , 'GetOEMCP' )
10111035
1012- # windows.version_caption: "wmic os get Caption,Version /value" command
1013- output = run_command (["wmic" , "os" , "get" , "Caption,Version" , "/value" ],
1014- # When wmic.exe output is redirected to a pipe,
1015- # it uses the OEM code page
1016- encoding = "oem" )
1017- if output :
1018- for line in output .splitlines ():
1019- line = line .strip ()
1020- if line .startswith ('Caption=' ):
1021- line = line .removeprefix ('Caption=' ).strip ()
1022- if line :
1023- info_add ('windows.version_caption' , line )
1024- elif line .startswith ('Version=' ):
1025- line = line .removeprefix ('Version=' ).strip ()
1026- if line :
1027- info_add ('windows.version' , line )
1036+ # Get operating system caption and version using WMI
1037+ data = wmi_query ("SELECT Caption, Version FROM Win32_OperatingSystem" )
1038+ caption = data .get ('Caption' , '' )
1039+ if caption :
1040+ info_add ('windows.version_caption' , caption )
1041+ version = data .get ('Version' , '' )
1042+ if version :
1043+ info_add ('windows.version' , version )
10281044
10291045 # windows.ver: "ver" command
10301046 output = run_command (["ver" ], shell = True )
@@ -1142,7 +1158,97 @@ def get_machine_id():
11421158 return None
11431159
11441160
1145- def detect_virt ():
1161+ def detect_virt_windows (info_add ):
1162+ # On Windows, use WMI to detect the virtualization.
1163+ #
1164+ # Microsoft Hyper-V:
1165+ # - Win32_Bios.Version = 'VRTUAL - 12001807'
1166+ # - Win32_Bios.Manufacturer = 'American Megatrends Inc.'
1167+ # - Win32_ComputerSystem.Model = 'Virtual Machine'
1168+ # - Win32_ComputerSystem.Manufacturer = 'Microsoft Corporation'
1169+ #
1170+ # VMware:
1171+ # - Win32_ComputerSystem.Model = 'VMware'
1172+ # - Win32_ComputerSystem.Manufacturer = 'VMWare' (uppercase W in Ware)
1173+ # - Win32_Bios.SerialNumber starts with 'VMware-'
1174+ #
1175+ # QEMU:
1176+ # - Win32_ComputerSystem.Manufacturer = 'QEMU'
1177+ # - Win32_ComputerSystem.Model = 'Standard PC (Q35 + ICH9, 2009)'
1178+ # - Win32_Bios.Version = 'BOCHS - 1'
1179+ # - Win32_Bios.Manufacturer = 'EDK II'
1180+ #
1181+ # Parallels:
1182+ # - Win32_Bios.Version = 'PARALLELS'
1183+ #
1184+ # VirtualBox:
1185+ # - Win32_Bios.Version = 'VBOX'
1186+ # - Win32_ComputerSystem.Model = 'VirtualBox'
1187+ # - Win32_ComputerSystem.Manufacturer = 'innotek GmbH'
1188+ #
1189+ # Amazon EC2:
1190+ # - Win32_Bios.Version = 'AMAZON - 1'
1191+ # - Win32_Bios.Manufacturer = 'Amazon EC2'
1192+ # - Win32_ComputerSystem.Model = 'm7i.4xlarge'
1193+ # - Win32_ComputerSystem.Manufacturer = 'Amazon EC2'
1194+
1195+ KNOWN_VIRT = (
1196+ 'Amazon EC2' ,
1197+ 'QEMU' ,
1198+ 'VMware' ,
1199+ 'VirtualBox' ,
1200+ 'Xen' ,
1201+ 'oVirt' ,
1202+ )
1203+ KNOWN_BIOS_VERSIONS = {
1204+ 'PARALLELS' : 'Parallels' ,
1205+ 'VBOX' : 'VirtualBox' ,
1206+ }
1207+
1208+ computer = wmi_query ('SELECT Model, Manufacturer FROM Win32_ComputerSystem' )
1209+ computer_model = computer .get ('Model' , '' )
1210+ computer_manufacturer = computer .get ('Manufacturer' , '' )
1211+ if computer_manufacturer == 'Amazon EC2' :
1212+ # Log the VM model (ex: 'm7i.4xlarge')
1213+ info_add ('system.computer.model' , computer_model )
1214+ return computer_manufacturer
1215+ if computer_model in KNOWN_VIRT :
1216+ return computer_model
1217+ if computer_manufacturer in KNOWN_VIRT :
1218+ return computer_manufacturer
1219+
1220+ bios = wmi_query ('SELECT Version, Manufacturer FROM Win32_Bios' )
1221+
1222+ bios_version = bios .get ('Version' , '' )
1223+ if bios_version in KNOWN_VIRT :
1224+ return bios_version
1225+ if (bios_version .startswith ('VRTUAL - ' )
1226+ and computer_manufacturer == 'Microsoft Corporation' ):
1227+ return 'Microsoft Hyper-V'
1228+ try :
1229+ return KNOWN_BIOS_VERSIONS [bios_version ]
1230+ except KeyError :
1231+ pass
1232+
1233+ bios_manufacturer = bios .get ('Manufacturer' , '' )
1234+ if bios_manufacturer in KNOWN_VIRT :
1235+ return bios_manufacturer
1236+
1237+ # Log the values to update the code if a new VM is discovered
1238+ if computer_model :
1239+ info_add ('system.computer.model' , computer_model )
1240+ if computer_manufacturer :
1241+ info_add ('system.computer.manufacturer' , computer_manufacturer )
1242+ if bios_version :
1243+ info_add ('system.bios.version' , bios_version )
1244+ if bios_manufacturer :
1245+ info_add ('system.bios.manufacturer' , bios_manufacturer )
1246+
1247+
1248+ def detect_virt (info_add ):
1249+ if MS_WINDOWS :
1250+ return detect_virt_windows (info_add )
1251+
11461252 # Run systemd-detect-virt command
11471253 virt = run_command (["systemd-detect-virt" ], check = False )
11481254 if virt and virt != "none" :
@@ -1200,7 +1306,7 @@ def collect_system(info_add):
12001306 uptime = f'{ uptime } sec'
12011307 info_add ('system.uptime' , uptime )
12021308
1203- virt = detect_virt ()
1309+ virt = detect_virt (info_add )
12041310 if virt :
12051311 info_add ('system.virt' , virt )
12061312
0 commit comments