summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Klinger2016-07-15 14:18:29 +0200
committerChristian Klinger2016-07-15 14:19:27 +0200
commit6e73dfb95b22524322c2de7a09db04af462b50cc (patch)
tree7078ad789cf0ea6f2df3ab35f2d76e1f0486b425
parent[syslog] API: Write machineuuid to DB (diff)
downloadslx-admin-6e73dfb95b22524322c2de7a09db04af462b50cc.tar.gz
slx-admin-6e73dfb95b22524322c2de7a09db04af462b50cc.tar.xz
slx-admin-6e73dfb95b22524322c2de7a09db04af462b50cc.zip
Merging some refactoring + new filter functionality.
-rw-r--r--modules-available/statistics/inc/filterset.inc.php50
-rw-r--r--modules-available/statistics/inc/parser.inc.php256
-rw-r--r--modules-available/statistics/lang/de/template-tags.json4
-rw-r--r--modules-available/statistics/lang/en/template-tags.json2
-rw-r--r--modules-available/statistics/page.inc.php532
-rw-r--r--modules-available/statistics/templates/clientlist.html237
-rw-r--r--modules-available/statistics/templates/cpumodels.html4
-rw-r--r--modules-available/statistics/templates/filterbox.html249
-rw-r--r--modules-available/statistics/templates/id44.html2
-rw-r--r--modules-available/statistics/templates/kvmstate.html2
-rw-r--r--modules-available/statistics/templates/memory.html2
-rw-r--r--modules-available/statistics/templates/summary.html6
12 files changed, 679 insertions, 667 deletions
diff --git a/modules-available/statistics/inc/filterset.inc.php b/modules-available/statistics/inc/filterset.inc.php
new file mode 100644
index 00000000..ac928ac4
--- /dev/null
+++ b/modules-available/statistics/inc/filterset.inc.php
@@ -0,0 +1,50 @@
+<?php
+
+class FilterSet
+{
+ private $filters;
+ private $sortDirection;
+ private $sortColumn;
+
+ public function __construct($filters) {
+ $this->filters = $filters;
+ }
+
+ public function setSort($col, $direction) {
+ $this->sortDirection = $direction === 'DESC' ? 'DESC' : 'ASC';
+
+ if (array_key_exists($col, Page_Statistics::$columns)) {
+ $isMapped = array_key_exists('map_sort', Page_Statistics::$columns[$col]);
+ $this->sortColumn = $isMapped ? Page_Statistics::$columns[$col]['map_sort'] : $col;
+ } else {
+ /* default sorting column is clientip */
+ $this->sortColumn = 'clientip';
+ }
+
+ }
+ public function makeFragments(&$where, &$join, &$sort, &$args) {
+ /* generate where clause & arguments */
+ $where = '';
+ $joins = [];
+ $sort = "";
+ $args = [];
+ if (empty($this->filters)) {
+ $where = ' 1 ';
+ } else {
+ foreach ($this->filters as $filter) {
+ $sep = ($where != '' ? ' AND ' : '');
+ $where .= $sep . $filter->whereClause($args, $joins);
+ }
+ }
+ $join = implode('', array_unique($joins));
+
+
+ $sort = " ORDER BY " . $this->sortColumn . " " . $this->sortDirection;
+ }
+ public function getSortDirection() {
+ return $this->sortDirection;
+ }
+ public function getSortColumn() {
+ return $this->sortColumn;
+ }
+}
diff --git a/modules-available/statistics/inc/parser.inc.php b/modules-available/statistics/inc/parser.inc.php
new file mode 100644
index 00000000..1ecfbbf8
--- /dev/null
+++ b/modules-available/statistics/inc/parser.inc.php
@@ -0,0 +1,256 @@
+<?php
+
+class Parser {
+ public static function parseCpu(&$row, $data)
+ {
+ if (0 >= preg_match_all('/^(.+):\s+(\d+)$/im', $data, $out, PREG_SET_ORDER)) {
+ return;
+ }
+ foreach ($out as $entry) {
+ $row[str_replace(' ', '', $entry[1])] = $entry[2];
+ }
+ }
+
+ public static function parseDmiDecode(&$row, $data)
+ {
+ $lines = preg_split("/[\r\n]+/", $data);
+ $section = false;
+ $ramOk = false;
+ $ramForm = $ramType = $ramSpeed = $ramClockSpeed = false;
+ foreach ($lines as $line) {
+ if (empty($line)) {
+ continue;
+ }
+ if ($line{0} !== "\t" && $line{0} !== ' ') {
+ $section = $line;
+ $ramOk = false;
+ if (($ramForm || $ramType) && ($ramSpeed || $ramClockSpeed)) {
+ if (isset($row['ramtype']) && !$ramClockSpeed) {
+ continue;
+ }
+ $row['ramtype'] = $ramType . ' ' . $ramForm;
+ if ($ramClockSpeed) {
+ $row['ramtype'] .= ', ' . $ramClockSpeed;
+ } elseif ($ramSpeed) {
+ $row['ramtype'] .= ', ' . $ramSpeed;
+ }
+ $ramForm = false;
+ $ramType = false;
+ $ramClockSpeed = false;
+ }
+ continue;
+ }
+ if ($section === 'System Information' || $section === 'Base Board Information') {
+ if (empty($row['pcmodel']) && preg_match('/^\s*Product Name: +(\S.+?) *$/i', $line, $out)) {
+ $row['pcmodel'] = $out[1];
+ }
+ if (empty($row['manufacturer']) && preg_match('/^\s*Manufacturer: +(\S.+?) *$/i', $line, $out)) {
+ $row['manufacturer'] = $out[1];
+ }
+ } elseif ($section === 'Physical Memory Array') {
+ if (!$ramOk && preg_match('/Use: System Memory/i', $line)) {
+ $ramOk = true;
+ }
+ if ($ramOk && preg_match('/^\s*Number Of Devices: +(\S.+?) *$/i', $line, $out)) {
+ $row['ramslotcount'] = $out[1];
+ }
+ if ($ramOk && preg_match('/^\s*Maximum Capacity: +(\S.+?)\s*$/i', $line, $out)) {
+ $row['maxram'] = preg_replace('/([MGT])B/', '$1iB', $out[1]);
+ }
+ } elseif ($section === 'Memory Device') {
+ if (preg_match('/^\s*Size:\s*(.*?)\s*$/i', $line, $out)) {
+ $row['extram'] = true;
+ if (preg_match('/(\d+)\s*(\w)i?B/i', $out[1], $out)) {
+ $out[2] = strtoupper($out[2]);
+ if ($out[2] === 'K' || ($out[2] === 'M' && $out[1] < 500)) {
+ $ramForm = $ramType = $ramSpeed = $ramClockSpeed = false;
+ continue;
+ }
+ if ($out[2] === 'M' && $out[1] >= 1024) {
+ $out[2] = 'G';
+ $out[1] = floor(($out[1] + 100) / 1024);
+ }
+ $row['ramslot'][]['size'] = $out[1] . ' ' . strtoupper($out[2]) . 'iB';
+ } elseif (!isset($row['ramslot']) || (count($row['ramslot']) < 8 && (!isset($row['ramslotcount']) || $row['ramslotcount'] <= 8))) {
+ $row['ramslot'][]['size'] = '_____';
+ }
+ }
+ if (preg_match('/^\s*Form Factor:\s*(.*?)\s*$/i', $line, $out) && $out[1] !== 'Unknown') {
+ $ramForm = $out[1];
+ }
+ if (preg_match('/^\s*Type:\s*(.*?)\s*$/i', $line, $out) && $out[1] !== 'Unknown') {
+ $ramType = $out[1];
+ }
+ if (preg_match('/^\s*Speed:\s*(\d.*?)\s*$/i', $line, $out)) {
+ $ramSpeed = $out[1];
+ }
+ if (preg_match('/^\s*Configured Clock Speed:\s*(\d.*?)\s*$/i', $line, $out)) {
+ $ramClockSpeed = $out[1];
+ }
+ }
+ }
+ if (empty($row['ramslotcount'])) {
+ $row['ramslotcount'] = count($row['ramslot']);
+ }
+ }
+
+ public static function parseHdd(&$row, $data)
+ {
+ $hdds = array();
+ // Could have more than one disk - linear scan
+ $lines = preg_split("/[\r\n]+/", $data);
+ $dev = false;
+ $i = 0;
+ foreach ($lines as $line) {
+ if (preg_match('/^Disk (\S+):.* (\d+) bytes/i', $line, $out)) {
+ // disk total size and name
+ unset($hdd);
+ $unit = 0;
+ $hdd = array(
+ 'devid' => 'devid-' . ++$i,
+ 'dev' => $out[1],
+ 'size' => round($out[2] / (1024 * 1024 * 1024)),
+ 'used' => 0,
+ 'partitions' => array(),
+ 'json' => array(),
+ );
+ $hdds[] = &$hdd;
+ } elseif (preg_match('/^Units =.*= (\d+) bytes/i', $line, $out)) {
+ // Unit for start and end
+ $unit = $out[1] / (1024 * 1024); // Convert so that multiplying by unit yields MiB
+ } elseif (isset($hdd) && $unit !== 0 && preg_match(',^/dev/(\S+)\s+.*\s(\d+)[\+\-]?\s+(\d+)[\+\-]?\s+\d+[\+\-]?\s+([0-9a-f]+)\s+(.*)$,i', $line, $out)) {
+ // Some partition
+ $type = strtolower($out[4]);
+ if ($type === '5' || $type === 'f' || $type === '85') {
+ continue;
+ }
+ $partsize = round(($out[3] - $out[2]) * $unit);
+ $hdd['partitions'][] = array(
+ 'id' => $out[1],
+ 'name' => $out[1],
+ 'size' => round($partsize / 1024, $partsize < 1024 ? 1 : 0),
+ 'type' => ($type === '44' ? 'OpenSLX' : $out[5]),
+ );
+ $hdd['json'][] = array(
+ 'label' => $out[1],
+ 'value' => $partsize,
+ 'color' => ($type === '44' ? '#4d4' : ($type === '82' ? '#48f' : '#e55')),
+ );
+ $hdd['used'] += $partsize;
+ }
+ }
+ unset($hdd);
+ $i = 0;
+ foreach ($hdds as &$hdd) {
+ $hdd['used'] = round($hdd['used'] / 1024);
+ $free = $hdd['size'] - $hdd['used'];
+ if ($free > 5) {
+ $hdd['partitions'][] = array(
+ 'id' => 'free-id-' . $i,
+ 'name' => Dictionary::translate('unused'),
+ 'size' => $free,
+ 'type' => '-',
+ );
+ $hdd['json'][] = array(
+ 'label' => 'free-id-' . $i,
+ 'value' => $free * 1024,
+ 'color' => '#aaa',
+ );
+ ++$i;
+ }
+ $hdd['json'] = json_encode($hdd['json']);
+ }
+ unset($hdd);
+ $row['hdds'] = &$hdds;
+ }
+
+ public static function parsePci(&$pci1, &$pci2, $data)
+ {
+ preg_match_all('/[a-f0-9\:\.]{7}\s+"(Class\s*)?(?<class>[a-f0-9]{4})"\s+"(?<ven>[a-f0-9]{4})"\s+"(?<dev>[a-f0-9]{4})"/is', $data, $out, PREG_SET_ORDER);
+ $NOW = time();
+ $pci = array();
+ 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) {
+ $pci[$entry['class']]['lookupClass'] = 'do-lookup';
+ $pci[$entry['class']]['class'] = $class;
+ } else {
+ $pci[$entry['class']]['class'] = $res['value'];
+ }
+ }
+ $new = array(
+ 'ven' => $entry['ven'],
+ 'dev' => $entry['ven'] . ':' . $entry['dev'],
+ );
+ $res = Page_Statistics::getPciId('VENDOR', $new['ven']);
+ if ($res === false || $res['dateline'] < $NOW) {
+ $new['lookupVen'] = 'do-lookup';
+ } else {
+ $new['ven'] = $res['value'];
+ }
+ $res = Page_Statistics::getPciId('DEVICE', $new['ven'] . ':' . $new['dev']);
+ if ($res === false || $res['dateline'] < $NOW) {
+ $new['lookupDev'] = 'do-lookup';
+ } else {
+ $new['dev'] = $res['value'];
+ }
+ $pci[$entry['class']]['entries'][] = $new;
+ }
+ ksort($pci);
+ foreach ($pci as $class => $entry) {
+ if ($class === '0300' || $class === '0200' || $class === '0403') {
+ $pci1[] = $entry;
+ } else {
+ $pci2[] = $entry;
+ }
+ }
+ }
+
+ public function parseSmartctl(&$hdds, $data)
+ {
+ $lines = preg_split("/[\r\n]+/", $data);
+ $i = 0;
+ foreach ($lines as $line) {
+ if (preg_match('/^NEXTHDD=(.+)$/', $line, $out)) {
+ unset($dev);
+ foreach ($hdds as &$hdd) {
+ if ($hdd['dev'] === $out[1]) {
+ $dev = &$hdd;
+ }
+ }
+ continue;
+ }
+ if (!isset($dev)) {
+ continue;
+ }
+ if (preg_match('/^([A-Z][^:]+):\s*(.*)$/', $line, $out)) {
+ $dev['s_' . preg_replace('/\s|-|_/', '', $out[1])] = $out[2];
+ } elseif (preg_match('/^\s*\d+\s+(\S+)\s+\S+\s+\d+\s+\d+\s+\d+\s+\S+\s+(\d+)(\s|$)/', $line, $out)) {
+ $dev['s_' . preg_replace('/\s|-|_/', '', $out[1])] = $out[2];
+ }
+ }
+ // Format strings
+ foreach ($hdds as &$hdd) {
+ if (isset($hdd['s_PowerOnHours'])) {
+ $hdd['PowerOnTime'] = '';
+ $val = (int)$hdd['s_PowerOnHours'];
+ 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';
+ }
+ }
+ }
+
+}
diff --git a/modules-available/statistics/lang/de/template-tags.json b/modules-available/statistics/lang/de/template-tags.json
index 7f4d13e9..85229a49 100644
--- a/modules-available/statistics/lang/de/template-tags.json
+++ b/modules-available/statistics/lang/de/template-tags.json
@@ -59,6 +59,8 @@
"lang_reallocatedSectors": "Defekte Sektoren",
"lang_refresh": "Ansicht aktualisieren",
"lang_serialNo": "Serien-Nr",
+ "lang_showList": "Liste",
+ "lang_showVisualization": "Visualisierung",
"lang_sockets": "Sockel",
"lang_tempPart": "Temp. Partition",
"lang_tempPartStats": "Tempor\u00e4re Partition",
@@ -71,4 +73,4 @@
"lang_virtualCores": "Virtuelle Kerne",
"lang_when": "Wann",
"lang_withBadSectors": "Clients mit potentiell defekten Festplatten (mehr als 10 defekte Sektoren)"
-} \ No newline at end of file
+}
diff --git a/modules-available/statistics/lang/en/template-tags.json b/modules-available/statistics/lang/en/template-tags.json
index 3e26ae03..6178d4c3 100644
--- a/modules-available/statistics/lang/en/template-tags.json
+++ b/modules-available/statistics/lang/en/template-tags.json
@@ -71,4 +71,4 @@
"lang_virtualCores": "Virtual cores",
"lang_when": "When",
"lang_withBadSectors": "Clients with potentially bad HDDs (more than 10 reallocated sectors)"
-} \ No newline at end of file
+}
diff --git a/modules-available/statistics/page.inc.php b/modules-available/statistics/page.inc.php
index e4a47ca5..ca0ea96e 100644
--- a/modules-available/statistics/page.inc.php
+++ b/modules-available/statistics/page.inc.php
@@ -2,6 +2,7 @@
global $STATS_COLORS, $SIZE_ID44, $SIZE_RAM;
global $unique_key;
+
$STATS_COLORS = array();
for ($i = 0; $i < 10; ++$i) {
$STATS_COLORS[] = '#55' . sprintf('%02s%02s', dechex((($i + 1) * ($i + 1)) / .3922), dechex(abs((5 - $i) * 51)));
@@ -18,6 +19,8 @@ class Page_Statistics extends Page
public static $op_stringcmp;
public static $columns;
+ private $query;
+
/* PHP sucks, no static, const array definitions... Or am I missing something? */
public function initConstants()
{
@@ -56,11 +59,6 @@ class Page_Statistics extends Page
'type' => 'int',
'column' => true,
],
-// 'mbram' => [
-// 'op' => Page_Statistics::$op_ordinal,
-// 'type' => 'int',
-// 'column' => true,
-// ],
'systemmodel' => [
'op' => Page_Statistics::$op_stringcmp,
'type' => 'string',
@@ -148,24 +146,68 @@ class Page_Statistics extends Page
return;
}
- $filter = Request::get('filter', false, 'string');
- $show = Request::get('show', 'stat', 'string');
- if ($filter !== false || $show == 'list') {
- $argument = Request::get('argument', false, 'string');
- $this->showMachineList($filter, $argument);
+ /* read filter */
+ $this->query = Request::any('filters');
+ $sortColumn = Request::any('sortColumn');
+ $sortDirection = Request::any('sortDirection');
+ $filters = Filter::parseQuery($this->query);
+
+ $filterSet = new FilterSet($filters);
+ $filterSet->setSort($sortColumn, $sortDirection);
+
+
+ $show = Request::get('show', 'stat', 'string');
+ if ($show == 'list') {
+ $this->showFilter('list', $filterSet);
+ $this->showMachineList($filterSet);
return;
}
Render::openTag('div', array('class' => 'row'));
- $this->showSummary();
- $this->showMemory();
- $this->showId44();
- $this->showKvmState();
- $this->showLatestMachines();
- $this->showSystemModels();
+ $this->showFilter('stat', $filterSet);
+ $this->showSummary($filterSet);
+ $this->showMemory($filterSet);
+ $this->showId44($filterSet);
+ $this->showKvmState($filterSet);
+ $this->showLatestMachines($filterSet);
+ $this->showSystemModels($filterSet);
Render::closeTag('div');
}
+ private function showFilter($show, $filterSet)
+ {
+ $data = array(
+ 'show' => $show,
+ 'query' => $this->query,
+ 'delimiter' => Filter::DELIMITER,
+ 'sortDirection' => $filterSet->getSortDirection(),
+ 'sortColumn' => $filterSet->getSortColumn(),
+ 'columns' => json_encode(Page_Statistics::$columns),
+ 'showList' => 1);
+
+ $data['showList'] = ($show == 'list');
+
+
+ $locsFlat = array();
+ if (Module::isAvailable('locations')) {
+ foreach (Location::getLocations() as $loc) {
+ $locsFlat['L' . $loc['locationid']] = array(
+ 'pad' => $loc['locationpad'],
+ 'name' => $loc['locationname']
+ );
+ }
+ }
+
+ $data['locations'] = json_encode($locsFlat);
+ // if($show == 'list') {
+ // $data['showList'] = true;
+ // } else {
+ // $data['showList'] = false;
+ // }
+ Render::addTemplate('filterbox', $data);
+
+
+ }
private function capChart(&$json, $cutoff, $minSlice = 0.015)
{
$total = 0;
@@ -195,14 +237,16 @@ class Page_Statistics extends Page
}
}
- private function showSummary()
+ private function showSummary($filterSet)
{
+ $filterSet->makeFragments($where, $join, $sort, $args);
+
$cutoff = time() - 86400 * 30;
$online = time() - 610;
- $known = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $cutoff");
- $on = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $online");
- $used = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE lastseen > $online AND logintime <> 0");
- $hdd = Database::queryFirst("SELECT Count(*) AS val FROM machine WHERE badsectors > 10 AND lastseen > $cutoff");
+ $known = Database::queryFirst("SELECT Count(*) AS val FROM machine $join WHERE lastseen > $cutoff AND $where", $args);
+ $on = Database::queryFirst("SELECT Count(*) AS val FROM machine $join WHERE lastseen > $online AND $where", $args);
+ $used = Database::queryFirst("SELECT Count(*) AS val FROM machine $join WHERE lastseen > $online AND logintime <> 0 AND $where", $args);
+ $hdd = Database::queryFirst("SELECT Count(*) AS val FROM machine $join WHERE badsectors > 10 AND lastseen > $cutoff AND $where", $args);
if ($on['val'] != 0) {
$usedpercent = round($used['val'] / $on['val'] * 100);
} else {
@@ -238,15 +282,19 @@ class Page_Statistics extends Page
}
}
$data['json'] = json_encode(array('labels' => $labels, 'datasets' => array($points1, $points2)));
+ $data['query'] = $this->query;
// Draw
Render::addTemplate('summary', $data);
}
- private function showSystemModels()
+ private function showSystemModels($filterSet)
{
global $STATS_COLORS;
+
+ $filterSet->makeFragments($where, $join, $sort, $args);
+
$res = Database::simpleQuery('SELECT systemmodel, Round(AVG(realcores)) AS cores, Count(*) AS `count` FROM machine'
- . ' GROUP BY systemmodel ORDER BY `count` DESC, systemmodel ASC');
+ . " $join WHERE $where GROUP BY systemmodel ORDER BY `count` DESC, systemmodel ASC", $args);
$lines = array();
$json = array();
$id = 0;
@@ -266,13 +314,16 @@ class Page_Statistics extends Page
++$id;
}
$this->capChart($json, 0.92);
- Render::addTemplate('cpumodels', array('rows' => $lines, 'json' => json_encode($json)));
+ Render::addTemplate('cpumodels', array('rows' => $lines, 'query' => $this->query, 'json' => json_encode($json)));
}
- private function showMemory()
+ private function showMemory($filterSet)
{
global $STATS_COLORS, $SIZE_RAM;
- $res = Database::simpleQuery('SELECT mbram, Count(*) AS `count` FROM machine GROUP BY mbram');
+
+ $filterSet->makeFragments($where, $join, $sort, $args);
+
+ $res = Database::simpleQuery("SELECT mbram, Count(*) AS `count` FROM machine $join WHERE $where GROUP BY mbram", $args);
$lines = array();
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
$gb = ceil($row['mbram'] / 1024);
@@ -307,13 +358,16 @@ class Page_Statistics extends Page
}
$this->capChart($json, 0.92);
$data['json'] = json_encode($json);
+ $data['query'] = $this->query;
Render::addTemplate('memory', $data);
}
- private function showKvmState()
+ private function showKvmState($filterSet)
{
+ $filterSet->makeFragments($where, $join, $sort, $args);
+
$colors = array('UNKNOWN' => '#666', 'UNSUPPORTED' => '#ea5', 'DISABLED' => '#e55', 'ENABLED' => '#6d6');
- $res = Database::simpleQuery('SELECT kvmstate, Count(*) AS `count` FROM machine GROUP BY kvmstate ORDER BY `count` DESC');
+ $res = Database::simpleQuery("SELECT kvmstate, Count(*) AS `count` FROM machine $join WHERE $where GROUP BY kvmstate ORDER BY `count` DESC", $args);
$lines = array();
$json = array();
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
@@ -324,13 +378,16 @@ class Page_Statistics extends Page
'value' => $row['count'],
);
}
- Render::addTemplate('kvmstate', array('rows' => $lines, 'json' => json_encode($json)));
+ Render::addTemplate('kvmstate', array('rows' => $lines, 'query' => $this->query,'json' => json_encode($json)));
}
- private function showId44()
+ private function showId44($filterSet)
{
global $STATS_COLORS, $SIZE_ID44;
- $res = Database::simpleQuery('SELECT id44mb, Count(*) AS `count` FROM machine GROUP BY id44mb');
+
+ $filterSet->makeFragments($where, $join, $sort, $args);
+
+ $res = Database::simpleQuery("SELECT id44mb, Count(*) AS `count` FROM machine $join WHERE $where GROUP BY id44mb", $args);
$lines = array();
$total = 0;
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
@@ -371,14 +428,18 @@ class Page_Statistics extends Page
}
$this->capChart($json, 0.95);
$data['json'] = json_encode($json);
+ $data['query'] = $this->query;
Render::addTemplate('id44', $data);
}
- private function showLatestMachines()
+ private function showLatestMachines($filterSet)
{
- $data = array('cutoff' => ceil(time() / 3600) * 3600 - 86400 * 7);
- $res = Database::simpleQuery('SELECT machineuuid, clientip, hostname, firstseen, mbram, kvmstate, id44mb FROM machine'
- . ' WHERE firstseen > :cutoff ORDER BY firstseen DESC LIMIT 32', $data);
+ $filterSet->makeFragments($where, $join, $sort, $args);
+
+ $args['cutoff'] = ceil(time() / 3600) * 3600 - 86400 * 7;
+
+ $res = Database::simpleQuery("SELECT machineuuid, clientip, hostname, firstseen, mbram, kvmstate, id44mb FROM machine $join"
+ . " WHERE firstseen > :cutoff AND $where ORDER BY firstseen DESC LIMIT 32", $args);
$rows = array();
$count = 0;
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
@@ -401,119 +462,10 @@ class Page_Statistics extends Page
}
- private function showMachineList($filter, $argument)
+ private function showMachineList($filterSet)
{
- $query = Request::any('filters');
- $sortColumn = Request::any('sortColumn');
- $sortDirection = Request::any('sortDirection');
-
- $filters = Filter::parseQuery($query);
-
- /* generate where clause & arguments */
- $where = '';
- $joins = [];
- $args = [];
- error_log('number of filters: ' . count($filters));
- if (empty($filters)) {
- $where = ' 1 ';
- } else {
- foreach ($filters as $filter) {
- $sep = ($where != '' ? ' AND ' : '');
- $where .= $sep . $filter->whereClause($args, $joins);
- }
- }
- $join = implode('', array_unique($joins));
-
-
- if (!in_array($sortDirection, ['ASC', 'DESC'])) {
- $sortDirection = 'ASC';
- }
- if (array_key_exists($sortColumn, Page_Statistics::$columns)) {
- if (array_key_exists('map_sort', Page_Statistics::$columns[$sortColumn])) {
- /* use the column mapping */
- $actualCol = Page_Statistics::$columns[$sortColumn]['map_sort'];
- $sort = " ORDER BY $actualCol $sortDirection";
- } else {
- $sort = " ORDER BY $sortColumn $sortDirection";
- }
- } else {
- $sort = "";
- }
+ $filterSet->makeFragments($where, $join, $sort, $args);
-
- // /* find the sorting */
- // $sort = explode(' ', Request::any('sort', 'clientip asc'));
- // $sort[1] = strtoupper($sort[1]);
- // if (array_key_exists($sort[0], Page_Statistics::$columns) && ($sort[1] == 'ASC' || $sort[1] == 'DESC')) {
-
- // /* check map_sort mapping */
- // if(array_key_exists('map_sort', Page_Statistics::$columns[$sort[0]])) {
- // $sort[0] = Page_Statistics::$columns[$sort[0]]['map_sort'];
- // }
- // $sort = "ORDER BY " . $sort[0] . ' ' . $sort[1];
- // } else {
- // $sort = "";
- // }
-
- error_log(" JOIN is " . $join);
- error_log($where);
- global $SIZE_RAM, $SIZE_ID44;
- // $filters = array('cpumodel', 'realcores', 'kvmstate', 'clientip', 'macaddr', 'machineuuid', 'systemmodel');
- // if (in_array($filter, $filters)) {
- // // Simple filters mapping into db
- // $where = " $filter = :argument";
- // $args = array('argument' => $argument);
- // } elseif ($filter === 'gbram') {
- // // Memory by rounded GB
- // $lower = floor($this->findBestValue($SIZE_RAM, $argument, false) * 1024 - 100);
- // $upper = ceil($this->findBestValue($SIZE_RAM, $argument, true) * 1024 + 100);
- // $where = " mbram BETWEEN $lower AND $upper";
- // $args = array();
- // } elseif ($filter === 'hddgb') {
- // // HDD by rounded GB
- // $lower = floor($this->findBestValue($SIZE_ID44, $argument, false) * 1024 - 100);
- // $upper = ceil($this->findBestValue($SIZE_ID44, $argument, true) * 1024 + 100);
- // $where = " id44mb BETWEEN $lower AND $upper";
- // $args = array();
- // } elseif ($filter === 'subnet') {
- // $argument = preg_replace('/[^0-9\.:]/', '', $argument);
- // $where = " clientip LIKE '$argument%'";
- // $args = array();
- // } elseif ($filter === 'badsectors') {
- // $where = ' badsectors >= :argument ';
- // $args = array('argument' => $argument);
- // } elseif ($filter === 'state') {
- // if ($argument === 'on') {
- // $where = ' lastseen + 600 > UNIX_TIMESTAMP() ';
- // } elseif ($argument === 'off') {
- // $where = ' lastseen + 600 < UNIX_TIMESTAMP() ';
- // } elseif ($argument === 'idle') {
- // $where = ' lastseen + 600 > UNIX_TIMESTAMP() AND logintime = 0 ';
- // } elseif ($argument === 'occupied') {
- // $where = ' lastseen + 600 > UNIX_TIMESTAMP() AND logintime <> 0 ';
- // } else {
- // Message::addError('invalid-filter');
-
- // return;
- // }
- // } elseif ($filter === 'location') {
- // settype($argument, 'int');
- // if ($argument === 0) {
- // $where = 'machine.locationid IS NULL AND s.locationid IS NULL';
- // $join = 'LEFT JOIN subnet s ON (INET_ATON(machine.clientip) BETWEEN s.startaddr AND s.endaddr)';
- // } else {
- // $where = 'subnet.locationid = :lid OR machine.locationid = :lid';
- // $join = ' INNER JOIN subnet ON (INET_ATON(clientip) BETWEEN startaddr AND endaddr) ';
- // $args = array('lid' => $argument);
- // }
- // } else {
- // //Message::addError('invalid-filter');
- // // return;
- // $where = '1';
- // $args = [];
- // }
-
- // $where and $args
$res = Database::simpleQuery('SELECT machineuuid, macaddr, clientip, firstseen, lastseen,'
. ' logintime, lastboot, realcores, mbram, kvmstate, cpumodel, id44mb, hostname, notes IS NOT NULL AS hasnotes, badsectors FROM machine'
. " $join WHERE $where $sort", $args);
@@ -545,25 +497,16 @@ class Page_Statistics extends Page
}
$rows[] = $row;
}
- $locsFlat = array();
- if (Module::isAvailable('locations')) {
- foreach (Location::getLocations() as $loc) {
- $locsFlat['L' . $loc['locationid']] = array(
- 'pad' => $loc['locationpad'],
- 'name' => $loc['locationname']
- );
- }
- }
Render::addTemplate('clientlist', array(
'rows' => $rows,
- 'filter' => $filter,
- 'query' => $query,
+ 'query' => $this->query,
'delimiter' => Filter::DELIMITER,
- 'sortDirection' => $sortDirection,
- 'sortColumn' => $sortColumn,
- 'argument' => $argument,
+ 'sortDirection' => $filterSet->getSortDirection(),
+ 'sortColumn' => $filterSet->getSortColumn(),
'columns' => json_encode(Page_Statistics::$columns),
'locations' => json_encode($locsFlat),
+ 'showList' => 1,
+ 'show' => 'list'
));
}
@@ -678,21 +621,21 @@ class Page_Statistics extends Page
if (preg_match_all('/##### ([^#]+) #+$(.*?)^#####/ims', $client['data'] . '########', $out, PREG_SET_ORDER)) {
foreach ($out as $section) {
if ($section[1] === 'CPU') {
- $this->parseCpu($client, $section[2]);
+ Parser::parseCpu($client, $section[2]);
}
if ($section[1] === 'dmidecode') {
- $this->parseDmiDecode($client, $section[2]);
+ Parser::parseDmiDecode($client, $section[2]);
}
if ($section[1] === 'Partition tables') {
- $this->parseHdd($hdds, $section[2]);
+ Parser::parseHdd($hdds, $section[2]);
}
if ($section[1] === 'PCI ID') {
$client['lspci1'] = $client['lspci2'] = array();
- $this->parsePci($client['lspci1'], $client['lspci2'], $section[2]);
+ Parser::parsePci($client['lspci1'], $client['lspci2'], $section[2]);
}
if (isset($hdds['hdds']) && $section[1] === 'smartctl') {
// This currently required that the partition table section comes first...
- $this->parseSmartctl($hdds['hdds'], $section[2]);
+ Parser::parseSmartctl($hdds['hdds'], $section[2]);
}
}
}
@@ -840,257 +783,6 @@ class Page_Statistics extends Page
}
}
- private function parseCpu(&$row, $data)
- {
- if (0 >= preg_match_all('/^(.+):\s+(\d+)$/im', $data, $out, PREG_SET_ORDER)) {
- return;
- }
- foreach ($out as $entry) {
- $row[str_replace(' ', '', $entry[1])] = $entry[2];
- }
- }
-
- private function parseDmiDecode(&$row, $data)
- {
- $lines = preg_split("/[\r\n]+/", $data);
- $section = false;
- $ramOk = false;
- $ramForm = $ramType = $ramSpeed = $ramClockSpeed = false;
- foreach ($lines as $line) {
- if (empty($line)) {
- continue;
- }
- if ($line{0} !== "\t" && $line{0} !== ' ') {
- $section = $line;
- $ramOk = false;
- if (($ramForm || $ramType) && ($ramSpeed || $ramClockSpeed)) {
- if (isset($row['ramtype']) && !$ramClockSpeed) {
- continue;
- }
- $row['ramtype'] = $ramType . ' ' . $ramForm;
- if ($ramClockSpeed) {
- $row['ramtype'] .= ', ' . $ramClockSpeed;
- } elseif ($ramSpeed) {
- $row['ramtype'] .= ', ' . $ramSpeed;
- }
- $ramForm = false;
- $ramType = false;
- $ramClockSpeed = false;
- }
- continue;
- }
- if ($section === 'System Information' || $section === 'Base Board Information') {
- if (empty($row['pcmodel']) && preg_match('/^\s*Product Name: +(\S.+?) *$/i', $line, $out)) {
- $row['pcmodel'] = $out[1];
- }
- if (empty($row['manufacturer']) && preg_match('/^\s*Manufacturer: +(\S.+?) *$/i', $line, $out)) {
- $row['manufacturer'] = $out[1];
- }
- } elseif ($section === 'Physical Memory Array') {
- if (!$ramOk && preg_match('/Use: System Memory/i', $line)) {
- $ramOk = true;
- }
- if ($ramOk && preg_match('/^\s*Number Of Devices: +(\S.+?) *$/i', $line, $out)) {
- $row['ramslotcount'] = $out[1];
- }
- if ($ramOk && preg_match('/^\s*Maximum Capacity: +(\S.+?)\s*$/i', $line, $out)) {
- $row['maxram'] = preg_replace('/([MGT])B/', '$1iB', $out[1]);
- }
- } elseif ($section === 'Memory Device') {
- if (preg_match('/^\s*Size:\s*(.*?)\s*$/i', $line, $out)) {
- $row['extram'] = true;
- if (preg_match('/(\d+)\s*(\w)i?B/i', $out[1], $out)) {
- $out[2] = strtoupper($out[2]);
- if ($out[2] === 'K' || ($out[2] === 'M' && $out[1] < 500)) {
- $ramForm = $ramType = $ramSpeed = $ramClockSpeed = false;
- continue;
- }
- if ($out[2] === 'M' && $out[1] >= 1024) {
- $out[2] = 'G';
- $out[1] = floor(($out[1] + 100) / 1024);
- }
- $row['ramslot'][]['size'] = $out[1] . ' ' . strtoupper($out[2]) . 'iB';
- } elseif (!isset($row['ramslot']) || (count($row['ramslot']) < 8 && (!isset($row['ramslotcount']) || $row['ramslotcount'] <= 8))) {
- $row['ramslot'][]['size'] = '_____';
- }
- }
- if (preg_match('/^\s*Form Factor:\s*(.*?)\s*$/i', $line, $out) && $out[1] !== 'Unknown') {
- $ramForm = $out[1];
- }
- if (preg_match('/^\s*Type:\s*(.*?)\s*$/i', $line, $out) && $out[1] !== 'Unknown') {
- $ramType = $out[1];
- }
- if (preg_match('/^\s*Speed:\s*(\d.*?)\s*$/i', $line, $out)) {
- $ramSpeed = $out[1];
- }
- if (preg_match('/^\s*Configured Clock Speed:\s*(\d.*?)\s*$/i', $line, $out)) {
- $ramClockSpeed = $out[1];
- }
- }
- }
- if (empty($row['ramslotcount'])) {
- $row['ramslotcount'] = count($row['ramslot']);
- }
- }
-
- private function parseHdd(&$row, $data)
- {
- $hdds = array();
- // Could have more than one disk - linear scan
- $lines = preg_split("/[\r\n]+/", $data);
- $dev = false;
- $i = 0;
- foreach ($lines as $line) {
- if (preg_match('/^Disk (\S+):.* (\d+) bytes/i', $line, $out)) {
- // disk total size and name
- unset($hdd);
- $unit = 0;
- $hdd = array(
- 'devid' => 'devid-' . ++$i,
- 'dev' => $out[1],
- 'size' => round($out[2] / (1024 * 1024 * 1024)),
- 'used' => 0,
- 'partitions' => array(),
- 'json' => array(),
- );
- $hdds[] = &$hdd;
- } elseif (preg_match('/^Units =.*= (\d+) bytes/i', $line, $out)) {
- // Unit for start and end
- $unit = $out[1] / (1024 * 1024); // Convert so that multiplying by unit yields MiB
- } elseif (isset($hdd) && $unit !== 0 && preg_match(',^/dev/(\S+)\s+.*\s(\d+)[\+\-]?\s+(\d+)[\+\-]?\s+\d+[\+\-]?\s+([0-9a-f]+)\s+(.*)$,i', $line, $out)) {
- // Some partition
- $type = strtolower($out[4]);
- if ($type === '5' || $type === 'f' || $type === '85') {
- continue;
- }
- $partsize = round(($out[3] - $out[2]) * $unit);
- $hdd['partitions'][] = array(
- 'id' => $out[1],
- 'name' => $out[1],
- 'size' => round($partsize / 1024, $partsize < 1024 ? 1 : 0),
- 'type' => ($type === '44' ? 'OpenSLX' : $out[5]),
- );
- $hdd['json'][] = array(
- 'label' => $out[1],
- 'value' => $partsize,
- 'color' => ($type === '44' ? '#4d4' : ($type === '82' ? '#48f' : '#e55')),
- );
- $hdd['used'] += $partsize;
- }
- }
- unset($hdd);
- $i = 0;
- foreach ($hdds as &$hdd) {
- $hdd['used'] = round($hdd['used'] / 1024);
- $free = $hdd['size'] - $hdd['used'];
- if ($free > 5) {
- $hdd['partitions'][] = array(
- 'id' => 'free-id-' . $i,
- 'name' => Dictionary::translate('unused'),
- 'size' => $free,
- 'type' => '-',
- );
- $hdd['json'][] = array(
- 'label' => 'free-id-' . $i,
- 'value' => $free * 1024,
- 'color' => '#aaa',
- );
- ++$i;
- }
- $hdd['json'] = json_encode($hdd['json']);
- }
- unset($hdd);
- $row['hdds'] = &$hdds;
- }
-
- private function parsePci(&$pci1, &$pci2, $data)
- {
- preg_match_all('/[a-f0-9\:\.]{7}\s+"(Class\s*)?(?<class>[a-f0-9]{4})"\s+"(?<ven>[a-f0-9]{4})"\s+"(?<dev>[a-f0-9]{4})"/is', $data, $out, PREG_SET_ORDER);
- $NOW = time();
- $pci = array();
- foreach ($out as $entry) {
- if (!isset($pci[$entry['class']])) {
- $class = 'c.' . $entry['class'];
- $res = $this->getPciId('CLASS', $class);
- if ($res === false || $res['dateline'] < $NOW) {
- $pci[$entry['class']]['lookupClass'] = 'do-lookup';
- $pci[$entry['class']]['class'] = $class;
- } else {
- $pci[$entry['class']]['class'] = $res['value'];
- }
- }
- $new = array(
- 'ven' => $entry['ven'],
- 'dev' => $entry['ven'] . ':' . $entry['dev'],
- );
- $res = $this->getPciId('VENDOR', $new['ven']);
- if ($res === false || $res['dateline'] < $NOW) {
- $new['lookupVen'] = 'do-lookup';
- } else {
- $new['ven'] = $res['value'];
- }
- $res = $this->getPciId('DEVICE', $new['ven'] . ':' . $new['dev']);
- if ($res === false || $res['dateline'] < $NOW) {
- $new['lookupDev'] = 'do-lookup';
- } else {
- $new['dev'] = $res['value'];
- }
- $pci[$entry['class']]['entries'][] = $new;
- }
- ksort($pci);
- foreach ($pci as $class => $entry) {
- if ($class === '0300' || $class === '0200' || $class === '0403') {
- $pci1[] = $entry;
- } else {
- $pci2[] = $entry;
- }
- }
- }
-
- private function parseSmartctl(&$hdds, $data)
- {
- $lines = preg_split("/[\r\n]+/", $data);
- $i = 0;
- foreach ($lines as $line) {
- if (preg_match('/^NEXTHDD=(.+)$/', $line, $out)) {
- unset($dev);
- foreach ($hdds as &$hdd) {
- if ($hdd['dev'] === $out[1]) {
- $dev = &$hdd;
- }
- }
- continue;
- }
- if (!isset($dev)) {
- continue;
- }
- if (preg_match('/^([A-Z][^:]+):\s*(.*)$/', $line, $out)) {
- $dev['s_' . preg_replace('/\s|-|_/', '', $out[1])] = $out[2];
- } elseif (preg_match('/^\s*\d+\s+(\S+)\s+\S+\s+\d+\s+\d+\s+\d+\s+\S+\s+(\d+)(\s|$)/', $line, $out)) {
- $dev['s_' . preg_replace('/\s|-|_/', '', $out[1])] = $out[2];
- }
- }
- // Format strings
- foreach ($hdds as &$hdd) {
- if (isset($hdd['s_PowerOnHours'])) {
- $hdd['PowerOnTime'] = '';
- $val = (int)$hdd['s_PowerOnHours'];
- 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';
- }
- }
- }
protected function doAjax()
{
@@ -1112,7 +804,7 @@ class Page_Statistics extends Page
} else {
die('Invalid format requested');
}
- $cached = $this->getPciId($cat, $param);
+ $cached = Page_Statistics::getPciId($cat, $param);
if ($cached !== false && $cached['dateline'] > time()) {
echo $cached['value'], $add;
exit;
@@ -1122,7 +814,7 @@ class Page_Statistics extends Page
foreach ($res as $entry) {
if (isset($entry['txt']) && substr($entry['txt'], 0, 2) === 'i=') {
$string = substr($entry['txt'], 2);
- $this->setPciId($cat, $param, $string);
+ Page_Statistic::setPciId($cat, $param, $string);
echo $string, $add;
exit;
}
@@ -1135,13 +827,13 @@ class Page_Statistics extends Page
die('Not found');
}
- private function getPciId($cat, $id)
+ public static function getPciId($cat, $id)
{
return Database::queryFirst('SELECT value, dateline FROM pciid WHERE category = :cat AND id = :id LIMIT 1',
array('cat' => $cat, 'id' => $id));
}
- private function setPciId($cat, $id, $value)
+ private static function setPciId($cat, $id, $value)
{
Database::exec('INSERT INTO pciid (category, id, value, dateline) VALUES (:cat, :id, :value, :timeout)'
. ' ON DUPLICATE KEY UPDATE value = VALUES(value), dateline = VALUES(dateline)',
diff --git a/modules-available/statistics/templates/clientlist.html b/modules-available/statistics/templates/clientlist.html
index 664d5413..f2a6ba03 100644
--- a/modules-available/statistics/templates/clientlist.html
+++ b/modules-available/statistics/templates/clientlist.html
@@ -1,53 +1,8 @@
-<div id="modal-add-filter" class="modal modal-sm fade" role="dialog" style="position:absolute; min-width:600px; min-height: 400px;margin:auto">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
- {{lang_add_filter}}
- </div>
- <form class="modal-body form-inline center">
- <div class="form-group">
- <select id="columnSelect" name="column" class="form-control col-4-xs"> </select>
- </div>
- <div class="form-group">
- <select id="operatorSelect" name="operator" class="form-control col-4-xs"> </select>
- </div>
- <div class="form-group">
- <input name="argument" id="argumentInput" class="form-control col-4-xs"> </input>
- <select name="argument" id="argumentSelect" class="form-control col-4-xs"> </select>
- </div>
- <button type="button" class="btn btn-primary" onclick="addFilterFromForm()" >
- <span class="glyphicon glyphicon-plus"></span>
- {{lang_add}}</button>
- </form>
- </div>
-</div>
-
<h1>{{lang_clientList}}</h1>
-<div>
- <!-- use GET here, to avoid the "resend form?" confirmation, and anyway this is stateless, so GET makes more sense -->
-<form id="queryForm" method="GET" action="?do=Statistics&show=list" class="" role="form">
- <input type="hidden" name="do" value="Statistics"/>
- <input type="hidden" name="show" value="list"/>
- <label for="filterInput">{{lang_labelFilter}}</label>
- <input type="text" name="filters" class="" id="filterInput"/>
- <input type="hidden" name="sortColumn" id="sortColumn" value="{{sortColumn}}"/>
- <input type="hidden" name="sortDirection" id="sortDirection" value="{{sortDirection}}"/>
-
- <button type="button" class="btn btn-success" onclick="popupFilter(null)">
- <span class="glyphicon glyphicon-plus"></span>
- {{lang_add_filter}}</button>
- <button class="btn btn-primary pull-right" type="submit">
- <span class="glyphicon glyphicon-refresh"></span>
- {{lang_refresh}}</button>
-</form>
-<br/>
-<br/>
-</div>
-
<table class="table table-condensed table-striped">
<tr>
<th>{{lang_machine}}</th>
@@ -132,195 +87,3 @@
</tr>
{{/rows}}
</table>
-<script type="application/javascript"><!--
-
-var filterSelectize;
-
-var slxFilterNames = {
- machineuuid: '{{lang_uuid}}',
- macaddr: '{{lang_macAddr}}',
- firstseen: '{{lang_firstSeen}}',
- lastseen: '{{lang_lastSeen}}',
- lastboot: '{{lang_lastBoot}}',
- logintime: '{{lang_lastLogin}}',
- realcores: '{{lang_cores}}',
- systemmodel: '{{lang_model}}',
- cpumodel: '{{lang_cpuModel}}',
- hddgb: '{{lang_tmpGb}}',
- gbram: '{{lang_gbRam}}',
- kvmstate: '{{lang_kvmSupport}}',
- badsectors: '{{lang_reallocatedSectors}}',
- clientip: '{{lang_ip}}',
- state: '{{lang_usageState}}',
- location: '{{lang_location}}'
-};
-
-slxLocations = {{{locations}}};
-
-var slxFilterDel = '{{delimiter}}';
-
-
-document.addEventListener("DOMContentLoaded", function () {
-
- /* some objects */
- var $columnSelect = $('#columnSelect');
- $modal = $('#modal-add-filter');
- $queryForm = $('#queryForm');
-
- var columns= {{{columns}}};
-
- /* add options to column select */
- for (var key in columns) {
- $columnSelect.append($('<option>', {
- value: key, text: (slxFilterNames[key] ? slxFilterNames[key] : key) }));
- };
-
-
- /* initialize selectize */
- filterSelectize = $('#filterInput').selectize({
- delimiter: slxFilterDel,
- plugins: ['restore_on_backspace', 'remove_button'],
- create: function(input) {
- return {value: input, text: input}
- },
- onChange: function() {
- // if (initComplete && !$('#filterInput').is(':focus')) {
- // reload();
- // }
- }
- })[0].selectize;
- /* add query */
- var str = "{{{query}}}";
- var eExp = /^(\w+)\s*([=><!~]+)\s*(.*)$/;
- str.split(slxFilterDel).forEach(function(v) {
- var match = eExp.exec(v);
- if (match && match.length === 4) {
- addFilter(match[1], match[2], match[3]);
- } else {
- filterSelectize.addOption({value: v, text: v});
- filterSelectize.addItem(v);
- }
- });
-
- $('#columnSelect').on('change', function() {
- $('#operatorSelect option').remove();
- var col = $('#columnSelect').val();
- var opS = $('#operatorSelect');
- columns[col]['op'].sort(myOpSort);
- columns[col]['op'].forEach(function (v) {
- $(opS).append($('<option>', {
- value: v, text: v
- }));
- });
- /* also set the type of the input */
- if (columns[col]['type'] == 'date') {
- $('#argumentInput').datepicker({format : 'yyyy-mm-dd'});
- $('#argumentSelect').hide();
- } else if(columns[col]['type'] == 'enum') {
- $('#argumentInput').hide();
- $('#argumentSelect').show();
- columns[col]['values'].forEach(function (v) {
- var t = v;
- if (col === 'location' && slxLocations['L' + v]) {
- t = slxLocations['L' + v].pad + ' ' + slxLocations['L' + v].name;
- }
- $('#argumentSelect').append($('<option>', { value: v, text: t }));
- });
- } else {
- $('#argumentInput').datepicker('remove');
- $('#argumentSelect option').remove();
- $('#argumentInput').show();
- $('#argumentSelect').hide();
- }
- });
-
- initButtons();
-
-}, false);
-
-
-function initButtons() {
- ['gbram', 'hddgb', 'realcores', 'kvmstate', 'lastseen', 'clientip'].forEach(function (v) {
- var $sortBtn = $('#sortButton-' + v);
- var order = 'up'; /* default */
- if ($('#sortColumn').val() == v) {
- $sortBtn.addClass('btn-success');
- order = $('#sortDirection').val() == 'ASC' ? 'up' : 'down';
- }
- $sortBtn.html('<span class="glyphicon glyphicon-arrow-' + order + '"></span>');
- $sortBtn.attr('onclick', 'toggleButton(\'' + v + '\');');
- });
-}
-
-function toggleButton(v) {
- var $sortBtn = $('#sortButton-' + v);
- var $col = $('#sortColumn');
- var $dir = $('#sortDirection');
- if ($col.val() == v) {
- /* toggle direction */
- var newDir = $dir.val() == 'ASC' ? 'DESC' : 'ASC';
- $dir.val(newDir);
- /* update button */
- var order = newDir == 'ASC' ? 'up' : 'down';
- $sortBtn.html('<span class="glyphicon glyphicon-arrow-' + order + '"></span>');
- } else {
- /* remove "btn-success" from current sorting */
- $('#sortButton-'+v).removeClass('btn-success');
- $sortBtn.addClass('btn-success');
- $col.val(v);
- $dir = 'ASC';
- }
- refresh();
-}
-
-function popupFilter(field) {
- if (field != null) {
- $('#columnSelect').val(field);
- }
- $('#columnSelect').change();
- $modal.modal('show');
-}
-
-function addFilterFromForm() {
- var argument1 = $('#argumentInput').val();
- var argument2 = $('#argumentSelect').val();
- var argument = argument1 == '' ? argument2 : argument1;
- var col = $('#columnSelect').val();
- var op = $('#operatorSelect').val();
-
- addFilter(col, op, argument);
- refresh(); // TODO: AJAX
-}
-
-function addFilter(col, op, argument) {
- var filterValue = col + ' ' + op + ' ' + argument;
- var filterText = filterValue;
- var displayArgument = argument;
- if (col === 'location' && slxLocations['L' + argument]) {
- displayArgument = slxLocations['L' + argument].name;
- }
- if (slxFilterNames[col]) {
- filterText = slxFilterNames[col] + ' ' + op + ' ' + displayArgument;
- }
- filterSelectize.addOption({value: filterValue, text: filterText});
- filterSelectize.addItem(filterValue);
-}
-
-function toggleSort(field) {
- $('#sort').val(field + ' ' + order);
- refresh();
-}
-
-/* equal sign should always be first, the rest doesn't matter*/
-function myOpSort(a,b) {
- if (a == '=') { return -1; }
- else if (a == b) {return 0}
- else { return 1;}
-
-}
-
-function refresh() {
- console.log('refresh');
- $queryForm.submit(); /* TODO: use AJAX */
-}
-// --></script>
diff --git a/modules-available/statistics/templates/cpumodels.html b/modules-available/statistics/templates/cpumodels.html
index 87f161bb..1bc2e6ec 100644
--- a/modules-available/statistics/templates/cpumodels.html
+++ b/modules-available/statistics/templates/cpumodels.html
@@ -15,9 +15,9 @@
{{#rows}}
<tr id="{{id}}">
<td class="text-left slx-nowrap">
- <a href="?do=Statistics&amp;show=list&amp;filters=systemmodel={{urlsystemmodel}}">{{systemmodel}}</a>
+ <a href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~systemmodel={{urlsystemmodel}}">{{systemmodel}}</a>
</td>
- <td class="text-right"><a href="?do=Statistics&amp;show=list&amp;filters=realcores={{cores}}">{{cores}}</a></td>
+ <td class="text-right"><a href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~realcores={{cores}}">{{cores}}</a></td>
<td class="text-right">{{count}}</td>
</tr>
{{/rows}}
diff --git a/modules-available/statistics/templates/filterbox.html b/modules-available/statistics/templates/filterbox.html
new file mode 100644
index 00000000..2e7928e9
--- /dev/null
+++ b/modules-available/statistics/templates/filterbox.html
@@ -0,0 +1,249 @@
+<div id="modal-add-filter" class="modal modal-sm fade" role="dialog" style="position:absolute; min-width:600px; min-height: 400px;margin:auto">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
+ {{lang_add_filter}}
+ </div>
+ <form class="modal-body form-inline center">
+ <div class="form-group">
+ <select id="columnSelect" name="column" class="form-control col-4-xs"> </select>
+ </div>
+ <div class="form-group">
+ <select id="operatorSelect" name="operator" class="form-control col-4-xs"> </select>
+ </div>
+ <div class="form-group">
+ <input name="argument" id="argumentInput" class="form-control col-4-xs"> </input>
+ <select name="argument" id="argumentSelect" class="form-control col-4-xs"> </select>
+ </div>
+ <button type="button" class="btn btn-primary" onclick="addFilterFromForm()" >
+ <span class="glyphicon glyphicon-plus"></span>
+ {{lang_add}}</button>
+ </form>
+ </div>
+</div>
+
+
+<div style="height:120px">
+ <!-- use GET here, to avoid the "resend form?" confirmation, and anyway this is stateless, so GET makes more sense -->
+ <form id="queryForm" method="GET" action="?do=Statistics" class="" role="form">
+ <input type="hidden" name="do" value="Statistics"/>
+ <label for="filters">{{lang_labelFilter}}</label>
+ <input type="text" name="filters" class="" id="filterInput"/>
+ <input type="hidden" name="sortColumn" id="sortColumn" value="{{sortColumn}}"/>
+ <input type="hidden" name="sortDirection" id="sortDirection" value="{{sortDirection}}"/>
+
+ <button type="button" class="btn btn-success" onclick="popupFilter(null)">
+ <span class="glyphicon glyphicon-plus"></span>
+ {{lang_add_filter}}</button>
+ <button class="btn btn-primary pull-right" type="submit" name="show" value="{{show}}">
+ <span class="glyphicon glyphicon-refresh"></span>
+ {{lang_refresh}}</button>
+ {{#showList}}
+ <button class="btn btn-secondary pull-right" type="submit" name="show" value="stat">
+ <span class="glyphicon glyphicon-stats"></span>
+ {{lang_showVisualization}}</button>
+ {{/showList}}
+ {{^showList}}
+ <button class="btn btn-secondary pull-right" type="submit" name="show" value="list">
+ <span class="glyphicon glyphicon-list"></span>
+ {{lang_showList}}</button>
+ {{/showList}}
+</form>
+<br/>
+<br/>
+</div>
+<script type="application/javascript"><!--
+
+var filterSelectize;
+
+var slxFilterNames = {
+ machineuuid: '{{lang_uuid}}',
+ macaddr: '{{lang_macAddr}}',
+ firstseen: '{{lang_firstSeen}}',
+ lastseen: '{{lang_lastSeen}}',
+ lastboot: '{{lang_lastBoot}}',
+ logintime: '{{lang_lastLogin}}',
+ realcores: '{{lang_cores}}',
+ systemmodel: '{{lang_model}}',
+ cpumodel: '{{lang_cpuModel}}',
+ hddgb: '{{lang_tmpGb}}',
+ gbram: '{{lang_gbRam}}',
+ kvmstate: '{{lang_kvmSupport}}',
+ badsectors: '{{lang_reallocatedSectors}}',
+ clientip: '{{lang_ip}}',
+ state: '{{lang_usageState}}',
+ location: '{{lang_location}}'
+};
+
+slxLocations = {{{locations}}};
+
+var slxFilterDel = '{{delimiter}}';
+
+
+document.addEventListener("DOMContentLoaded", function () {
+
+ /* some objects */
+ var $columnSelect = $('#columnSelect');
+ $modal = $('#modal-add-filter');
+ $queryForm = $('#queryForm');
+
+ var columns= {{{columns}}};
+
+ /* add options to column select */
+ for (var key in columns) {
+ $columnSelect.append($('<option>', {
+ value: key, text: (slxFilterNames[key] ? slxFilterNames[key] : key) }));
+ };
+
+
+ /* initialize selectize */
+ filterSelectize = $('#filterInput').selectize({
+ delimiter: slxFilterDel,
+ plugins: ['restore_on_backspace', 'remove_button'],
+ create: function(input) {
+ return {value: input, text: input}
+ },
+ onChange: function() {
+ // if (initComplete && !$('#filterInput').is(':focus')) {
+ // reload();
+ // }
+ }
+ })[0].selectize;
+ /* add query */
+ var str = "{{{query}}}";
+ var eExp = /^(\w+)\s*([=><!~]+)\s*(.*)$/;
+ str.split(slxFilterDel).forEach(function(v) {
+ var match = eExp.exec(v);
+ if (match && match.length === 4) {
+ addFilter(match[1], match[2], match[3]);
+ } else {
+ filterSelectize.addOption({value: v, text: v});
+ filterSelectize.addItem(v);
+ }
+ });
+
+ $('#columnSelect').on('change', function() {
+ $('#operatorSelect option').remove();
+ var col = $('#columnSelect').val();
+ var opS = $('#operatorSelect');
+ columns[col]['op'].sort(myOpSort);
+ columns[col]['op'].forEach(function (v) {
+ $(opS).append($('<option>', {
+ value: v, text: v
+ }));
+ });
+ /* also set the type of the input */
+ if (columns[col]['type'] == 'date') {
+ $('#argumentInput').datepicker({format : 'yyyy-mm-dd'});
+ $('#argumentSelect').hide();
+ } else if(columns[col]['type'] == 'enum') {
+ $('#argumentInput').hide();
+ $('#argumentSelect').show();
+ columns[col]['values'].forEach(function (v) {
+ var t = v;
+ if (col === 'location' && slxLocations['L' + v]) {
+ t = slxLocations['L' + v].pad + ' ' + slxLocations['L' + v].name;
+ }
+ $('#argumentSelect').append($('<option>', { value: v, text: t }));
+ });
+ } else {
+ $('#argumentInput').datepicker('remove');
+ $('#argumentSelect option').remove();
+ $('#argumentInput').show();
+ $('#argumentSelect').hide();
+ }
+ });
+
+ initButtons();
+
+}, false);
+
+
+function initButtons() {
+ ['gbram', 'hddgb', 'realcores', 'kvmstate', 'lastseen', 'clientip'].forEach(function (v) {
+ var $sortBtn = $('#sortButton-' + v);
+ var order = 'up'; /* default */
+ if ($('#sortColumn').val() == v) {
+ $sortBtn.addClass('btn-success');
+ order = $('#sortDirection').val() == 'ASC' ? 'up' : 'down';
+ }
+ $sortBtn.html('<span class="glyphicon glyphicon-arrow-' + order + '"></span>');
+ $sortBtn.attr('onclick', 'toggleButton(\'' + v + '\');');
+ });
+}
+
+function toggleButton(v) {
+ var $sortBtn = $('#sortButton-' + v);
+ var $col = $('#sortColumn');
+ var $dir = $('#sortDirection');
+ if ($col.val() == v) {
+ /* toggle direction */
+ var newDir = $dir.val() == 'ASC' ? 'DESC' : 'ASC';
+ $dir.val(newDir);
+ /* update button */
+ var order = newDir == 'ASC' ? 'up' : 'down';
+ $sortBtn.html('<span class="glyphicon glyphicon-arrow-' + order + '"></span>');
+ } else {
+ /* remove "btn-success" from current sorting */
+ $('#sortButton-'+v).removeClass('btn-success');
+ $sortBtn.addClass('btn-success');
+ $col.val(v);
+ $dir = 'ASC';
+ }
+ refresh();
+}
+
+function popupFilter(field) {
+ if (field != null) {
+ $('#columnSelect').val(field);
+ }
+ $('#columnSelect').change();
+ $modal.modal('show');
+}
+
+function addFilterFromForm() {
+ var argument1 = $('#argumentInput').val();
+ var argument2 = $('#argumentSelect').val();
+ var argument = argument1 == '' ? argument2 : argument1;
+ var col = $('#columnSelect').val();
+ var op = $('#operatorSelect').val();
+
+ addFilter(col, op, argument);
+ refresh(); // TODO: AJAX
+}
+
+function addFilter(col, op, argument) {
+ var filterValue = col + ' ' + op + ' ' + argument;
+ var filterText = filterValue;
+ var displayArgument = argument;
+ if (col === 'location' && slxLocations['L' + argument]) {
+ displayArgument = slxLocations['L' + argument].name;
+ }
+ if (slxFilterNames[col]) {
+ filterText = slxFilterNames[col] + ' ' + op + ' ' + displayArgument;
+ }
+ filterSelectize.addOption({value: filterValue, text: filterText});
+ filterSelectize.addItem(filterValue);
+}
+
+function toggleSort(field) {
+ $('#sort').val(field + ' ' + order);
+ refresh();
+}
+
+/* equal sign should always be first, the rest doesn't matter*/
+function myOpSort(a,b) {
+ if (a == '=') { return -1; }
+ else if (a == b) {return 0}
+ else { return 1;}
+
+}
+
+function refresh() {
+ console.log('refresh');
+ $queryForm.submit(); /* TODO: use AJAX */
+}
+// --></script>
+
+
+
diff --git a/modules-available/statistics/templates/id44.html b/modules-available/statistics/templates/id44.html
index 1a00fc8c..f0b5b9f7 100644
--- a/modules-available/statistics/templates/id44.html
+++ b/modules-available/statistics/templates/id44.html
@@ -13,7 +13,7 @@
</tr>
{{#rows}}
<tr id="tmpid{{gb}}" class="{{class}}">
- <td class="text-left slx-nowrap"><a href="?do=Statistics&amp;show=list&amp;filters=hddgb={{gb}}">{{gb}}&thinsp;GiB</td>
+ <td class="text-left slx-nowrap"><a href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~hddgb={{gb}}">{{gb}}&thinsp;GiB</td>
<td class="text-right">{{count}}</td>
</tr>
{{/rows}}
diff --git a/modules-available/statistics/templates/kvmstate.html b/modules-available/statistics/templates/kvmstate.html
index af163893..4c286d36 100644
--- a/modules-available/statistics/templates/kvmstate.html
+++ b/modules-available/statistics/templates/kvmstate.html
@@ -13,7 +13,7 @@
</tr>
{{#rows}}
<tr id="kvm{{kvmstate}}">
- <td class="text-left slx-nowrap"><a href="?do=Statistics&amp;show=list&amp;filters=kvmstate={{kvmstate}}">{{kvmstate}}</a></td>
+ <td class="text-left slx-nowrap"><a href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~kvmstate={{kvmstate}}">{{kvmstate}}</a></td>
<td class="text-right">{{count}}</td>
</tr>
{{/rows}}
diff --git a/modules-available/statistics/templates/memory.html b/modules-available/statistics/templates/memory.html
index efa7c44b..8a882fa6 100644
--- a/modules-available/statistics/templates/memory.html
+++ b/modules-available/statistics/templates/memory.html
@@ -13,7 +13,7 @@
</tr>
{{#rows}}
<tr id="ramid{{gb}}" class="{{class}}">
- <td class="text-left slx-nowrap"><a href="?do=Statistics&amp;show=list&amp;filters=gbram={{gb}}">{{gb}}&thinsp;GiB</a></td>
+ <td class="text-left slx-nowrap"><a href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~gbram={{gb}}">{{gb}}&thinsp;GiB</a></td>
<td class="text-right">{{count}}</td>
</tr>
{{/rows}}
diff --git a/modules-available/statistics/templates/summary.html b/modules-available/statistics/templates/summary.html
index f71620f9..642c48fc 100644
--- a/modules-available/statistics/templates/summary.html
+++ b/modules-available/statistics/templates/summary.html
@@ -3,13 +3,13 @@
<div class="panel-body">
<div>
{{lang_knownMachines}}: <b>{{known}}</b>&emsp;
- <a href="?do=Statistics&amp;show=list&amp;filters=state=on">{{lang_onlineMachines}}</a>: <b>{{online}}</b>&emsp;
- <a href="?do=Statistics&amp;show=list&amp;filters=state=occupied">{{lang_inUseMachines}}</a>: <b>{{used}}</b> (<b>{{usedpercent}}%</b>)
+ <a href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~state=on">{{lang_onlineMachines}}</a>: <b>{{online}}</b>&emsp;
+ <a href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~state=occupied">{{lang_inUseMachines}}</a>: <b>{{used}}</b> (<b>{{usedpercent}}%</b>)
</div>
{{#badhdd}}
<div>
<span class="glyphicon glyphicon-exclamation-sign red"></span>
- <a href="?do=Statistics&amp;show=list&amp;filters=badsectors>=10">
+ <a href="?do=Statistics&amp;show=list&amp;filters={{query}}~,~badsectors>=10">
{{lang_withBadSectors}}: <b>{{badhdd}}</b>
</a>
</div>