From 1ce202d1686588f37d46f99a597138398a826d7a Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 8 Sep 2021 15:44:26 +0200 Subject: [statistics] Computer --- modules-available/statistics/api.inc.php | 2 +- .../statistics/inc/hardwareinfo.inc.php | 95 +++++++++++++++------- 2 files changed, 68 insertions(+), 29 deletions(-) diff --git a/modules-available/statistics/api.inc.php b/modules-available/statistics/api.inc.php index b7f3541c..7cb8a312 100644 --- a/modules-available/statistics/api.inc.php +++ b/modules-available/statistics/api.inc.php @@ -3,7 +3,7 @@ if (Request::any('action') === 'test' && isLocalExecution()) { HardwareInfo::parseMachine('0A5D9E23-80F4-9C43-912C-96D80AE7E80B', file_get_contents('/tmp/bla.json')); - error_log('Kweries: ' . Database::getQueryCount()); + echo 'Kweries: ' . Database::getQueryCount(); exit; } diff --git a/modules-available/statistics/inc/hardwareinfo.inc.php b/modules-available/statistics/inc/hardwareinfo.inc.php index ff780b62..1259391f 100644 --- a/modules-available/statistics/inc/hardwareinfo.inc.php +++ b/modules-available/statistics/inc/hardwareinfo.inc.php @@ -11,6 +11,7 @@ class HardwareInfo const SYSTEM_SLOT = 'SYSTEM_SLOT'; const PCI_DEVICE = 'PCI_DEVICE'; const HDD = 'HDD'; + const CPU = 'CPU'; public static function parseMachine(string $uuid, string $data) @@ -22,18 +23,35 @@ class HardwareInfo return; } // determine misc stuff first - $mainboardExtra = []; + $globalMainboardExtra = []; + $localMainboardExtra = []; // physical memory array $memArrays = self::getDmiHandles($data, 16); - $mainboardExtra['Memory Slot Count'] = 0; - $mainboardExtra['Memory Maximum Capacity'] = 0; + $globalMainboardExtra['Memory Slot Count'] = 0; + $globalMainboardExtra['Memory Maximum Capacity'] = 0; foreach ($memArrays as $mem) { $mem = self::prepareDmiProperties($mem); if (isset($mem['Number Of Devices']) && ($mem['Use'] ?? 0) === 'System Memory') { - $mainboardExtra['Memory Slot Count'] += $mem['Number Of Devices']; + $globalMainboardExtra['Memory Slot Count'] += $mem['Number Of Devices']; } if (isset($mem['Maximum Capacity'])) { - $mainboardExtra['Memory Maximum Capacity'] += Parser::convertSize($mem['Maximum Capacity'], 'M', false); + $globalMainboardExtra['Memory Maximum Capacity'] += Parser::convertSize($mem['Maximum Capacity'], 'M', false); + } + } + // BIOS section - need to cross-match this with mainboard or system model, as it doesn't have a meaningful + // identifier on its own + $bios = self::prepareDmiProperties(self::getDmiHandles($data, 0)[0]); + foreach (['Version', 'Release Date', 'Firmware Revision'] as $k) { + if (isset($bios[$k])) { + $localMainboardExtra['BIOS ' . $k] = $bios[$k]; + } + } + if (isset($bios['BIOS Revision'])) { + $localMainboardExtra['BIOS Revision'] = $bios['BIOS Revision']; + } + foreach (['Vendor', 'ROM Size'] as $k) { + if (isset($bios[$k])) { + $globalMainboardExtra['BIOS ' . $k] = $bios[$k]; } } // Using the general helper function @@ -46,12 +64,12 @@ class HardwareInfo ['Locator', 'Bank Locator', 'Serial Number', 'Asset Tag', 'Configured Memory Speed', 'Configured Voltage'] ); // Fake RAM slots used/total into this - $mainboardExtra['Memory Slot Occupied'] = $ramModCount; + $localMainboardExtra['Memory Slot Occupied'] = $ramModCount; self::updateHwTypeFromDmi($uuid, $data, 2, self::MAINBOARD, ['Manufacturer', 'Product Name'], [], ['Manufacturer', 'Product Name', 'Type', 'Version'], ['Serial Number', 'Asset Tag', 'Location In Chassis'], - $mainboardExtra + $globalMainboardExtra, $localMainboardExtra ); self::updateHwTypeFromDmi($uuid, $data, 1, self::DMI_SYSTEM, ['Manufacturer', 'Product Name'], [], @@ -59,10 +77,14 @@ class HardwareInfo ['Serial Number', 'UUID', 'SKU Number'] ); self::updateHwTypeFromDmi($uuid, $data, 39, self::POWER_SUPPLY, ['Manufacturer'], - ['Location', 'Power Unit Group', 'Name'], // Location might ne empty/"Unknown", but Name can be something like "PSU 2" + ['Location', 'Power Unit Group', 'Name'], // Location might be empty/"Unknown", but Name can be something like "PSU 2" ['Manufacturer', 'Product Name', 'Model Part Number', 'Revision', 'Max Power Capacity'], ['Serial Number', 'Asset Tag', 'Status', 'Plugged', 'Hot Replaceable'] ); + self::updateHwTypeFromDmi($uuid, $data, 4, self::CPU, ['Version'], + ['Socket Designation'], + ['Type', 'Family', 'Manufacturer', 'Signature', 'Version', 'Core Count', 'Thread Count'], + ['Voltage', 'Current Speed', 'Upgrade', 'Core Enabled']); self::updateHwTypeFromDmi($uuid, $data, 9, self::SYSTEM_SLOT, function(array &$entry): bool { if (!isset($entry['Type'])) return false; @@ -93,7 +115,7 @@ class HardwareInfo $pciHwIds[] = $mappingId; } self::markDisconnected($uuid, self::PCI_DEVICE, $pciHwIds); - // ---- Disks ------------------------------------ + // ---- Disks ------------------------------------0Y3R3K $hddHwIds = []; foreach (($data['drives'] ?? []) as $dev) { if (empty($dev['readlink'])) @@ -205,7 +227,7 @@ class HardwareInfo private static function updateHwTypeFromDmi(string $uuid, array $data, int $type, string $dbType, $requiredPropsOrCallback, array $pathFields, array $globalProps, array $localProps, - array $globalExtra = []): int + array $globalExtra = [], array $localExtra = []): int { $sections = self::getDmiHandles($data, $type); $thisMachineHwIds = []; @@ -221,10 +243,13 @@ class HardwareInfo if (!$requiredPropsOrCallback($flat)) continue; } - $hwid = self::writeGlobalHardwareData($dbType, self::propsFromArray($flat, ...$globalProps) + $globalExtra); + // Global + $props = self::propsFromArray($flat, ...$globalProps); + $hwid = self::writeGlobalHardwareData($dbType, $props + $globalExtra); + // Local $pathId = md5(self::idFromArray($flat, ...$pathFields)); $props = self::propsFromArray($flat, ...$localProps); - $mappingId = self::writeLocalHardwareData($uuid, $hwid, $pathId, $props); + $mappingId = self::writeLocalHardwareData($uuid, $hwid, $pathId, $props + $localExtra); $thisMachineHwIds[] = $mappingId; } // Any hw <-> client mappings not in that list get marked as disconnected @@ -242,11 +267,27 @@ class HardwareInfo if (!isset($cache[$id])) { // Cache lookup, make sure we insert this only once for every run, as this is supposed to be general // information about the hardware, e.g. model number, max. resultion, capacity, ... - $cache[$id] = self::updateHwEntity($dbType, $id, $global); + $hwid = Database::insertIgnore('statistic_hw', 'hwid', ['hwtype' => $dbType, 'hwname' => $id]); + $vals = []; + foreach ($global as $k => $v) { + $vals[] = [$hwid, $k, $v]; + } + if (!empty($vals)) { + Database::exec("INSERT IGNORE INTO statistic_hw_prop (hwid, prop, `value`) VALUES :vals", + ['vals' => $vals]); + } + $cache[$id] = $hwid; } return $cache[$id]; } + /** + * Mark all devices of a given type disconnected from the given machine, with an optional + * exclude list of machine-client-mapping IDs + * @param string $uuid client + * @param string $dbType type, eg HDD + * @param array $excludedHwIds mappingIDs to exclude, ie. devices that are still connected + */ private static function markDisconnected(string $uuid, string $dbType, array $excludedHwIds) { error_log("Marking disconnected for $dbType from " . implode(', ', $excludedHwIds)); @@ -315,20 +356,6 @@ class HardwareInfo return $out; } - private static function updateHwEntity(string $hwType, string $uniqueName, array $props): int - { - $hwid = Database::insertIgnore('statistic_hw', 'hwid', ['hwtype' => $hwType, 'hwname' => $uniqueName]); - $vals = []; - foreach ($props as $k => $v) { - $vals[] = [$hwid, $k, $v]; - } - if (!empty($vals)) { - Database::exec("INSERT IGNORE INTO statistic_hw_prop (hwid, prop, `value`) VALUES :vals", - ['vals' => $vals]); - } - return $hwid; - } - /** * Takes hwinfo json, then looks up and returns all sections from the * dmidecode subtree that represent the given dmi table entry type, @@ -368,7 +395,7 @@ class HardwareInfo if ($val === '' || $val === 'notspecified' || $val === 'tobefilledbyoem' || $val === 'unknown' || $val === 'chassismanufacture' || $val === 'chassismanufacturer' || $val === 'chassisversion' || $val === 'chassisserialnumber' || $val === 'defaultstring' || $val === 'productname' - || $val === 'manufacturer' || $val === 'systemmodel') { + || $val === 'manufacturer' || $val === 'systemmodel' || $val === 'fillbyoem') { continue; } $val = trim($vals['values'][0] ?? ''); @@ -382,6 +409,9 @@ class HardwareInfo return $ret; } + /** + * Unify different variants of manufacturer names + */ private static function fixManufacturer(string $in): string { $in = Parser::decodeJedec($in); @@ -418,6 +448,15 @@ class HardwareInfo return $in; } + /** + * Establish a mapping between a client and some hardware device. + * Optionally writes hardware properties specific to a hardware instance of a client + * @param string $uuid client + * @param int $hwid hw global hw id + * @param string $pathId unique identifier for the local instance of this hw, e.q. PCI slot, /dev path, something that handles the case that there are multiple instances of the same hardware in one machine + * @param array $props KV-pairs of properties to write for this instance; can be empty + * @return int + */ private static function writeLocalHardwareData(string $uuid, int $hwid, string $pathId, array $props): int { // Add mapping between hw entity and machine -- cgit v1.2.3-55-g7522