diff options
author | Simon Rettberg | 2021-09-21 16:52:06 +0200 |
---|---|---|
committer | Simon Rettberg | 2021-09-21 16:52:06 +0200 |
commit | 0156024a414ea1503e539cb2a30d05a422d4cd16 (patch) | |
tree | a92eb114deb3a0e44252a2c3d90928b5c6a673d1 /modules-available/statistics/inc | |
parent | [statistics] Query builder WIP (diff) | |
download | slx-admin-0156024a414ea1503e539cb2a30d05a422d4cd16.tar.gz slx-admin-0156024a414ea1503e539cb2a30d05a422d4cd16.tar.xz slx-admin-0156024a414ea1503e539cb2a30d05a422d4cd16.zip |
Passthrough WIP
Diffstat (limited to 'modules-available/statistics/inc')
4 files changed, 157 insertions, 20 deletions
diff --git a/modules-available/statistics/inc/hardwareinfo.inc.php b/modules-available/statistics/inc/hardwareinfo.inc.php index 5ef94365..90b8975b 100644 --- a/modules-available/statistics/inc/hardwareinfo.inc.php +++ b/modules-available/statistics/inc/hardwareinfo.inc.php @@ -13,4 +13,58 @@ class HardwareInfo const HDD = 'HDD'; const CPU = 'CPU'; + /** + * Get a KCL modification string for the given machine, enabling GVT, PCI passthrough etc. + * You can provide a UUID and/or MAC, or nothing. If nothing is provided, + * the "uuid" and "mac" GET parameters will be used. If both are provided, + * the resulting machine that has the greatest "lastseen" value will be used. + * @param ?string $uuid UUID of machine + * @param ?string $mac MAC of machine + * @return string + */ + public static function getKclModifications($uuid = null, $mac = null): string + { + if ($uuid === null && $mac === null) { + $uuid = Request::get('uuid', '', 'string'); + $mac = Request::get('mac', '', 'string'); + $mac = str_replace(':', '-', $mac); + } + $res = Database::simpleQuery("SELECT machineuuid, lastseen FROM machine + WHERE machineuuid = :uuid OR macaddr = :mac", ['uuid' => $uuid, 'mac' => $mac]); + $best = null; + foreach ($res as $row) { + if ($best === null || $best['lastseen'] < $row['lastseen']) { + $best = $row; + } + } + if ($best === null) + return ''; + $hw = new HardwareQuery(self::PCI_DEVICE, $best['machineuuid'], true); + // TODO: Get list of enabled pass through groups for this client's location + $hw->addWhere(true, '@PASSTHROUGH', 'IN', ['GPU', 'GVT']); + $hw->addColumn(true, 'vendor'); + $hw->addColumn(true, 'device'); + $res = $hw->query(); + $passthrough = []; + $gvt = false; + foreach ($res as $row) { + if ($row['@PASSTHROUGH'] === 'GVT') { + $gvt = true; + } else { + $passthrough[] = $row['vendor'] . ':' . $row['device']; + } + } + $kcl = ''; + if ($gvt || !empty($passthrough)) { + $kcl = '-iommu -intel_iommu iommu=pt intel_iommu=on'; // TODO AMD + } + if (!empty($passthrough)) { + $kcl .= ' vfio-pci.ids=' . implode(',', $passthrough); + } + if ($gvt) { + $kcl .= ' i915.enable_gvt=1'; + } + return $kcl; + } + } diff --git a/modules-available/statistics/inc/hardwareparser.inc.php b/modules-available/statistics/inc/hardwareparser.inc.php index d356e226..87dcc4cf 100644 --- a/modules-available/statistics/inc/hardwareparser.inc.php +++ b/modules-available/statistics/inc/hardwareparser.inc.php @@ -308,29 +308,29 @@ class HardwareParser foreach ($out as $entry) { if (!isset($pci[$entry['class']])) { $class = 'c.' . $entry['class']; - $res = Page_Statistics::getPciId('CLASS', $class); - if ($res === false || $res['dateline'] < $NOW) { + $res = PciId::getPciId('CLASS', $class); + if ($res === false) { $pci[$entry['class']]['lookupClass'] = 'do-lookup'; $pci[$entry['class']]['class'] = $class; } else { - $pci[$entry['class']]['class'] = $res['value']; + $pci[$entry['class']]['class'] = $res; } } $new = array( 'ven' => $entry['ven'], 'dev' => $entry['ven'] . ':' . $entry['dev'], ); - $res = Page_Statistics::getPciId('VENDOR', $new['ven']); - if ($res === false || $res['dateline'] < $NOW) { + $res = PciId::getPciId('VENDOR', $new['ven']); + if ($res === false) { $new['lookupVen'] = 'do-lookup'; } else { - $new['ven'] = $res['value']; + $new['ven'] = $res; } - $res = Page_Statistics::getPciId('DEVICE', $new['dev']); - if ($res === false || $res['dateline'] < $NOW) { + $res = PciId::getPciId('DEVICE', $new['dev']); + if ($res === false) { $new['lookupDev'] = 'do-lookup'; } else { - $new['dev'] = $res['value'] . ' (' . $new['dev'] . ')'; + $new['dev'] = $res . ' (' . $new['dev'] . ')'; } $pci[$entry['class']]['entries'][] = $new; } @@ -717,7 +717,7 @@ class HardwareParser $hwid = self::writeGlobalHardwareData(HardwareInfo::PCI_DEVICE, self::propsFromArray($dev, 'vendor', 'device', 'rev', 'class')); $mappingId = self::writeLocalHardwareData($uuid, $hwid, $dev['slot'] ?? 'unknown', - self::propsFromArray($dev, 'slot', 'subsystem', 'subsystem_vendor')); + self::propsFromArray($dev, 'slot', 'subsystem', 'subsystem_vendor', 'iommu_group')); $pciHwIds[] = $mappingId; } self::markDisconnected($uuid, HardwareInfo::PCI_DEVICE, $pciHwIds); diff --git a/modules-available/statistics/inc/hardwarequery.inc.php b/modules-available/statistics/inc/hardwarequery.inc.php index 7ccde2f6..b0b7d6ee 100644 --- a/modules-available/statistics/inc/hardwarequery.inc.php +++ b/modules-available/statistics/inc/hardwarequery.inc.php @@ -16,13 +16,15 @@ class HardwareQuery public function __construct($type, $uuid = null, $connectedOnly = true) { if ($connectedOnly) { - $this->where[] = 'mxhw.disconnecttime = 0'; + $this->joins['mxhw_join'] = "INNER JOIN machine_x_hw mxhw ON (mxhw.hwid = shw.hwid AND mxhw.disconnecttime = 0)"; + } else { + $this->joins['mxhw_join'] = "INNER JOIN machine_x_hw mxhw ON (mxhw.hwid = shw.hwid)"; } if ($uuid !== null) { $this->where[] = 'mxhw.machineuuid = :uuid'; $this->args['uuid'] = $uuid; } - $this->joins[] = "INNER JOIN statistic_hw shw ON (mxhw.hwid = shw.hwid AND shw.hwtype = :hwtype)"; + $this->where[] = 'shw.hwtype = :hwtype'; $this->args['hwtype'] = $type; } @@ -44,7 +46,13 @@ class HardwareQuery } } - public function addWhere(bool $global, string $prop, string $op, string $value) + /** + * @param bool $global + * @param string $prop + * @param string $op + * @param string|string[] $value + */ + public function addWhere(bool $global, string $prop, string $op, $value) { if (isset($this->columns[$prop])) return; @@ -54,7 +62,7 @@ class HardwareQuery $vid = $this->id(); $valueCol = ($op === '<' || $op === '>' || $op === '<=' || $op === '>=') ? 'numeric' : 'value'; $this->joins[$prop] = "INNER JOIN $table $tid ON ($srcTable.$column = $tid.$column AND - $tid.prop = :$pid AND $tid.`$valueCol` $op :$vid)"; + $tid.prop = :$pid AND $tid.`$valueCol` $op (:$vid))"; $this->args[$pid] = $prop; $this->args[$vid] = $value; $this->columns[$prop] = "$tid.`value` AS `$prop`"; @@ -85,7 +93,7 @@ class HardwareQuery $this->fillTableVars($global, $srcTable, $table, $column); $tid = $this->id(); $pid = $this->id(); - $this->joins[$prop] = "INNER JOIN $table $tid ON ($srcTable.$column = $tid.$column AND $tid.prop = :$pid)"; + $this->joins[$prop] = "LEFT JOIN $table $tid ON ($srcTable.$column = $tid.$column AND $tid.prop = :$pid)"; $this->args[$pid] = $prop; $this->columns[$prop] = "$tid.`value` AS `$prop`"; } @@ -93,13 +101,25 @@ class HardwareQuery /** * @return false|PDOStatement */ - public function query() + public function query(string $groupBy = '') { - $this->columns[] = 'mxhw.machineuuid'; - $query = 'SELECT ' . implode(', ', $this->columns) - . ' FROM machine_x_hw mxhw ' + $columns = $this->columns; + $columns[] = 'mxhw.machineuuid'; + $columns[] = 'shw.hwid'; + if (empty($groupBy) || $groupBy === 'mxhw.machinehwid') { + $columns[] = 'mxhw.disconnecttime'; + } else { + $columns[] = 'Sum(If(mxhw.disconnecttime = 0, 1, 0)) AS connected_count'; + } + if (!empty($groupBy)) { + $columns[] = 'Count(*) AS group_count'; + $groupBy = " GROUP BY $groupBy"; + } + $query = 'SELECT ' . implode(', ', $columns) + . ' FROM statistic_hw shw ' . implode(' ', $this->joins) - . ' WHERE ' . implode(' AND ', $this->where); + . ' WHERE ' . implode(' AND ', $this->where) + . $groupBy; return Database::simpleQuery($query, $this->args); } diff --git a/modules-available/statistics/inc/pciid.inc.php b/modules-available/statistics/inc/pciid.inc.php new file mode 100644 index 00000000..6bf852e6 --- /dev/null +++ b/modules-available/statistics/inc/pciid.inc.php @@ -0,0 +1,63 @@ +<?php + +class PciId +{ + + const DEVICE = 'DEVICE'; + const VENDOR = 'VENDOR'; + const DEVCLASS = 'CLASS'; + + + /** + * @param string $cat type of query - self::DEVICE, self::VENDOR or self::DEVCLASS + * @param string $id the id to query - depends on $cat + * @return string|false Name of Class/Vendor/Device, false if not found + */ + public static function getPciId(string $cat, string $id, bool $dnsQuery = false) + { + static $cache = []; + if ($cat === self::DEVCLASS && $id[1] !== '.') { + $id = 'c.' . $id; + } + $key = $cat . '-' . $id; + if (isset($cache[$key])) + return $cache[$key]; + $row = Database::queryFirst('SELECT value, dateline FROM pciid WHERE category = :cat AND id = :id LIMIT 1', + array('cat' => $cat, 'id' => $id)); + if ($row !== false && $row['dateline'] >= time()) { + return $cache[$key] = $row['value']; + } + if (!$dnsQuery) + return false; + // Unknown, query + if ($cat === self::DEVICE && preg_match('/^([a-f0-9]{4}):([a-f0-9]{4})$/', $id, $out)) { + $host = $out[2] . '.' . $out[1]; + } elseif ($cat === self::VENDOR && preg_match('/^([a-f0-9]{4})$/', $id, $out)) { + $host = $out[1]; + } elseif ($cat === self::DEVCLASS && preg_match('/^c\.([a-f0-9]{2})([a-f0-9]{2})$/', $id, $out)) { + $host = $out[2] . '.' . $out[1] . '.c'; + } else { + error_log("getPciId called with unknown format: ($cat) ($id)"); + return false; + } + $res = dns_get_record($host . '.pci.id.ucw.cz', DNS_TXT); + if (!is_array($res)) + return false; + foreach ($res as $entry) { + if (isset($entry['txt']) && substr($entry['txt'], 0, 2) === 'i=') { + $string = substr($entry['txt'], 2); + Database::exec('INSERT INTO pciid (category, id, value, dateline) VALUES (:cat, :id, :value, :timeout)' + . ' ON DUPLICATE KEY UPDATE value = VALUES(value), dateline = VALUES(dateline)', + array( + 'cat' => $cat, + 'id' => $id, + 'value' => $string, + 'timeout' => time() + mt_rand(10, 30) * 86400, + ), true); + return $cache[$key] = $string; + } + } + return $cache[$key] = ($row['value'] ?? false); + } + +}
\ No newline at end of file |