@@ -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 )
@@ -951,6 +957,24 @@ def winreg_query(path):
951957 return None
952958
953959
960+ def wmi_query (query ):
961+ try :
962+ import _wmi
963+ except ImportError :
964+ return {}
965+
966+ try :
967+ data = _wmi .exec_query (query )
968+ except OSError :
969+ return {}
970+
971+ dict_data = {}
972+ for item in data .split ("\0 " ):
973+ key , _ , value = item .partition ("=" )
974+ dict_data [key ] = value
975+ return dict_data
976+
977+
954978def collect_windows (info_add ):
955979 if not MS_WINDOWS :
956980 # Code specific to Windows
@@ -990,22 +1014,14 @@ def collect_windows(info_add):
9901014 except (ImportError , AttributeError ):
9911015 pass
9921016
993- # windows.version_caption: "wmic os get Caption,Version /value" command
994- output = run_command (["wmic" , "os" , "get" , "Caption,Version" , "/value" ],
995- # When wmic.exe output is redirected to a pipe,
996- # it uses the OEM code page
997- encoding = "oem" )
998- if output :
999- for line in output .splitlines ():
1000- line = line .strip ()
1001- if line .startswith ('Caption=' ):
1002- line = line .removeprefix ('Caption=' ).strip ()
1003- if line :
1004- info_add ('windows.version_caption' , line )
1005- elif line .startswith ('Version=' ):
1006- line = line .removeprefix ('Version=' ).strip ()
1007- if line :
1008- info_add ('windows.version' , line )
1017+ # Get operating system caption and version using WMI
1018+ data = wmi_query ("SELECT Caption, Version FROM Win32_OperatingSystem" )
1019+ caption = data .get ('Caption' , '' )
1020+ if caption :
1021+ info_add ('windows.version_caption' , caption )
1022+ version = data .get ('Version' , '' )
1023+ if version :
1024+ info_add ('windows.version' , version )
10091025
10101026 # windows.ver: "ver" command
10111027 output = run_command (["ver" ], shell = True )
@@ -1123,7 +1139,97 @@ def get_machine_id():
11231139 return None
11241140
11251141
1126- def detect_virt ():
1142+ def detect_virt_windows (info_add ):
1143+ # On Windows, use WMI to detect the virtualization.
1144+ #
1145+ # Microsoft Hyper-V:
1146+ # - Win32_Bios.Version = 'VRTUAL - 12001807'
1147+ # - Win32_Bios.Manufacturer = 'American Megatrends Inc.'
1148+ # - Win32_ComputerSystem.Model = 'Virtual Machine'
1149+ # - Win32_ComputerSystem.Manufacturer = 'Microsoft Corporation'
1150+ #
1151+ # VMware:
1152+ # - Win32_ComputerSystem.Model = 'VMware'
1153+ # - Win32_ComputerSystem.Manufacturer = 'VMWare' (uppercase W in Ware)
1154+ # - Win32_Bios.SerialNumber starts with 'VMware-'
1155+ #
1156+ # QEMU:
1157+ # - Win32_ComputerSystem.Manufacturer = 'QEMU'
1158+ # - Win32_ComputerSystem.Model = 'Standard PC (Q35 + ICH9, 2009)'
1159+ # - Win32_Bios.Version = 'BOCHS - 1'
1160+ # - Win32_Bios.Manufacturer = 'EDK II'
1161+ #
1162+ # Parallels:
1163+ # - Win32_Bios.Version = 'PARALLELS'
1164+ #
1165+ # VirtualBox:
1166+ # - Win32_Bios.Version = 'VBOX'
1167+ # - Win32_ComputerSystem.Model = 'VirtualBox'
1168+ # - Win32_ComputerSystem.Manufacturer = 'innotek GmbH'
1169+ #
1170+ # Amazon EC2:
1171+ # - Win32_Bios.Version = 'AMAZON - 1'
1172+ # - Win32_Bios.Manufacturer = 'Amazon EC2'
1173+ # - Win32_ComputerSystem.Model = 'm7i.4xlarge'
1174+ # - Win32_ComputerSystem.Manufacturer = 'Amazon EC2'
1175+
1176+ KNOWN_VIRT = (
1177+ 'Amazon EC2' ,
1178+ 'QEMU' ,
1179+ 'VMware' ,
1180+ 'VirtualBox' ,
1181+ 'Xen' ,
1182+ 'oVirt' ,
1183+ )
1184+ KNOWN_BIOS_VERSIONS = {
1185+ 'PARALLELS' : 'Parallels' ,
1186+ 'VBOX' : 'VirtualBox' ,
1187+ }
1188+
1189+ computer = wmi_query ('SELECT Model, Manufacturer FROM Win32_ComputerSystem' )
1190+ computer_model = computer .get ('Model' , '' )
1191+ computer_manufacturer = computer .get ('Manufacturer' , '' )
1192+ if computer_manufacturer == 'Amazon EC2' :
1193+ # Log the VM model (ex: 'm7i.4xlarge')
1194+ info_add ('system.computer.model' , computer_model )
1195+ return computer_manufacturer
1196+ if computer_model in KNOWN_VIRT :
1197+ return computer_model
1198+ if computer_manufacturer in KNOWN_VIRT :
1199+ return computer_manufacturer
1200+
1201+ bios = wmi_query ('SELECT Version, Manufacturer FROM Win32_Bios' )
1202+
1203+ bios_version = bios .get ('Version' , '' )
1204+ if bios_version in KNOWN_VIRT :
1205+ return bios_version
1206+ if (bios_version .startswith ('VRTUAL - ' )
1207+ and computer_manufacturer == 'Microsoft Corporation' ):
1208+ return 'Microsoft Hyper-V'
1209+ try :
1210+ return KNOWN_BIOS_VERSIONS [bios_version ]
1211+ except KeyError :
1212+ pass
1213+
1214+ bios_manufacturer = bios .get ('Manufacturer' , '' )
1215+ if bios_manufacturer in KNOWN_VIRT :
1216+ return bios_manufacturer
1217+
1218+ # Log the values to update the code if a new VM is discovered
1219+ if computer_model :
1220+ info_add ('system.computer.model' , computer_model )
1221+ if computer_manufacturer :
1222+ info_add ('system.computer.manufacturer' , computer_manufacturer )
1223+ if bios_version :
1224+ info_add ('system.bios.version' , bios_version )
1225+ if bios_manufacturer :
1226+ info_add ('system.bios.manufacturer' , bios_manufacturer )
1227+
1228+
1229+ def detect_virt (info_add ):
1230+ if MS_WINDOWS :
1231+ return detect_virt_windows (info_add )
1232+
11271233 # Run systemd-detect-virt command
11281234 virt = run_command (["systemd-detect-virt" ], check = False )
11291235 if virt and virt != "none" :
@@ -1181,7 +1287,7 @@ def collect_system(info_add):
11811287 uptime = f'{ uptime } sec'
11821288 info_add ('system.uptime' , uptime )
11831289
1184- virt = detect_virt ()
1290+ virt = detect_virt (info_add )
11851291 if virt :
11861292 info_add ('system.virt' , virt )
11871293
0 commit comments