summaryrefslogtreecommitdiffstats
path: root/modules-available/statistics/inc
diff options
context:
space:
mode:
authorSimon Rettberg2021-09-21 16:52:06 +0200
committerSimon Rettberg2021-09-21 16:52:06 +0200
commit0156024a414ea1503e539cb2a30d05a422d4cd16 (patch)
treea92eb114deb3a0e44252a2c3d90928b5c6a673d1 /modules-available/statistics/inc
parent[statistics] Query builder WIP (diff)
downloadslx-admin-0156024a414ea1503e539cb2a30d05a422d4cd16.tar.gz
slx-admin-0156024a414ea1503e539cb2a30d05a422d4cd16.tar.xz
slx-admin-0156024a414ea1503e539cb2a30d05a422d4cd16.zip
Passthrough WIP
Diffstat (limited to 'modules-available/statistics/inc')
-rw-r--r--modules-available/statistics/inc/hardwareinfo.inc.php54
-rw-r--r--modules-available/statistics/inc/hardwareparser.inc.php20
-rw-r--r--modules-available/statistics/inc/hardwarequery.inc.php40
-rw-r--r--modules-available/statistics/inc/pciid.inc.php63
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