summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2017-04-06 17:30:12 +0200
committerSimon Rettberg2017-04-06 17:30:12 +0200
commit4a230b9a4843ade82ff12fda31b771a5a9e58977 (patch)
treea9faaf7b4c9327d63302a785ddcf4e3d35e2407a
parent[statistics_reporting] Fallback to ip if client has no hostname (diff)
downloadslx-admin-4a230b9a4843ade82ff12fda31b771a5a9e58977.tar.gz
slx-admin-4a230b9a4843ade82ff12fda31b771a5a9e58977.tar.xz
slx-admin-4a230b9a4843ade82ff12fda31b771a5a9e58977.zip
[statistics] Handle client screens, manage projectors
-rw-r--r--modules-available/statistics/api.inc.php73
-rw-r--r--modules-available/statistics/hooks/config-tgz.inc.php32
-rw-r--r--modules-available/statistics/inc/devicetype.inc.php6
-rw-r--r--modules-available/statistics/install.inc.php55
-rw-r--r--modules-available/statistics/page.inc.php70
-rw-r--r--modules-available/statistics/templates/machine-main.html30
-rw-r--r--modules-available/statistics/templates/projector-list.html21
7 files changed, 284 insertions, 3 deletions
diff --git a/modules-available/statistics/api.inc.php b/modules-available/statistics/api.inc.php
index 2ac6e782..126c6e91 100644
--- a/modules-available/statistics/api.inc.php
+++ b/modules-available/statistics/api.inc.php
@@ -201,6 +201,79 @@ if ($type{0} === '~') {
}
}
Database::exec('UPDATE machine SET logintime = 0, lastseen = UNIX_TIMESTAMP(), lastboot = 0 WHERE machineuuid = :uuid', array('uuid' => $uuid));
+ } elseif ($type === '~screens') {
+ $screens = Request::post('screen', false, 'array');
+ if (is_array($screens)) {
+ // `devicetype`, `devicename`, `subid`, `machineuuid`
+ // Make sure all screens are in the general hardware table
+ $hwids = array();
+ foreach ($screens as $port => $screen) {
+ if (!array_key_exists('name', $screen))
+ continue;
+ if (array_key_exists($screen['name'], $hwids)) {
+ $hwid = $hwids[$screen['name']];
+ } else {
+ $hwid = (int)Database::insertIgnore('statistic_hw', 'hwid',
+ array('hwtype' => DeviceType::SCREEN, 'hwname' => $screen['name']));
+ $hwids[$screen['name']] = $hwid;
+ }
+ // Now add new entries
+ $machinehwid = Database::insertIgnore('machine_x_hw', 'machinehwid', array(
+ 'hwid' => $hwid,
+ 'machineuuid' => $uuid,
+ 'devpath' => $port,
+ ), array('disconnecttime' => 0));
+ $validProps = array();
+ if (count($screen) > 1) {
+ // Screen has additional properties (resolution, size, etc.)
+ unset($screen['name']);
+ foreach ($screen as $key => $value) {
+ if (!preg_match('/^[a-zA-Z0-9][\x21-\x7e]{0,15}$/', $key)) {
+ echo "No matsch '$key'\n";
+ continue; // Ignore evil key names
+ }
+ $validProps[] = $key;
+ Database::exec("INSERT INTO machine_x_hw_prop (machinehwid, prop, value)"
+ . " VALUES (:id, :key, :value) ON DUPLICATE KEY UPDATE value = VALUES(value)", array(
+ 'id' => $machinehwid,
+ 'key' => $key,
+ 'value' => $value,
+ ));
+ }
+ }
+ // Purge properties that might have existed in the past
+ if (empty($validProps)) {
+ Database::exec("DELETE FROM machine_x_hw_prop WHERE machinehwid = :machinehwid AND prop NOT LIKE '@%'",
+ array('machinehwid' => $machinehwid));
+ } else {
+ $qs = '?' . str_repeat(',?', count($validProps) - 1);
+ array_unshift($validProps, $machinehwid);
+ Database::exec("DELETE FROM machine_x_hw_prop"
+ . " WHERE machinehwid = ? AND prop NOT LIKE '@%' AND prop NOT IN ($qs)",
+ $validProps);
+ }
+ }
+ // Remove/disable stale entries
+ if (empty($hwids)) {
+ // No screens connected at all, purge all screen entries for this machine
+ Database::exec("UPDATE machine_x_hw x, statistic_hw h"
+ . " SET x.disconnecttime = UNIX_TIMESTAMP()"
+ . " WHERE x.machineuuid = :uuid AND x.hwid = h.hwid AND h.hwtype = :type AND x.disconnecttime = 0",
+ array('uuid' => $uuid, 'type' => DeviceType::SCREEN));
+ } else {
+ // Some screens connected, make sure old entries get removed
+ $params = array_values($hwids);
+ array_unshift($params, $uuid);
+ array_unshift($params, DeviceType::SCREEN);
+ $qs = '?' . str_repeat(',?', count($hwids) - 1);
+ Database::exec("UPDATE machine_x_hw x, statistic_hw h"
+ . " SET x.disconnecttime = UNIX_TIMESTAMP()"
+ . " WHERE h.hwid = x.hwid AND x.disconnecttime = 0 AND h.hwtype = ? AND x.machineuuid = ? AND x.hwid NOT IN ($qs)", $params);
+
+ }
+ }
+ } else {
+ die("INVALID ACTION '$type'");
}
die("OK. (RESULT=0)\n");
}
diff --git a/modules-available/statistics/hooks/config-tgz.inc.php b/modules-available/statistics/hooks/config-tgz.inc.php
new file mode 100644
index 00000000..1272a94f
--- /dev/null
+++ b/modules-available/statistics/hooks/config-tgz.inc.php
@@ -0,0 +1,32 @@
+<?php
+
+$res = Database::simpleQuery('SELECT h.hwname FROM statistic_hw h'
+ . " INNER JOIN statistic_hw_prop p ON (h.hwid = p.hwid AND p.prop = :projector)"
+ . " WHERE h.hwtype = :screen ORDER BY h.hwname ASC", array(
+ 'projector' => 'projector',
+ 'screen' => DeviceType::SCREEN,
+));
+
+$content = '';
+while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $content .= $row['hwname'] . "=beamer\n";
+}
+
+if (!empty($content)) {
+ $tmpfile = '/tmp/bwlp-' . md5($content) . '.tar';
+ if (!is_file($tmpfile) || !is_readable($tmpfile) || filemtime($tmpfile) + 86400 < time()) {
+ if (file_exists($tmpfile)) {
+ unlink($tmpfile);
+ }
+ try {
+ $a = new PharData($tmpfile);
+ $a->addFromString("/opt/openslx/beamergui/beamer.conf", $content);
+ $file = $tmpfile;
+ } catch (Exception $e) {
+ EventLog::failure('Could not include beamer.conf in config.tgz', (string)$e);
+ unlink($tmpfile);
+ }
+ } elseif (is_file($tmpfile) && is_readable($tmpfile)) {
+ $file = $tmpfile;
+ }
+}
diff --git a/modules-available/statistics/inc/devicetype.inc.php b/modules-available/statistics/inc/devicetype.inc.php
new file mode 100644
index 00000000..41ee237d
--- /dev/null
+++ b/modules-available/statistics/inc/devicetype.inc.php
@@ -0,0 +1,6 @@
+<?php
+
+class DeviceType
+{
+ const SCREEN = 'SCREEN';
+}
diff --git a/modules-available/statistics/install.inc.php b/modules-available/statistics/install.inc.php
index 7baf046e..5d8ce1bc 100644
--- a/modules-available/statistics/install.inc.php
+++ b/modules-available/statistics/install.inc.php
@@ -12,7 +12,7 @@ $res[] = tableCreate('statistic', "
`dateline` int(10) unsigned NOT NULL,
`typeid` varchar(30) NOT NULL,
`clientip` varchar(40) NOT NULL,
- `machineuuid` varchar(36) CHARACTER SET ascii DEFAULT NULL,
+ `machineuuid` char(36) CHARACTER SET ascii DEFAULT NULL,
`username` varchar(30) NOT NULL,
`data` varchar(255) NOT NULL,
PRIMARY KEY (`logid`),
@@ -24,7 +24,7 @@ $res[] = tableCreate('statistic', "
// Main table containing all known clients
-$res[] = tableCreate('machine', "
+$res[] = $machineCreate = tableCreate('machine', "
`machineuuid` char(36) CHARACTER SET ascii NOT NULL,
`fixedlocationid` int(11) DEFAULT NULL COMMENT 'Manually set location (e.g. roomplanner)',
`subnetlocationid` int(11) DEFAULT NULL COMMENT 'Automatically determined location (e.g. from subnet match),
@@ -61,6 +61,40 @@ $res[] = tableCreate('machine', "
KEY `systemmodel` (`systemmodel`)
");
+$res[] = $machineHwCreate = tableCreate('machine_x_hw', "
+ `machinehwid` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `hwid` int(10) unsigned NOT NULL,
+ `machineuuid` char(36) CHARACTER SET ascii NOT NULL,
+ `devpath` char(50) CHARACTER SET ascii NOT NULL,
+ `disconnecttime` int(10) unsigned NOT NULL COMMENT 'time the device was not connected to the pc anymore for the first time, 0 if it is connected',
+ PRIMARY KEY (`machinehwid`),
+ UNIQUE KEY `hwid` (`hwid`,`machineuuid`,`devpath`),
+ KEY `machineuuid` (`machineuuid`,`hwid`),
+ KEY `disconnecttime` (`disconnecttime`)
+ ");
+
+$res[] = tableCreate('machine_x_hw_prop', "
+ `machinehwid` int(10) unsigned NOT NULL,
+ `prop` char(16) CHARACTER SET ascii NOT NULL,
+ `value` varchar(500) NOT NULL,
+ PRIMARY KEY (`machinehwid`,`prop`)
+");
+
+$res[] = tableCreate('statistic_hw', "
+ `hwid` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `hwtype` char(11) CHARACTER SET ascii NOT NULL,
+ `hwname` varchar(200) NOT NULL,
+ PRIMARY KEY (`hwid`),
+ UNIQUE KEY `hwtype` (`hwtype`,`hwname`)
+");
+
+$res[] = tableCreate('statistic_hw_prop', "
+ `hwid` int(10) unsigned NOT NULL,
+ `prop` char(16) CHARACTER SET ascii NOT NULL,
+ `value` varchar(500) NOT NULL,
+ PRIMARY KEY (`hwid`,`prop`)
+");
+
// PCI-ID cache
$res[] = tableCreate('pciid', "
@@ -71,7 +105,8 @@ $res[] = tableCreate('pciid', "
PRIMARY KEY (`category`,`id`)
");
-if (in_array(UPDATE_DONE, $res)) {
+// need trigger?
+if ($machineCreate === UPDATE_DONE) {
$addTrigger = true;
}
@@ -165,6 +200,20 @@ if ($addTrigger) {
}
}
+if ($machineHwCreate === UPDATE_DONE) {
+ $ret = true;
+ $ret = Database::exec('ALTER TABLE `machine_x_hw`
+ ADD CONSTRAINT `machine_x_hw_ibfk_1` FOREIGN KEY (`hwid`) REFERENCES `statistic_hw` (`hwid`) ON DELETE CASCADE,
+ ADD CONSTRAINT `machine_x_hw_ibfk_2` FOREIGN KEY (`machineuuid`) REFERENCES `machine` (`machineuuid`) ON DELETE CASCADE') && $ret;
+ $ret = Database::exec('ALTER TABLE `machine_x_hw_prop`
+ ADD CONSTRAINT `machine_x_hw_prop_ibfk_1` FOREIGN KEY (`machinehwid`) REFERENCES `machine_x_hw` (`machinehwid`) ON DELETE CASCADE') && $ret;
+ $ret = Database::exec('ALTER TABLE `statistic_hw_prop`
+ ADD CONSTRAINT `statistic_hw_prop_ibfk_1` FOREIGN KEY (`hwid`) REFERENCES `statistic_hw` (`hwid`) ON DELETE CASCADE') && $ret;
+ if ($ret === false) {
+ finalResponse(UPDATE_FAILED, 'Adding foreign key machineuuid to hardware* failed: ' . Database::lastError());
+ }
+}
+
// Create response
if (in_array(UPDATE_DONE, $res)) {
diff --git a/modules-available/statistics/page.inc.php b/modules-available/statistics/page.inc.php
index 87c8e7c4..5ad8bc20 100644
--- a/modules-available/statistics/page.inc.php
+++ b/modules-available/statistics/page.inc.php
@@ -119,6 +119,53 @@ class Page_Statistics extends Page
/* TODO ... */
}
+ /*
+ * TODO: Move to separate unit... hardware configurator?
+ */
+
+ protected function handleProjector($action)
+ {
+ $hwid = Request::post('hwid', false, 'int');
+ if ($hwid === false) {
+ Util::traceError('Param hwid missing');
+ }
+ if ($action === 'addprojector') {
+ Database::exec('INSERT INTO statistic_hw_prop (hwid, prop, value)'
+ . ' VALUES (:hwid, :prop, :value)', array(
+ 'hwid' => $hwid,
+ 'prop' => 'projector',
+ 'value' => 'true',
+ ));
+ } else {
+ Database::exec('DELETE FROM statistic_hw_prop WHERE hwid = :hwid AND prop = :prop', array(
+ 'hwid' => $hwid,
+ 'prop' => 'projector',
+ ));
+ }
+ if (Module::isAvailable('sysconfig')) {
+ ConfigTgz::rebuildAllConfigs();
+ }
+ Util::redirect('?do=statistics&show=projectors');
+ }
+
+ protected function showProjectors()
+ {
+ $res = Database::simpleQuery('SELECT h.hwname, h.hwid FROM statistic_hw h'
+ . " INNER JOIN statistic_hw_prop p ON (h.hwid = p.hwid AND p.prop = :projector)"
+ . " WHERE h.hwtype = :screen ORDER BY h.hwname ASC", array(
+ 'projector' => 'projector',
+ 'screen' => DeviceType::SCREEN,
+ ));
+ $data = array(
+ 'projectors' => $res->fetchAll(PDO::FETCH_ASSOC)
+ );
+ Render::addTemplate('projector-list', $data);
+ }
+
+ /*
+ * End TODO
+ */
+
protected function doPreprocess()
{
$this->initConstants();
@@ -140,6 +187,8 @@ class Page_Statistics extends Page
));
Message::addSuccess('notes-saved');
Util::redirect('?do=Statistics&uuid=' . $uuid);
+ } elseif ($action === 'addprojector' || $action === 'delprojector') {
+ $this->handleProjector($action);
}
// Fix online state of machines that crashed -- TODO: Make cronjob for this
Database::exec("UPDATE machine SET lastboot = 0 WHERE lastseen < UNIX_TIMESTAMP() - 610");
@@ -174,6 +223,9 @@ class Page_Statistics extends Page
Render::closeTag('div');
$this->showMachineList($filterSet);
return;
+ } elseif ($show === 'projectors') {
+ $this->showProjectors();
+ return;
}
Render::openTag('div', array('class' => 'row'));
$this->showFilter('stat', $filterSet);
@@ -723,6 +775,24 @@ class Page_Statistics extends Page
}
$client['locations'] = $output;
}
+ // Screens TODO Move everything else to hw table instead of blob parsing above
+ // `devicetype`, `devicename`, `subid`, `machineuuid`
+ $res = Database::simpleQuery("SELECT m.hwid, h.hwname, m.devpath AS connector, m.disconnecttime,"
+ . " p.value AS resolution, q.prop AS projector FROM machine_x_hw m"
+ . " INNER JOIN statistic_hw h ON (m.hwid = h.hwid AND h.hwtype = :screen)"
+ . " LEFT JOIN machine_x_hw_prop p ON (m.machinehwid = p.machinehwid AND p.prop = 'resolution')"
+ . " LEFT JOIN statistic_hw_prop q ON (m.hwid = q.hwid AND q.prop = 'projector')"
+ . " WHERE m.machineuuid = :uuid",
+ array('screen' => DeviceType::SCREEN, 'uuid' => $uuid));
+ $client['screens'] = array();
+ $ports = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ if ($row['disconnecttime'] != 0)
+ continue;
+ $ports[] = $row['connector'];
+ $client['screens'][] = $row;
+ }
+ array_multisort($ports, SORT_ASC, $client['screens']);
// Throw output at user
Render::addTemplate('machine-main', $client);
// Sessions
diff --git a/modules-available/statistics/templates/machine-main.html b/modules-available/statistics/templates/machine-main.html
index 0b333a27..56454e78 100644
--- a/modules-available/statistics/templates/machine-main.html
+++ b/modules-available/statistics/templates/machine-main.html
@@ -130,6 +130,36 @@
<td class="text-nowrap">{{lang_64bitSupport}}</td>
<td>{{kvmstate}}</td>
</tr>
+ <tr>
+ <td class="text-nowrap">{{lang_screens}}</td>
+ <td>
+ <form method="post" action="?do=statistics" id="delprojector">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="action" value="delprojector">
+ </form>
+ <form method="post" action="?do=statistics" id="addprojector">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="action" value="addprojector">
+ </form>
+ {{#screens}}
+ <div class="small">
+ <div class="pull-right btn-group btn-group-xs">
+ {{#projector}}
+ <span class="btn btn-default">{{lang_projector}}</span>
+ <button form="delprojector" type="submit" name="hwid" value="{{hwid}}"
+ class="btn btn-danger"><span class="glyphicon glyphicon-remove"></span></button>
+ {{/projector}}
+ {{^projector}}
+ <button form="addprojector" type="submit" name="hwid" value="{{hwid}}"
+ class="btn btn-success"><span class="glyphicon glyphicon-plus"></span> {{lang_projector}}</button>
+ {{/projector}}
+ </div>
+ {{connector}}: <b>{{hwname}}</b> {{resolution}}
+ <div class="clearfix"></div>
+ </div>
+ {{/screens}}
+ </td>
+ </tr>
</table>
<h4>{{lang_devices}}</h4>
{{#lspci1}}
diff --git a/modules-available/statistics/templates/projector-list.html b/modules-available/statistics/templates/projector-list.html
new file mode 100644
index 00000000..bc9ecdbd
--- /dev/null
+++ b/modules-available/statistics/templates/projector-list.html
@@ -0,0 +1,21 @@
+<div class="panel panel-default">
+ <div class="panel-heading">{{lang_projectors}}</div>
+ <div class="panel-body">
+ <form method="post" action="?do=statistics" id="delprojector">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="action" value="delprojector">
+ <p>{{lang_thoseAreProjectors}}</p>
+ {{#projectors}}
+ <div>
+ <button type="submit" name="hwid" value="{{hwid}}" class="btn btn-danger">
+ <span class="glyphicon glyphicon-remove"></span>
+ </button>
+ {{hwname}}
+ </div>
+ {{/projectors}}
+ {{^projectors}}
+ <div class="alert alert-info">{{lang_noProjectorsDefined}}</div>
+ {{/projectors}}
+ </form>
+ </div>
+</div> \ No newline at end of file