diff options
author | Simon Rettberg | 2017-04-06 17:30:12 +0200 |
---|---|---|
committer | Simon Rettberg | 2017-04-06 17:30:12 +0200 |
commit | 4a230b9a4843ade82ff12fda31b771a5a9e58977 (patch) | |
tree | a9faaf7b4c9327d63302a785ddcf4e3d35e2407a /modules-available/statistics | |
parent | [statistics_reporting] Fallback to ip if client has no hostname (diff) | |
download | slx-admin-4a230b9a4843ade82ff12fda31b771a5a9e58977.tar.gz slx-admin-4a230b9a4843ade82ff12fda31b771a5a9e58977.tar.xz slx-admin-4a230b9a4843ade82ff12fda31b771a5a9e58977.zip |
[statistics] Handle client screens, manage projectors
Diffstat (limited to 'modules-available/statistics')
-rw-r--r-- | modules-available/statistics/api.inc.php | 73 | ||||
-rw-r--r-- | modules-available/statistics/hooks/config-tgz.inc.php | 32 | ||||
-rw-r--r-- | modules-available/statistics/inc/devicetype.inc.php | 6 | ||||
-rw-r--r-- | modules-available/statistics/install.inc.php | 55 | ||||
-rw-r--r-- | modules-available/statistics/page.inc.php | 70 | ||||
-rw-r--r-- | modules-available/statistics/templates/machine-main.html | 30 | ||||
-rw-r--r-- | modules-available/statistics/templates/projector-list.html | 21 |
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 |