summaryrefslogtreecommitdiffstats
path: root/modules-available/statistics/pages
diff options
context:
space:
mode:
authorSimon Rettberg2021-09-30 17:44:30 +0200
committerSimon Rettberg2022-03-09 15:06:54 +0100
commitd736e75ade7a4472aefb72af9036f86016adcb42 (patch)
tree39e38d5dd687bc6d25cb1de45ce4b78423aa7984 /modules-available/statistics/pages
parent[passthrough] New module for managing hardware passthrough for QEMU (diff)
downloadslx-admin-d736e75ade7a4472aefb72af9036f86016adcb42.tar.gz
slx-admin-d736e75ade7a4472aefb72af9036f86016adcb42.tar.xz
slx-admin-d736e75ade7a4472aefb72af9036f86016adcb42.zip
[statistics] Adapt hw-data parsing to new json format for display
Diffstat (limited to 'modules-available/statistics/pages')
-rw-r--r--modules-available/statistics/pages/hints.inc.php50
-rw-r--r--modules-available/statistics/pages/machine.inc.php296
2 files changed, 303 insertions, 43 deletions
diff --git a/modules-available/statistics/pages/hints.inc.php b/modules-available/statistics/pages/hints.inc.php
index 5c6acfbb..278c0e26 100644
--- a/modules-available/statistics/pages/hints.inc.php
+++ b/modules-available/statistics/pages/hints.inc.php
@@ -5,19 +5,26 @@ class SubPage
public static function doPreprocess()
{
-
+ User::assertPermission('hints');
}
public static function doRender()
{
- self::showMemoryUpgrade();
- self::showMemorySlow();
- self::showUnusedSpace();
+ $locs = User::getAllowedLocations('hints');
+ if (in_array(0, $locs)) {
+ $locs = [];
+ }
+ self::showMemoryUpgrade($locs);
+ self::showMemorySlow($locs);
+ self::showUnusedSpace($locs);
}
- private static function showMemoryUpgrade()
+ private static function showMemoryUpgrade(array $locs)
{
$q = new HardwareQuery(HardwareInfo::MAINBOARD);
+ if (!empty($locs)) {
+ $q->addMachineWhere('locationid', 'IN', $locs);
+ }
$q->addLocalColumn('Memory Slot Occupied');
$q->addGlobalColumn('Memory Slot Count');
$q->addGlobalColumn('Memory Maximum Capacity');
@@ -40,9 +47,12 @@ class SubPage
Render::addTemplate('hints-ram-upgrade', ['list' => $list]);
}
- private static function showMemorySlow()
+ private static function showMemorySlow(array $locs)
{
$q = new HardwareQuery(HardwareInfo::RAM_MODULE);
+ if (!empty($locs)) {
+ $q->addMachineWhere('locationid', 'IN', $locs);
+ }
$q->addLocalColumn('Locator');
$q->addLocalColumn('Bank Locator');
$q->addGlobalColumn('Form Factor');
@@ -57,17 +67,39 @@ class SubPage
Render::addTemplate('hints-ram-underclocked', ['list' => $list]);
}
- private static function showUnusedSpace()
+ private static function showUnusedSpace(array $locs)
{
+ $id44 = $id45 = [];
+ // ID44
$q = new HardwareQuery(HardwareInfo::HDD);
+ if (!empty($locs)) {
+ $q->addMachineWhere('locationid', 'IN', $locs);
+ }
+ $q->addMachineColumn('clientip');
+ $q->addMachineColumn('hostname');
$q->addWhere(false, 'unused', '>', 2000000000); // 2 GB
$q->addMachineWhere('id44mb', '<', 20000); // 20 GB
- $id44 = $q->query()->fetchAll();
+ foreach ($q->query()->fetchAll() as $row) {
+ $row['unused_s'] = Util::readableFileSize($row['unused']);
+ $row['id44mb_s'] = Util::readableFileSize($row['id44mb'], -1, 2);
+ $id44[] = $row;
+ }
+ // ID45
$q = new HardwareQuery(HardwareInfo::HDD);
+ if (!empty($locs)) {
+ $q->addMachineWhere('locationid', 'IN', $locs);
+ }
+ $q->addMachineColumn('clientip');
+ $q->addMachineColumn('hostname');
$q->addWhere(false, 'unused', '>', 25000000000); // 25 GB
$q->addMachineWhere('id44mb', '>', 20000); // 20 GB
$q->addMachineWhere('id45mb', '<', 20000); // 20 GB
- $id45 = $q->query()->fetchAll();
+ foreach ($q->query()->fetchAll() as $row) {
+ $row['unused_s'] = Util::readableFileSize($row['unused']);
+ $row['id44mb_s'] = Util::readableFileSize($row['id44mb'], -1, 2);
+ $row['id45mb_s'] = Util::readableFileSize($row['id45mb'], -1, 2);
+ $id45[] = $row;
+ }
Render::addTemplate('hints-hdd-grow', [
'id44' => $id44,
'id45' => $id45,
diff --git a/modules-available/statistics/pages/machine.inc.php b/modules-available/statistics/pages/machine.inc.php
index f3af4f47..b23db721 100644
--- a/modules-available/statistics/pages/machine.inc.php
+++ b/modules-available/statistics/pages/machine.inc.php
@@ -54,7 +54,7 @@ class SubPage
$client = Database::queryFirst('SELECT machineuuid, locationid, macaddr, clientip, firstseen, lastseen, logintime, lastboot, state,
mbram, live_tmpsize, live_tmpfree, live_id45size, live_id45free, live_swapsize, live_swapfree,
live_memsize, live_memfree, live_cpuload, live_cputemp,
- Length(position) AS hasroomplan, kvmstate, cpumodel, id44mb, data, hostname, currentuser, currentsession, notes
+ Length(position) AS hasroomplan, kvmstate, cpumodel, id44mb, id45mb, data, hostname, currentuser, currentsession, notes
FROM machine WHERE machineuuid = :uuid',
array('uuid' => $uuid));
if ($client === false) {
@@ -70,6 +70,55 @@ class SubPage
Header('Content-Type: text/plain; charset=utf-8');
die($client['data']);
}
+ // Parse data
+ $hdds = array();
+ if ($client['data'][0] === '{') {
+ $json = json_decode($client['data'], true);
+ if (is_array($json)) {
+ $client += self::parseJson($uuid, $json);
+ $hdds['hdds'] = self::queryHddData($uuid);
+ }
+ } else {
+ self::parseLegacy($client, $hdds);
+ }
+ unset($client['data']);
+ // Get rid of configured speed, if equal to maximum speed
+ foreach ($client['ram'] as &$item) {
+ if (isset($item['Configured Memory Speed']) && $item['Configured Memory Speed'] === $item['Speed']) {
+ unset($item['Configured Memory Speed']);
+ }
+ }
+ unset($item);
+ $client['lspci1'] = $client['lspci2'] = [];
+ foreach ($client['lspci'] as $item) {
+ $item['vendor_s'] = PciId::getPciId(PciId::VENDOR, $item['vendor']);
+ $item['device_s'] = PciId::getPciId(PciId::DEVICE, $item['vendor'] . $item['device']);
+ if ($item['vendor_s'] === false) {
+ $pciLookup[$item['vendor']] = true;
+ }
+ if ($item['device_s'] === false) {
+ $pciLookup[$item['vendor'] . ':' . $item['device']] = true;
+ }
+ $class = $item['class'];
+ if ($class === '0300' || $class === '0200' || $class === '0403') {
+ $dst =& $client['lspci1'];
+ } else {
+ $dst =& $client['lspci2'];
+ }
+ if (!isset($dst[$class])) {
+ $dst[$class] = [
+ 'class' => $class,
+ 'class_s' => PciId::getPciId(PciId::DEVCLASS, $class, true),
+ 'entries' => [],
+ ];
+ }
+ $dst[$class]['entries'][] = $item;
+ }
+ unset($dst, $client['lspci']);
+ ksort($client['lspci1']);
+ ksort($client['lspci2']);
+ $client['lspci1'] = array_values($client['lspci1']);
+ $client['lspci2'] = array_values($client['lspci2']);
// Runmode
if (Module::isAvailable('runmode')) {
$data = RunMode::getRunMode($uuid, RunMode::DATA_STRINGS);
@@ -121,6 +170,7 @@ class SubPage
}
$client['gbram'] = round(ceil($client['mbram'] / 512) / 2, 1);
$client['gbtmp'] = round($client['id44mb'] / 1024);
+ $client['gbid45'] = round($client['id45mb'] / 1024);
foreach (['tmp', 'id45', 'swap', 'mem'] as $item) {
if ($client['live_' . $item . 'size'] == 0)
continue;
@@ -135,44 +185,25 @@ class SubPage
$client['ramclass'] = StatisticsStyling::ramColorClass($client['mbram']);
$client['kvmclass'] = StatisticsStyling::kvmColorClass($client['kvmstate']);
$client['hddclass'] = StatisticsStyling::hddColorClass($client['gbtmp']);
- // Parse the giant blob of data
- if (strpos($client['data'], "\r") !== false) {
- $client['data'] = str_replace("\r", "\n", $client['data']);
- }
- $hdds = array();
- if (preg_match_all('/##### ([^#]+) #+$(.*?)^#####/ims', $client['data'] . '########', $out, PREG_SET_ORDER)) {
- foreach ($out as $section) {
- if ($section[1] === 'CPU') {
- HardwareParser::parseCpu($client, $section[2]);
- }
- if ($section[1] === 'dmidecode') {
- HardwareParser::parseDmiDecode($client, $section[2]);
- }
- if ($section[1] === 'Partition tables') {
- HardwareParser::parseHdd($hdds, $section[2]);
- }
- if ($section[1] === 'PCI ID') {
- $client['lspci1'] = $client['lspci2'] = array();
- HardwareParser::parsePci($client['lspci1'], $client['lspci2'], $section[2]);
- }
- if (isset($hdds['hdds']) && $section[1] === 'smartctl') {
- // This currently requires that the partition table section comes first...
- HardwareParser::parseSmartctl($hdds['hdds'], $section[2]);
- }
- }
+ // Format HDD data to strings
+ foreach ($hdds['hdds'] as &$hdd) {
+ $hdd['smart_status_failed'] = !($client['smart_status//passed'] ?? 1);
+ self::mangleHdd($hdd);
}
- unset($client['data']);
// BIOS update check
- if (!empty($client['biosrevision'])) {
- $mainboard = $client['mobomanufacturer'] . '##' . $client['mobomodel'];
- $system = $client['pcmanufacturer'] . '##' . $client['pcmodel'];
- $ret = self::checkBios($mainboard, $system, $client['biosdate'], $client['biosrevision']);
+ if (!empty($client['bios']['BIOS Revision']) || !empty($client['bios']['Release Date'])) {
+ if (preg_match('#^(\d{1,2})/(\d{1,2})/(\d{4})#', $client['bios']['Release Date'], $out)) {
+ $client['bios']['Release Date'] = $out[2] . '.' . $out[1] . '.' . $out[3];
+ }
+ $mainboard = $client['mainboard']['Manufacturer'] . '##' . $client['mainboard']['Product Name'];
+ $system = $client['system']['Manufacturer'] . '##' . $client['system']['Product Name'];
+ $ret = self::checkBios($mainboard, $system, $client['bios']['Release Date'], $client['bios']['BIOS Revision']);
if ($ret === false) { // Not loaded, use AJAX
$params = [
'mainboard' => $mainboard,
'system' => $system,
- 'date' => $client['biosdate'],
- 'revision' => $client['biosrevision'],
+ 'date' => $client['bios']['Release Date'],
+ 'revision' => $client['bios']['BIOS Revision'],
];
$client['biosurl'] = '?do=statistics&action=bios&' . http_build_query($params);
} elseif (!isset($ret['status']) || $ret['status'] !== 0) {
@@ -211,6 +242,10 @@ class SubPage
Permission::addGlobalTags($client['perms'], null, ['hardware.projectors.edit', 'hardware.projectors.view']);
// Throw output at user
Render::addTemplate('machine-main', $client);
+ if (!empty($pciLookup)) {
+ Render::addTemplate('js-pciquery',
+ ['missing_ids' => json_encode(array_keys($pciLookup))]);
+ }
// Sessions
$NOW = time();
$cutoff = $NOW - 86400 * 7;
@@ -348,7 +383,182 @@ class SubPage
}
}
- private static function eventToIconName($event)
+ private static function parseLegacy(array &$client, array &$hdds)
+ {
+ // Parse the giant blob of data
+ if (strpos($client['data'], "\r") !== false) {
+ $client['data'] = str_replace("\r", "\n", $client['data']);
+ }
+ if (preg_match_all('/##### ([^#]+) #+$(.*?)^#####/ims', $client['data'] . '########', $out, PREG_SET_ORDER)) {
+ foreach ($out as $section) {
+ if ($section[1] === 'CPU') {
+ HardwareParserLegacy::parseCpu($client, $section[2]);
+ }
+ if ($section[1] === 'dmidecode') {
+ HardwareParserLegacy::parseDmiDecode($client, $section[2]);
+ }
+ if ($section[1] === 'Partition tables') {
+ HardwareParserLegacy::parseHdd($hdds, $section[2]);
+ }
+ if ($section[1] === 'PCI ID') {
+ $client['lspci'] = HardwareParserLegacy::parsePci($section[2]);
+ }
+ if (isset($hdds['hdds']) && $section[1] === 'smartctl') {
+ // This currently requires that the partition table section comes first...
+ HardwareParserLegacy::parseSmartctl($hdds['hdds'], $section[2]);
+ }
+ }
+ }
+ }
+
+ private static function parseJson(string $uuid, array $json): array
+ {
+ $return = [
+ 'cpu' => $json['cpu'] ?? [],
+ 'lspci' => $json['lspci'] ?? [],
+ 'ram' => array_map(function($item) {
+ return HardwareParser::prepareDmiProperties($item);
+ }, HardwareParser::getDmiHandles($json, 17)),
+ ];
+ foreach ($return['ram'] as $ram) {
+ if (!empty($ram['Form Factor']) && !empty($ram['Type'])) {
+ $return['ramtype'] = $ram['Type'] . '-' . $ram['Form Factor'];
+ break;
+ }
+ }
+ $need = [
+ 'bios' => 0,
+ 'system' => 1,
+ 'mainboard' => 2,
+ ];
+ foreach ($need as $name => $id) {
+ $return[$name] = HardwareParser::prepareDmiProperties(
+ HardwareParser::getDmiHandles($json, $id)[0] ?? []);
+ }
+ $q = new HardwareQuery(HardwareInfo::MAINBOARD, $uuid);
+ $q->addGlobalColumn('Memory Maximum Capacity');
+ $q->addGlobalColumn('Memory Slot Count');
+ $res = $q->query()->fetch();
+ if (is_array($res)) {
+ $return += $res;
+ }
+ return $return;
+ }
+
+ private static function queryHddData(string $uuid): array
+ {
+ $hdds = [];
+ $ret = Database::simpleQuery("SELECT mp.`machinehwid`, mp.`prop`, mp.`value`, mp.`numeric`
+ FROM machine_x_hw_prop mp
+ INNER JOIN machine_x_hw mxhw ON (mp.machinehwid = mxhw.machinehwid AND mxhw.machineuuid = :uuid AND mxhw.disconnecttime = 0)
+ INNER JOIN statistic_hw sh ON (mxhw.hwid = sh.hwid AND sh.hwtype = :type)
+ UNION SELECT mxhw.`machinehwid`, hwp.`prop`, hwp.`value`, hwp.`numeric`
+ FROM statistic_hw_prop hwp
+ INNER JOIN machine_x_hw mxhw ON (hwp.hwid = mxhw.hwid AND mxhw.machineuuid = :uuid AND mxhw.disconnecttime = 0)
+ INNER JOIN statistic_hw sh ON (mxhw.hwid = sh.hwid AND sh.hwtype = :type)
+ ",
+ ['type' => HardwareInfo::HDD, 'uuid' => $uuid]);
+ foreach ($ret as $row) {
+ if (!isset($hdds[$row['machinehwid']])) {
+ $hdds[$row['machinehwid']] = ['partitions' => []];
+ }
+ $hdd =& $hdds[$row['machinehwid']];
+ if (preg_match('/^(attr_[0-9]+)_(.*)$/', $row['prop'], $out)) {
+ // SMART attributes
+ if (!isset($hdd[$out[1]])) {
+ $hdd[$out[1]] = [];
+ }
+ $hdd[$out[1]][$out[2]] = $row['numeric'] ?? $row['value'];
+ } elseif (preg_match('/^part_([0-9]+)_(.*)$/', $row['prop'], $out)) {
+ // Partitions
+ if (!isset($hdd['partitions'][$out[1]])) {
+ $hdd['partitions'][$out[1]] = ['id' => 'dev-' . count($hdds) . '-' . $out[1], 'index' => $out[1] + 1];
+ }
+ $hdd['partitions'][$out[1]][$out[2]] = $row['numeric'] ?? $row['value'];
+ } else {
+ $hdd[$row['prop']] = $row['numeric'] ?? $row['value'];
+ }
+ }
+ foreach ($hdds as $k => &$hdd) {
+ $hdd['devid'] = 'k' . $k;
+ $hdd['partitions'] = array_values($hdd['partitions']);
+ }
+ return array_values($hdds);
+ }
+
+ private static function mangleHdd(array &$hdd)
+ {
+ $hours = $hdd['power_on_time//hours'] ?? $hdd['attr_9']['raw'] ?? $hdd['power_on_hours']
+ ?? $hdd['power_on_time']['hours'] ?? null;
+ if ($hours !== null) {
+ $hdd['PowerOnTime'] = '';
+ $val = (int)str_replace('.', '', $hours);
+ if ($val > 8760) {
+ $hdd['PowerOnTime'] .= floor($val / 8760) . 'Y, ';
+ $val %= 8760;
+ }
+ if ($val > 720) {
+ $hdd['PowerOnTime'] .= floor($val / 720) . 'M, ';
+ $val %= 720;
+ }
+ if ($val > 24) {
+ $hdd['PowerOnTime'] .= floor($val / 24) . 'd, ';
+ $val %= 24;
+ }
+ $hdd['PowerOnTime'] .= $val . 'h';
+ }
+ // Sort by start for building pie-chart
+ $xx = array_column($hdd['partitions'], 'start');
+ array_multisort($xx, SORT_ASC, SORT_NUMERIC,
+ $hdd['partitions']);
+ $used = 0;
+ $json = [];
+ $lastEnd = 0;
+ $minDisplaySize = $hdd['size'] / 150;
+ foreach ($hdd['partitions'] as &$part) {
+ $dist = $part['start'] - $lastEnd;
+ if ($dist > $minDisplaySize) {
+ error_log('Dist: ' . Util::readableFileSize($dist));
+ $json[] = ['value' => $dist, 'color' => '#aaa'];
+ }
+ if ($part['size'] > $minDisplaySize) {
+ $json[] = ['value' => $part['size'], 'color' => self::typeToColor($part), 'label' => $part['id']];
+ }
+ $part['size_s'] = Util::readableFileSize($part['size']);
+ $used += $part['size'];
+ $lastEnd = $part['start'] + $part['size'];
+ if (!isset($part['name'])) {
+ $part['name'] = self::mbrType($part['slxtype'] ?? $part['type']);
+ }
+ }
+ $dist = $hdd['size'] - $lastEnd;
+ if ($dist > $minDisplaySize) {
+ $json[] = ['value' => $dist, 'color' => '#aaa'];
+ }
+ $hdd['json'] = json_encode($json);
+ $hdd['size_s'] = Util::readableFileSize($hdd['size']);
+ if ($hdd['size'] - $used > 1000000000) {
+ $hdd['unused_s'] = Util::readableFileSize($hdd['size'] - $used);
+ }
+ // Finally sort by index for table display
+ array_multisort(array_column($hdd['partitions'], 'index'), SORT_ASC,
+ $hdd['partitions']);
+ }
+
+ private static function typeToColor(array $part): string
+ {
+ switch ($part['slxtype'] ?? $part['type']) {
+ case 44:
+ return '#5c1';
+ case 45:
+ return '#0d7';
+ case 82:
+ return '#48f';
+ }
+ return '#e55';
+ }
+
+ private static function eventToIconName($event): string
{
switch ($event) {
case 'session-open':
@@ -441,4 +651,22 @@ class SubPage
return $retval;
}
+ private static function mbrType(string $type): string
+ {
+ switch ($type) {
+ case '44':
+ case '45':
+ return 'OpenSLX-ID' . $type;
+ case '82':
+ return 'Linux Swap';
+ case '83':
+ return 'Linux';
+ case '7':
+ return 'NTFS/Windows';
+ case 'ef':
+ return 'EFI';
+ }
+ return $type;
+ }
+
} \ No newline at end of file