@@ -450,6 +450,12 @@ def format_attr(attr, value):
450450
451451def run_command (cmd , check = True , ** kwargs ):
452452 import subprocess
453+ from test .support import has_subprocess_support
454+
455+ if not has_subprocess_support :
456+ # subprocess is not supported by the current platform
457+ return ''
458+
453459 timeout = COMMAND_TIMEOUT
454460
455461 cmd_str = ' ' .join (cmd )
@@ -949,6 +955,24 @@ def winreg_query(path):
949955 return None
950956
951957
958+ def wmi_query (query ):
959+ try :
960+ import _wmi
961+ except ImportError :
962+ return {}
963+
964+ try :
965+ data = _wmi .exec_query (query )
966+ except OSError :
967+ return {}
968+
969+ dict_data = {}
970+ for item in data .split ("\0 " ):
971+ key , _ , value = item .partition ("=" )
972+ dict_data [key ] = value
973+ return dict_data
974+
975+
952976def collect_windows (info_add ):
953977 if not MS_WINDOWS :
954978 # Code specific to Windows
@@ -988,22 +1012,14 @@ def collect_windows(info_add):
9881012 except (ImportError , AttributeError ):
9891013 pass
9901014
991- # windows.version_caption: "wmic os get Caption,Version /value" command
992- output = run_command (["wmic" , "os" , "get" , "Caption,Version" , "/value" ],
993- # When wmic.exe output is redirected to a pipe,
994- # it uses the OEM code page
995- encoding = "oem" )
996- if output :
997- for line in output .splitlines ():
998- line = line .strip ()
999- if line .startswith ('Caption=' ):
1000- line = line .removeprefix ('Caption=' ).strip ()
1001- if line :
1002- info_add ('windows.version_caption' , line )
1003- elif line .startswith ('Version=' ):
1004- line = line .removeprefix ('Version=' ).strip ()
1005- if line :
1006- info_add ('windows.version' , line )
1015+ # Get operating system caption and version using WMI
1016+ data = wmi_query ("SELECT Caption, Version FROM Win32_OperatingSystem" )
1017+ caption = data .get ('Caption' , '' )
1018+ if caption :
1019+ info_add ('windows.version_caption' , caption )
1020+ version = data .get ('Version' , '' )
1021+ if version :
1022+ info_add ('windows.version' , version )
10071023
10081024 # windows.ver: "ver" command
10091025 output = run_command (["ver" ], shell = True )
@@ -1121,7 +1137,97 @@ def get_machine_id():
11211137 return None
11221138
11231139
1124- def detect_virt ():
1140+ def detect_virt_windows (info_add ):
1141+ # On Windows, use WMI to detect the virtualization.
1142+ #
1143+ # Microsoft Hyper-V:
1144+ # - Win32_Bios.Version = 'VRTUAL - 12001807'
1145+ # - Win32_Bios.Manufacturer = 'American Megatrends Inc.'
1146+ # - Win32_ComputerSystem.Model = 'Virtual Machine'
1147+ # - Win32_ComputerSystem.Manufacturer = 'Microsoft Corporation'
1148+ #
1149+ # VMware:
1150+ # - Win32_ComputerSystem.Model = 'VMware'
1151+ # - Win32_ComputerSystem.Manufacturer = 'VMWare' (uppercase W in Ware)
1152+ # - Win32_Bios.SerialNumber starts with 'VMware-'
1153+ #
1154+ # QEMU:
1155+ # - Win32_ComputerSystem.Manufacturer = 'QEMU'
1156+ # - Win32_ComputerSystem.Model = 'Standard PC (Q35 + ICH9, 2009)'
1157+ # - Win32_Bios.Version = 'BOCHS - 1'
1158+ # - Win32_Bios.Manufacturer = 'EDK II'
1159+ #
1160+ # Parallels:
1161+ # - Win32_Bios.Version = 'PARALLELS'
1162+ #
1163+ # VirtualBox:
1164+ # - Win32_Bios.Version = 'VBOX'
1165+ # - Win32_ComputerSystem.Model = 'VirtualBox'
1166+ # - Win32_ComputerSystem.Manufacturer = 'innotek GmbH'
1167+ #
1168+ # Amazon EC2:
1169+ # - Win32_Bios.Version = 'AMAZON - 1'
1170+ # - Win32_Bios.Manufacturer = 'Amazon EC2'
1171+ # - Win32_ComputerSystem.Model = 'm7i.4xlarge'
1172+ # - Win32_ComputerSystem.Manufacturer = 'Amazon EC2'
1173+
1174+ KNOWN_VIRT = (
1175+ 'Amazon EC2' ,
1176+ 'QEMU' ,
1177+ 'VMware' ,
1178+ 'VirtualBox' ,
1179+ 'Xen' ,
1180+ 'oVirt' ,
1181+ )
1182+ KNOWN_BIOS_VERSIONS = {
1183+ 'PARALLELS' : 'Parallels' ,
1184+ 'VBOX' : 'VirtualBox' ,
1185+ }
1186+
1187+ computer = wmi_query ('SELECT Model, Manufacturer FROM Win32_ComputerSystem' )
1188+ computer_model = computer .get ('Model' , '' )
1189+ computer_manufacturer = computer .get ('Manufacturer' , '' )
1190+ if computer_manufacturer == 'Amazon EC2' :
1191+ # Log the VM model (ex: 'm7i.4xlarge')
1192+ info_add ('system.computer.model' , computer_model )
1193+ return computer_manufacturer
1194+ if computer_model in KNOWN_VIRT :
1195+ return computer_model
1196+ if computer_manufacturer in KNOWN_VIRT :
1197+ return computer_manufacturer
1198+
1199+ bios = wmi_query ('SELECT Version, Manufacturer FROM Win32_Bios' )
1200+
1201+ bios_version = bios .get ('Version' , '' )
1202+ if bios_version in KNOWN_VIRT :
1203+ return bios_version
1204+ if (bios_version .startswith ('VRTUAL - ' )
1205+ and computer_manufacturer == 'Microsoft Corporation' ):
1206+ return 'Microsoft Hyper-V'
1207+ try :
1208+ return KNOWN_BIOS_VERSIONS [bios_version ]
1209+ except KeyError :
1210+ pass
1211+
1212+ bios_manufacturer = bios .get ('Manufacturer' , '' )
1213+ if bios_manufacturer in KNOWN_VIRT :
1214+ return bios_manufacturer
1215+
1216+ # Log the values to update the code if a new VM is discovered
1217+ if computer_model :
1218+ info_add ('system.computer.model' , computer_model )
1219+ if computer_manufacturer :
1220+ info_add ('system.computer.manufacturer' , computer_manufacturer )
1221+ if bios_version :
1222+ info_add ('system.bios.version' , bios_version )
1223+ if bios_manufacturer :
1224+ info_add ('system.bios.manufacturer' , bios_manufacturer )
1225+
1226+
1227+ def detect_virt (info_add ):
1228+ if MS_WINDOWS :
1229+ return detect_virt_windows (info_add )
1230+
11251231 # Run systemd-detect-virt command
11261232 virt = run_command (["systemd-detect-virt" ], check = False )
11271233 if virt and virt != "none" :
@@ -1179,7 +1285,7 @@ def collect_system(info_add):
11791285 uptime = f'{ uptime } sec'
11801286 info_add ('system.uptime' , uptime )
11811287
1182- virt = detect_virt ()
1288+ virt = detect_virt (info_add )
11831289 if virt :
11841290 info_add ('system.virt' , virt )
11851291
0 commit comments