diff options
Diffstat (limited to 'modules-available/statistics_reporting')
5 files changed, 152 insertions, 102 deletions
diff --git a/modules-available/statistics_reporting/hooks/cron.inc.php b/modules-available/statistics_reporting/hooks/cron.inc.php index 14597f7d..7336b7d1 100644 --- a/modules-available/statistics_reporting/hooks/cron.inc.php +++ b/modules-available/statistics_reporting/hooks/cron.inc.php @@ -1,25 +1,23 @@ <?php -if (RemoteReport::isReportingEnabled()) { - $nextReporting = RemoteReport::getReportingTimestamp(); +$nextReporting = RemoteReport::getReportingTimestamp(); - // It's time to generate a new report - while ($nextReporting <= time()) { - RemoteReport::writeNextReportingTimestamp(); +// It's time to generate a new report +while ($nextReporting <= time()) { + RemoteReport::writeNextReportingTimestamp(); - $to = $nextReporting; + $to = $nextReporting; - $statisticsReport = json_encode(RemoteReport::generateReport($to)); + $statisticsReport = json_encode(RemoteReport::generateReport($to)); - $params = array("action" => "statistics", "data" => $statisticsReport); + $params = array("action" => "statistics", "data" => $statisticsReport); - $result = Download::asStringPost(CONFIG_REPORTING_URL, $params, 30, $code); + $result = Download::asStringPost(CONFIG_REPORTING_URL, $params, 30, $code); - if ($code != 200) { - EventLog::warning("Statistics Reporting failed: " . $code, $result); - } else { - EventLog::info('Statistics report sent to ' . CONFIG_REPORTING_URL); - } - $nextReporting = strtotime("+7 days", $nextReporting); + if ($code != 200) { + EventLog::warning("Statistics Reporting failed: " . $code, $result); + } else { + EventLog::info('Statistics report sent to ' . CONFIG_REPORTING_URL); } -}
\ No newline at end of file + $nextReporting = strtotime("+7 days", $nextReporting); +} diff --git a/modules-available/statistics_reporting/inc/getdata.inc.php b/modules-available/statistics_reporting/inc/getdata.inc.php index 13d39502..75db119d 100644 --- a/modules-available/statistics_reporting/inc/getdata.inc.php +++ b/modules-available/statistics_reporting/inc/getdata.inc.php @@ -24,7 +24,7 @@ class GetData return $carry . sprintf("%04d", $item); }) . sprintf("%04d", $entry['locationid']); } else { - $entry['locationname'] = Dictionary::translate('notAssigned', true); + $entry['locationname'] = Dictionary::translate('notAssigned'); } if ($anonymize) { unset($entry['locationid']); @@ -56,7 +56,8 @@ class GetData } // total - public static function total($flags = 0) { + public static function total(int $flags = 0): array + { $printable = 0 !== ($flags & GETDATA_PRINTABLE); // total time online, average time online, total number of logins $data = Queries::getOverallStatistics(self::$from, self::$to, self::$lowerTimeBound, self::$upperTimeBound); @@ -70,7 +71,8 @@ class GetData } // per location - public static function perLocation($flags = 0) { + public static function perLocation(int $flags = 0): array + { $anonymize = 0 !== ($flags & GETDATA_ANONYMOUS); $printable = 0 !== ($flags & GETDATA_PRINTABLE); $data = Queries::getLocationStatistics(self::$from, self::$to, self::$lowerTimeBound, self::$upperTimeBound); @@ -85,7 +87,8 @@ class GetData } // per client - public static function perClient($flags = 0, $new = false) { + public static function perClient(int $flags = 0): array + { $anonymize = 0 !== ($flags & GETDATA_ANONYMOUS); $printable = 0 !== ($flags & GETDATA_PRINTABLE); $data = Queries::getClientStatistics(self::$from, self::$to, self::$lowerTimeBound, self::$upperTimeBound); @@ -101,11 +104,12 @@ class GetData } // per user - public static function perUser($flags = 0) { + public static function perUser(int $flags = 0): array + { $anonymize = 0 !== ($flags & GETDATA_ANONYMOUS); $res = Queries::getUserStatistics(self::$from, self::$to, self::$lowerTimeBound, self::$upperTimeBound); $data = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + foreach ($res as $row) { if ($anonymize && $row['name'] !== 'anonymous') { $row['name'] = md5($row['name'] . self::$salt); } @@ -116,11 +120,12 @@ class GetData // per vm - public static function perVM($flags = 0) { + public static function perVM(int $flags = 0): array + { $anonymize = 0 !== ($flags & GETDATA_ANONYMOUS); $res = Queries::getVMStatistics(self::$from, self::$to, self::$lowerTimeBound, self::$upperTimeBound); $data = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + foreach ($res as $row) { self::nullToZero($row); if ($anonymize) { $row['name'] = md5($row['name'] . self::$salt); @@ -130,7 +135,7 @@ class GetData return $data; } - private static function nullToZero(&$row) + private static function nullToZero(array &$row): void { foreach ($row as &$field) { if (is_null($field)) { @@ -140,7 +145,7 @@ class GetData } // Format $seconds into ".d .h .m .s" format (day, hour, minute, second) - private static function formatSeconds($seconds) + private static function formatSeconds(int $seconds): string { return sprintf('%dd, %02d:%02d:%02d', $seconds / (3600*24), ($seconds % (3600*24)) / 3600, ($seconds%3600) / 60, $seconds%60); } diff --git a/modules-available/statistics_reporting/inc/queries.inc.php b/modules-available/statistics_reporting/inc/queries.inc.php index fc61c68a..bafe80bc 100644 --- a/modules-available/statistics_reporting/inc/queries.inc.php +++ b/modules-available/statistics_reporting/inc/queries.inc.php @@ -13,7 +13,7 @@ class Queries } } - public static function getClientStatistics(int $from, int $to, $lowerTimeBound = 0, $upperTimeBound = 24) + public static function getClientStatistics(int $from, int $to, int $lowerTimeBound = 0, int $upperTimeBound = 24): array { $fromCutoff = $from - 86400 * 30; $res = Database::simpleQuery("SELECT m.machineuuid, m.hostname, m.clientip, @@ -23,12 +23,12 @@ class Queries foreach ($machines as &$machine) { $machine['medianSessionLength'] = self::calcMedian($machine['sessions']); unset($machine['sessions']); - $machine['clientName'] = $machine['hostname'] ? $machine['hostname'] : $machine['clientip']; + $machine['clientName'] = $machine['hostname'] ?: $machine['clientip']; } return $machines; } - public static function getLocationStatistics(int $from, int $to, $lowerTimeBound = 0, $upperTimeBound = 24) + public static function getLocationStatistics(int $from, int $to, int $lowerTimeBound = 0, int $upperTimeBound = 24): array { $fromCutoff = $from - 86400 * 30; $res = Database::simpleQuery("SELECT m.machineuuid, m.hostname, m.clientip, @@ -37,7 +37,7 @@ class Queries $machines = self::getStats3($res, $from, $to, $lowerTimeBound, $upperTimeBound); $locations = []; $keys = ['locationid', 'totalTime', 'totalOffTime', 'totalSessionTime', 'totalStandbyTime', 'totalIdleTime', 'totalIdleTime', 'longSessions', 'shortSessions', 'sessions']; - while ($machine = array_pop($machines)) { + while (($machine = array_pop($machines)) !== null) { if (!isset($locations[$machine['locationid']])) { self::keepKeys($machine, $keys); $locations[$machine['locationid']] = $machine; @@ -69,7 +69,7 @@ class Queries $machines = self::getStats3($res, $from, $to, $lowerTimeBound, $upperTimeBound); $total = false; $keys = ['totalTime', 'totalOffTime', 'totalSessionTime', 'totalStandbyTime', 'totalIdleTime', 'totalIdleTime', 'longSessions', 'shortSessions', 'sessions']; - while ($machine = array_pop($machines)) { + while (($machine = array_pop($machines)) !== null) { if ($total === false) { self::keepKeys($machine, $keys); $total = $machine; @@ -84,20 +84,12 @@ class Queries $total['sessions'] = array_merge($total['sessions'], $machine['sessions']); } } - $total['medianSessionLength'] = self::calcMedian($total['sessions']); + $total['medianSessionLength'] = $total ? self::calcMedian($total['sessions']) : 0; unset($total['sessions']); return $total; } - /** - * @param \PDOStatement $res - * @param int $from - * @param int $to - * @param int $lowerTimeBound - * @param int $upperTimeBound - * @return array - */ - private static function getStats3($res, $from, $to, $lowerTimeBound, $upperTimeBound) + private static function getStats3(PDOStatement $res, int $from, int $to, int $lowerTimeBound, int $upperTimeBound): array { //$debug = false; if ($lowerTimeBound === 0 && $upperTimeBound === 24 || $upperTimeBound <= $lowerTimeBound) { @@ -106,7 +98,7 @@ class Queries $bounds = [$lowerTimeBound, $upperTimeBound]; } $machines = array(); - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + foreach ($res as $row) { $row['firstseen'] = max($row['firstseen'], $from); $row += array( 'totalTime' => self::timeDiff($row['firstseen'], $to, $bounds), @@ -132,7 +124,7 @@ class Queries ORDER BY dateline ASC LIMIT 1000", compact('last', 'to')); $last = false; $count = 0; - while ($row = $res->fetch(PDO::FETCH_ASSOC)) { + foreach ($res as $row) { $count += 1; // Update count first, as we use it as a condition in outer loop. No continue before this settype($row['logid'], 'int'); // Update for next query @@ -228,8 +220,8 @@ class Queries } */ $machine[$row['typeid']] += self::timeDiff($start, $end, $bounds); - $sh = date('G', $start); } + $sh = date('G', $start); if ($row['typeid'] === 'totalSessionTime' && ($bounds === false || ($sh >= $bounds[0] && $sh < $bounds[1]))) { if ($row['data'] >= 60) { $machine['longSessions'] += 1; @@ -307,17 +299,18 @@ class Queries /** * Get median of array. - * @param int[] list of values + * @param int[] $array list of values * @return int The median */ - private static function calcMedian($array) { + private static function calcMedian(array $array): int + { if (empty($array)) return 0; sort($array, SORT_NUMERIC); $count = count($array); //total numbers in array $middleval = (int)floor(($count-1) / 2); // find the middle value, or the lowest middle value if($count % 2 === 1) { // odd number, middle is the median - return (int)$array[$middleval]; + return $array[$middleval]; } // even number, calculate avg of 2 medians $low = $array[$middleval]; @@ -327,25 +320,23 @@ class Queries // User Data: Name, Name(anonymized), Number of Logins public static function getUserStatistics($from, $to, $lowerTimeBound = 0, $upperTimeBound = 24) { - $res = Database::simpleQuery("SELECT username AS name, COUNT(*) AS 'count' + return Database::simpleQuery("SELECT username AS name, COUNT(*) AS 'count' FROM statistic WHERE typeid='.vmchooser-session-name' AND dateline >= $from and dateline <= $to AND FROM_UNIXTIME(dateline, '%H') >= $lowerTimeBound AND FROM_UNIXTIME(dateline, '%H') < $upperTimeBound GROUP BY username"); - return $res; } // Virtual Machine Data: Name, Number of Usages public static function getVMStatistics($from, $to, $lowerTimeBound = 0, $upperTimeBound = 24) { - $res = Database::simpleQuery("SELECT data AS name, COUNT(*) AS 'count' + return Database::simpleQuery("SELECT data AS name, COUNT(*) AS 'count' FROM statistic WHERE typeid='.vmchooser-session-name' AND dateline >= $from and dateline <= $to AND FROM_UNIXTIME(dateline, '%H') >= $lowerTimeBound AND FROM_UNIXTIME(dateline, '%H') < $upperTimeBound GROUP BY data"); - return $res; } - public static function getDozmodStats(int $from, int $to) + public static function getDozmodStats(int $from, int $to): array { if (Module::get('dozmod') === false) return ['disabled' => true]; @@ -366,7 +357,7 @@ class Queries return $return; } - public static function getExamStats(int $from, int $to) + public static function getExamStats(int $from, int $to): array { if (Module::get('exams') === false) return ['disabled' => true]; @@ -375,7 +366,7 @@ class Queries LEFT JOIN exams_x_location exl USING (examid) WHERE starttime < $to AND endtime > $from GROUP BY examid"); - while ($row = $eres->fetch(PDO::FETCH_ASSOC)) { + foreach ($eres as $row) { // Get all boot events $data = ['from' => $row['starttime'], 'to' => $row['endtime']]; if (empty($row['locs'])) { @@ -397,7 +388,7 @@ class Queries return $return; } - public static function getAggregatedMachineStats($from) + public static function getAggregatedMachineStats(int $from): array { $return = array(); $return['location'] = Database::queryAll("SELECT MD5(CONCAT(locationid, :salt)) AS `location`, Count(*) AS `count` @@ -407,7 +398,7 @@ class Queries array('salt' => GetData::$salt)); $prev = 0; $str = ' '; - foreach (array(0.5, 1, 1.5, 2, 3, 4, 6, 8, 10, 12, 16, 20, 24, 28, 32, 40, 48, 64, 72, 80, 88, 96, 128, 192, 256) as $val) { + foreach ([0.5, 1, 1.5, 2, 3, 4, 6, 8, 10, 12, 16, 20, 24, 28, 32, 40, 48, 64, 72, 80, 88, 96, 128, 192, 256, 512, 768, 1024, 1536, 2048, 3072, 4096] as $val) { $str .= 'WHEN mbram < ' . round(($val + $prev) * 512) . " THEN '" . $prev . "' "; $prev = $val; } @@ -421,6 +412,15 @@ class Queries WHERE lastseen >= $from GROUP BY $key"); } + // Legacy CPU + $f = Database::queryFirst("SELECT Count(*) AS `total` + FROM machine m + INNER JOIN machine_x_hw mxh USING (machineuuid) + INNER JOIN statistic_hw_prop hwp ON (hwp.hwid = mxh.hwid AND hwp.prop = 'vmx-legacy' AND hwp.value <> 0) + WHERE m.lastseen >= $from"); + if (is_array($f)) { + $return['vmx-legacy'] = $f['total']; + } return $return; } @@ -429,7 +429,7 @@ class Queries * @param int $to end timestamp * @return int count of user active in timespan */ - public static function getUniqueUserCount($from, $to) + public static function getUniqueUserCount(int $from, int $to): int { $res = Database::queryFirst("SELECT Count(DISTINCT username) as `total` FROM statistic @@ -437,5 +437,21 @@ class Queries return (int)$res['total']; } + public static function getBaseSystemStats(int $from, int $to) + { + return Database::queryAll("SELECT `data` AS `system`, Count(*) AS `count` + FROM statistic + WHERE (dateline BETWEEN $from AND $to) AND typeid = 'boot-system' + GROUP BY `system`"); + } + + public static function getRunmodeStats(int $from, int $to) + { + return Database::queryAll("SELECT `data` AS `mode`, Count(*) AS `count` + FROM statistic + WHERE (dateline BETWEEN $from AND $to) AND typeid = 'boot-runmode' + GROUP BY `mode`"); + } + } diff --git a/modules-available/statistics_reporting/inc/remotereport.inc.php b/modules-available/statistics_reporting/inc/remotereport.inc.php index 2de49d04..ebd31046 100644 --- a/modules-available/statistics_reporting/inc/remotereport.inc.php +++ b/modules-available/statistics_reporting/inc/remotereport.inc.php @@ -23,19 +23,19 @@ class RemoteReport * * @return bool true if reporting is on, false if off */ - public static function isReportingEnabled() + public static function isReportingEnabled(): bool { return Property::get(self::ENABLED_ID, 'on') === 'on'; } /** - * Get the timestamp of the end of the next 7 day interval to + * Get the timestamp of the end of the next 7-day interval to * report statistics for. Usually if this is < time() you want * to generate the report. * * @return int timestamp of the end of the reporting time frame */ - public static function getReportingTimestamp() + public static function getReportingTimestamp(): int { $ts = Property::get(self::NEXT_SUBMIT_ID, 0); if ($ts === 0) { @@ -52,7 +52,7 @@ class RemoteReport /** * Update the timestamp of the next scheduled statistics report. - * This sets the end of the next 7 day interval to the start of + * This sets the end of the next 7-day interval to the start of * next monday (00:00). */ public static function writeNextReportingTimestamp() @@ -68,41 +68,48 @@ class RemoteReport * @param int[] $days list of days to generate aggregated stats for * @return array wrapped up statistics, ready for reporting */ - public static function generateReport(int $to, $days = false) { - if ($days === false) { - $days = [7, 30, 90]; - } - GetData::$salt = bin2hex(Util::randomBytes(20, false)); - GetData::$lowerTimeBound = 7; - GetData::$upperTimeBound = 20; + public static function generateReport(int $to, $days = false): array + { $result = array(); - foreach ($days as $day) { - if (isset($result['days' . $day])) - continue; - $from = strtotime("-{$day} days", $to); - GetData::$from = $from; - GetData::$to = $to; - $data = array('total' => GetData::total(GETDATA_ANONYMOUS)); - $data['perLocation'] = array_values(GetData::perLocation(GETDATA_ANONYMOUS)); - $data['perVM'] = GetData::perVM(GETDATA_ANONYMOUS); - $data['tsFrom'] = $from; - $data['tsTo'] = $to; - $data['dozmod'] = Queries::getDozmodStats($from, $to); - $data['machines'] = Queries::getAggregatedMachineStats($from); - $data['exams'] = Queries::getExamStats($from, $to); - $result['days' . $day] = $data; + if (RemoteReport::isReportingEnabled()) { + if ($days === false) { + $days = [7, 30, 90]; + } + GetData::$salt = bin2hex(Util::randomBytes(20, false)); + GetData::$lowerTimeBound = 7; + GetData::$upperTimeBound = 20; + foreach ($days as $day) { + if (isset($result['days' . $day])) + continue; + $from = (int)strtotime("-{$day} days", $to); + GetData::$from = $from; + GetData::$to = $to; + $data = array('total' => GetData::total(GETDATA_ANONYMOUS)); + $data['perLocation'] = array_values(GetData::perLocation(GETDATA_ANONYMOUS)); + $data['perVM'] = GetData::perVM(GETDATA_ANONYMOUS); + $data['tsFrom'] = $from; + $data['tsTo'] = $to; + $data['dozmod'] = Queries::getDozmodStats($from, $to); + $data['machines'] = Queries::getAggregatedMachineStats($from); + $data['exams'] = Queries::getExamStats($from, $to); + $data['baseSystem'] = Queries::getBaseSystemStats($from, $to); + $data['runmode'] = Queries::getRunmodeStats($from, $to); + $data['gpus'] = self::getGpus($from, $to); + $result['days' . $day] = $data; + } + $result['server'] = self::getLocalHardware(); } - $result['server'] = self::getLocalHardware(); $result['version'] = CONFIG_FOOTER; return $result; } - private static function getLocalHardware() + private static function getLocalHardware(): array { $cpuInfo = file_get_contents('/proc/cpuinfo'); $uptime = file_get_contents('/proc/uptime'); $memInfo = file_get_contents('/proc/meminfo'); - preg_match_all('/\b(\w+):\s+(\d+)\s/s', $memInfo, $out, PREG_SET_ORDER); + $osInfo = parse_ini_file('/etc/os-release'); + preg_match_all('/\b(\w+):\s+(\d+)\s/', $memInfo, $out, PREG_SET_ORDER); $mem = array(); foreach ($out as $e) { $mem[$e[1]] = $e[2]; @@ -122,7 +129,36 @@ class RemoteReport $data['swapTotal'] = $mem['SwapTotal']; $data['swapUsed'] = ($mem['SwapTotal'] - $mem['SwapFree']); } + + $data['ip'] = $_SERVER['SERVER_ADDR'] ?? Property::getServerIp(); + $data['hostname'] = strtolower(gethostbyaddr($_SERVER['SERVER_ADDR'] ?? Property::getServerIp())); + + $data['osID'] = $osInfo['ID']; + $data['osVersionID'] = $osInfo['VERSION_ID']; + $data['osVersionCodename'] = $osInfo['VERSION_CODENAME']; + return $data; } -}
\ No newline at end of file + private static function getGpus(int $from, int $to): array + { + + $q = new HardwareQuery(HardwareInfo::PCI_DEVICE, null, true); + $q->addGlobalColumn('vendor'); + $q->addGlobalColumn('device'); + $q->addGlobalColumn('class')->addCondition('=', '0300'); // VGA adapter + $q->addMachineWhere('lastseen', '>=', $from); + $q->addMachineWhere('lastseen', '<=', $to); + $res = $q->query(['vendor', 'device']); + $return = []; + foreach ($res as $row) { + $return[] = [ + 'group_count' => $row['group_count'], + 'device' => $row['device'], + 'vendor' => $row['vendor'], + ]; + } + return $return; + } + +} diff --git a/modules-available/statistics_reporting/page.inc.php b/modules-available/statistics_reporting/page.inc.php index c3e469fe..ec4bbf1a 100644 --- a/modules-available/statistics_reporting/page.inc.php +++ b/modules-available/statistics_reporting/page.inc.php @@ -125,14 +125,14 @@ class Page_Statistics_Reporting extends Page foreach ($this->COLUMNS as $column) { $data['columns'][] = array( 'id' => 'col_' . $column, - 'name' => Dictionary::translateFile('template-tags', 'lang_' . $column, true), + 'name' => Dictionary::translateFile('template-tags', 'lang_' . $column), 'checked' => ($forceOn || Request::get('col_' . $column, 'off', 'string') !== 'off') ? 'checked' : '', ); } foreach ($this->TABLES as $table) { $data['tables'][] = array( - 'name' => Dictionary::translate('table_' . $table, true), + 'name' => Dictionary::translate('table_' . $table), 'value' => $table, 'allowed' => User::hasPermission("table.view.$table"), 'selected' => ($this->type === $table) ? 'selected' : '', @@ -185,12 +185,10 @@ class Page_Statistics_Reporting extends Page } Header('Content-Type: application/json; charset=utf-8'); die(json_encode($data)); - } else { - die('No permission.'); } - } else { - echo 'Invalid action.'; + die('No permission.'); } + echo 'Invalid action.'; } private function doExport() @@ -257,7 +255,6 @@ class Page_Statistics_Reporting extends Page } fclose($fh); exit(); - break; case 'xml': $xml_data = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8" ?><data></data>'); $this->array_to_xml($res, $xml_data, 'row'); @@ -273,9 +270,9 @@ class Page_Statistics_Reporting extends Page /** * @param $data array Data to encode - * @param $xml_data \SimpleXMLElement XML Object to append to + * @param $xml_data SimpleXMLElement XML Object to append to */ - private function array_to_xml($data, $xml_data, $parentName = 'row') + private function array_to_xml(array $data, SimpleXMLElement $xml_data, string $parentName = 'row'): void { foreach ($data as $key => $value) { if (is_numeric($key)) { @@ -290,7 +287,7 @@ class Page_Statistics_Reporting extends Page } } - private function fetchData($flags) + private function fetchData(int $flags) { // TODO: Make all modes location-aware, filter while querying, not after switch ($this->type) { @@ -314,10 +311,9 @@ class Page_Statistics_Reporting extends Page } } // correct indexing of array after deletions - $data = array_values($data); - return $data; + return array_values($data); case 'client': - $data = GetData::perClient($flags, Request::any('new', false, 'string')); + $data = GetData::perClient($flags); // only show clients from locations which you have permission for $filterLocs = User::getAllowedLocations("table.view.client"); foreach ($data as $key => $row) { @@ -326,8 +322,7 @@ class Page_Statistics_Reporting extends Page } } // correct indexing of array after deletions - $data = array_values($data); - return $data; + return array_values($data); case 'user': return GetData::perUser($flags); case 'vm': |