diff options
| author | Simon Rettberg | 2019-02-12 14:55:49 +0100 | 
|---|---|---|
| committer | Simon Rettberg | 2019-02-12 14:55:49 +0100 | 
| commit | ec4ac82adaabe55cb9c3c40fad66d3451cb5f4d9 (patch) | |
| tree | 0134ad4e33aa259250fb46a82e7da44b091db88e | |
| parent | [serversetup*] PXELinux and iPXE side-by-side (diff) | |
| parent | [inc/Dictionary] Teh evil unvalidated redirects must die! (diff) | |
| download | slx-admin-ipxe.tar.gz slx-admin-ipxe.tar.xz slx-admin-ipxe.zip | |
Merge branch 'master' into ipxeipxe
24 files changed, 316 insertions, 146 deletions
| diff --git a/inc/dictionary.inc.php b/inc/dictionary.inc.php index fcbfdfb8..935d1f4e 100644 --- a/inc/dictionary.inc.php +++ b/inc/dictionary.inc.php @@ -30,10 +30,15 @@ class Dictionary  		if ($lang !== false && in_array($lang, self::$languages)) {  			setcookie('lang', $lang, time() + 60 * 60 * 24 * 30 * 12);  			$url = Request::get('url'); -			if ($url === false && isset($_SERVER['HTTP_REFERER'])) +			if ($url === false && isset($_SERVER['HTTP_REFERER'])) {  				$url = $_SERVER['HTTP_REFERER']; -			if ($url === false) -				$url = '?do=Main'; +			} +			$parts = parse_url($url); +			if ($url === false || $parts === false || empty($parts['query'])) { +				$url = '?do=main'; +			} else { +				$url = '?' . $parts['query']; +			}  			Util::redirect($url);  		} diff --git a/inc/render.inc.php b/inc/render.inc.php index 4b1d3643..4da0567e 100644 --- a/inc/render.inc.php +++ b/inc/render.inc.php @@ -213,7 +213,7 @@ class Render  	 * @param string $module name of module to load template from; defaults to currently active module  	 * @return string Rendered template  	 */ -	public static function parse($template, $params = false, $module = false) +	public static function parse($template, $params = false, $module = false, $lang = false)  	{  		if ($module === false && class_exists('Page')) {  			$module = Page::getModule()->getIdentifier(); @@ -228,7 +228,7 @@ class Render  		}  		// Now find all language tags in this array  		if (preg_match_all('/{{\s*(lang_.+?)\s*}}/', $html, $out) > 0) { -			$dictionary = Dictionary::getArray($module, 'template-tags'); +			$dictionary = Dictionary::getArray($module, 'template-tags', $lang);  			$fallback = false;  			foreach ($out[1] as $tag) {  				if ($fallback === false && empty($dictionary[$tag])) { diff --git a/inc/util.inc.php b/inc/util.inc.php index 1a5cbefe..e459cc46 100644 --- a/inc/util.inc.php +++ b/inc/util.inc.php @@ -234,9 +234,10 @@ SADFACE;  	 *   	 * @param float|int $bytes numeric value of the filesize to make readable  	 * @param int $decimals number of decimals to show, -1 for automatic +	 * @param int $shift how many units to skip, i.e. if you pass in KiB or MiB  	 * @return string human readable string representing the given file size  	 */ -	public static function readableFileSize($bytes, $decimals = -1) +	public static function readableFileSize($bytes, $decimals = -1, $shift = 0)  	{  		$bytes = round($bytes);  		static $sz = array('Byte', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'); @@ -249,7 +250,7 @@ SADFACE;  				$decimals = 2 - floor(strlen((int)$bytes) - 1);  			}  		} -		return sprintf("%.{$decimals}f", $bytes) . "\xe2\x80\x89" . $sz[$factor]; +		return sprintf("%.{$decimals}f", $bytes) . "\xe2\x80\x89" . $sz[$factor + $shift];  	}  	public static function sanitizeFilename($name) diff --git a/modules-available/locationinfo/inc/coursebackend.inc.php b/modules-available/locationinfo/inc/coursebackend.inc.php index 1da0086a..dcd92f6f 100644 --- a/modules-available/locationinfo/inc/coursebackend.inc.php +++ b/modules-available/locationinfo/inc/coursebackend.inc.php @@ -334,7 +334,7 @@ abstract class CourseBackend  	{  		$cleanresponse = preg_replace('/(<\/?)(\w+):([^>]*>)/', '$1$2$3', $response);  		try { -			$xml = new SimpleXMLElement($cleanresponse); +			$xml = @new SimpleXMLElement($cleanresponse); // This spams before throwing exception  		} catch (Exception $e) {  			$this->error = 'Could not parse reply as XML, got ' . get_class($e) . ': ' . $e->getMessage();  			if (CONFIG_DEBUG) { diff --git a/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php b/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php index 22b1d8fb..c71623b3 100644 --- a/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php +++ b/modules-available/locationinfo/inc/coursebackend/coursebackend_hisinone.inc.php @@ -271,6 +271,7 @@ class CourseBackend_HisInOne extends CourseBackend  			$eventDetails = array_merge($eventDetails, $event);  		}  		$name = false; +		$now = time();  		foreach ($eventDetails as $event) {  			foreach (array('/hisdefaulttext',  							'/hisshorttext', @@ -293,6 +294,12 @@ class CourseBackend_HisInOne extends CourseBackend  			foreach ($planElements as $planElement) {  				if (empty($planElement['hisplannedDates']))  					continue; +				$checkDate = $this->getArrayPath($planElement, '/hisplannedDates/hisplannedDate/hisenddate'); +				if (!empty($checkDate) && strtotime($checkDate[0]) + 86400 < $now) +					continue; // Course ended +				$checkDate = $this->getArrayPath($planElement, '/hisplannedDates/hisplannedDate/hisstartdate'); +				if (!empty($checkDate) && strtotime($checkDate[0]) - 86400 > $now) +					continue; // Course didn't start yet  				$unitPlannedDates = $this->getArrayPath($planElement,  					'/hisplannedDates/hisplannedDate/hisindividualDates/hisindividualDate');  				if ($unitPlannedDates === false) { diff --git a/modules-available/locationinfo/lang/de/template-tags.json b/modules-available/locationinfo/lang/de/template-tags.json index 03eb63a5..f9c7fad5 100644 --- a/modules-available/locationinfo/lang/de/template-tags.json +++ b/modules-available/locationinfo/lang/de/template-tags.json @@ -30,7 +30,8 @@      "lang_error": "Fehler",      "lang_expertMode": "Expertenmodus",      "lang_fourLocsHint": "Hier k\u00f6nnen Sie bis zu vier Orte ausw\u00e4hlen, die in diesem Panel angezeigt werden.", -    "lang_free": "Frei", +    "lang_free": "Geöffnet", +    "lang_for": "für",      "lang_general": "Allgemein",      "lang_ignoreSslTooltip": "Akzeptiere ung\u00fcltige, abgelaufene oder selbstsignierte SSL-Zertifikate",      "lang_insecureSsl": "Unsicheres SSL", diff --git a/modules-available/locationinfo/lang/en/template-tags.json b/modules-available/locationinfo/lang/en/template-tags.json index 1b5ab0fd..5bbe3775 100644 --- a/modules-available/locationinfo/lang/en/template-tags.json +++ b/modules-available/locationinfo/lang/en/template-tags.json @@ -30,7 +30,8 @@      "lang_error": "Error",      "lang_expertMode": "Expert mode",      "lang_fourLocsHint": "You can pick up to four locations that will be shown in this panel.", -    "lang_free": "Free", +    "lang_free": "Open", +    "lang_for": "for",      "lang_general": "General",      "lang_ignoreSslTooltip": "Accept invalid, expired or self-signed ssl certificates",      "lang_insecureSsl": "Insecure SSL", diff --git a/modules-available/locationinfo/page.inc.php b/modules-available/locationinfo/page.inc.php index 8ea18940..7be875d0 100644 --- a/modules-available/locationinfo/page.inc.php +++ b/modules-available/locationinfo/page.inc.php @@ -1002,7 +1002,7 @@ class Page_LocationInfo extends Page  				'language' => $config['language'],  			); -			die(Render::parse('frontend-default', $data)); +			die(Render::parse('frontend-default', $data, $module = false, $lang = $config['language']));  		}  		if ($type === 'SUMMARY') { @@ -1014,7 +1014,7 @@ class Page_LocationInfo extends Page  				'language' => $config['language'],  			); -			die(Render::parse('frontend-summary', $data)); +			die(Render::parse('frontend-summary', $data, $module = false, $lang = $config['language']));  		}  		http_response_code(500); diff --git a/modules-available/locationinfo/templates/frontend-default.html b/modules-available/locationinfo/templates/frontend-default.html index 4dee8ef7..cbb765d0 100755 --- a/modules-available/locationinfo/templates/frontend-default.html +++ b/modules-available/locationinfo/templates/frontend-default.html @@ -352,6 +352,7 @@ optional:  	<span data-tag="room">{{lang_room}}</span>  	<span data-tag="closed">{{lang_closed}}</span>  	<span data-tag="free">{{lang_free}}</span> +	<span data-tag="for">{{lang_for}}</span>  	<span data-tag="shortSun">{{lang_shortSun}}</span>  	<span data-tag="shortMon">{{lang_shortMon}}</span>  	<span data-tag="shortTue">{{lang_shortTue}}</span> @@ -1151,7 +1152,7 @@ optional:  	 */  	function SetFreeSeats(room) {  		// if room has no allowed value, set text in the box to - -		room.$.seatsCounter.text(room.freePcs >= 0 ? room.freePcs : '-'); +		room.$.seatsCounter.text(room.freePcs >= 0 ? room.freePcs : '\u2014');  		room.$.seatsCounter.data('state', JSON.stringify(room.state));  		if (room.freePcs > 0 && room.state && room.state.free) {  			room.$.seatsBackground.css('background-color', '#250'); @@ -1174,8 +1175,6 @@ optional:  		var seats = room.freePcs;  		if (tmp.state === 'closed' || tmp.state === 'CalendarEvent' || tmp.state === 'Free') {  			newTime = GetTimeDiferenceAsString(tmp.end, MyDate(), globalConfig); -		} else if (!same) { -			newTime = '';  		}  		if (tmp.state === "closed") {  			if (!same) newText = t("closed"); @@ -1183,16 +1182,14 @@ optional:  			if (!same) newText = tmp.title;  			// whilst event is running set freePcs to -, hopefully not breaking anything else with this  			room.freePcs = "-"; -		} else if (tmp.state === "Free") { -			if (!same) newText = t("free"); -		} else if (tmp.state === "FreeNoEnd") { +		} else if (tmp.state === "Free" || tmp.state === "FreeNoEnd") {  			if (!same) newText = t("free");  		}  		if (newText !== false) {  			room.$.currentEvent.text(newText);  		}  		if (newTime !== false) { -			room.$.currentRemain.text(newTime); +			room.$.currentRemain.text(t("for") + " " +newTime);  		}  		if (room.lastFreeSeats !== seats || !same) {  			SetFreeSeats(room); diff --git a/modules-available/locationinfo/templates/frontend-summary.html b/modules-available/locationinfo/templates/frontend-summary.html index 95299e63..ae089da5 100644 --- a/modules-available/locationinfo/templates/frontend-summary.html +++ b/modules-available/locationinfo/templates/frontend-summary.html @@ -63,7 +63,7 @@  		}  		.pc-idle, .pc-occupied, .pc-offline, .pc-broken, .pc-standby { -			padding: 2px 1px; +			padding: 2px 0px;  			text-align: center;  			font-size: 90%;  			font-weight: 800; @@ -604,7 +604,11 @@  			// TODO: Add seconds again with a better update rate.  			var time_split = time.split(":");  			if (time != "") { -				$("#div_Time_" + id).text(time_split[0] + ":" + time_split[1]); +				if (time_split[0] > 0) { +					$("#div_Time_" + id).text(t("for") + " " + time_split[0] + "h " + time_split[1]+"min"); +				} else { +					$("#div_Time_" + id).text(t("for") + " " + time_split[1]+"min"); +				}  			} else {  				$("#div_Time_" + id).text(time);  			} @@ -759,6 +763,7 @@  	<span data-tag="room">{{lang_room}}</span>  	<span data-tag="closed">{{lang_closed}}</span>  	<span data-tag="free">{{lang_free}}</span> +	<span data-tag="for">{{lang_for}}</span>  	<span data-tag="shortSun">{{lang_shortSun}}</span>  	<span data-tag="shortMon">{{lang_shortMon}}</span>  	<span data-tag="shortTue">{{lang_shortTue}}</span> diff --git a/modules-available/statistics/api.inc.php b/modules-available/statistics/api.inc.php index 674cc48d..280e5abc 100644 --- a/modules-available/statistics/api.inc.php +++ b/modules-available/statistics/api.inc.php @@ -34,7 +34,8 @@ if ($type{0} === '~') {  	// External mode of operation?  	$mode = Request::post('mode', false, 'string');  	$NOW = time(); -	$old = Database::queryFirst('SELECT clientip, logintime, lastseen, lastboot, state, mbram, cpumodel FROM machine WHERE machineuuid = :uuid', array('uuid' => $uuid)); +	$old = Database::queryFirst('SELECT clientip, logintime, lastseen, lastboot, state, mbram, cpumodel, live_memfree, live_swapfree, live_tmpfree +			FROM machine WHERE machineuuid = :uuid', array('uuid' => $uuid));  	if ($old !== false) {  		settype($old['logintime'], 'integer');  		settype($old['lastseen'], 'integer'); @@ -114,6 +115,7 @@ if ($type{0} === '~') {  				. ' cpumodel = :cpumodel,'  				. ' systemmodel = :systemmodel,'  				. ' id44mb = :id44mb,' +				. ' live_tmpsize = 0, live_swapsize = 0, live_memsize = 0,'  				. ' badsectors = :badsectors,'  				. ' data = :data,'  				. ' state = :state    ' @@ -123,32 +125,20 @@ if ($type{0} === '~') {  			}  		}  		// Maybe log old crashed session -		if ($uptime < 120) { +		if ($uptime < 150 && $old !== false) {  			// See if we have a lingering session, create statistic entry if so -			if ($old !== false && $old['logintime'] !== 0) { +			if ($old['state'] === 'OCCUPIED' && $old['logintime'] !== 0) {  				$sessionLength = $old['lastseen'] - $old['logintime'];  				if ($sessionLength > 30 && $sessionLength < 86400*2) { -					Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)' -						. " VALUES (:start, '~session-length', :uuid, :clientip, '', :length)", array( -						'start'     => $old['logintime'], -						'uuid'     => $uuid, -						'clientip'  => $ip, -						'length'    => $sessionLength -					)); +					Statistics::logMachineState($uuid, $ip, Statistics::SESSION_LENGTH, $old['logintime'], $sessionLength);  				}  			}  			// Write poweroff period length to statistic table -			if ($old !== false && $old['lastseen'] !== 0) { +			if ($old['lastseen'] !== 0) {  				$lastSeen = $old['lastseen'];  				$offtime = ($NOW - $uptime) - $lastSeen; -				if ($offtime > 300 && $offtime < 86400 * 90) { -					Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)' -						. " VALUES (:shutdown, '~offline-length', :uuid, :clientip, '', :length)", array( -						'shutdown' => $lastSeen, -						'uuid'     => $uuid, -						'clientip' => $ip, -						'length'   => $offtime -					)); +				if ($offtime > 90 && $offtime < 86400 * 30) { +					Statistics::logMachineState($uuid, $ip, $old['state'] === 'STANDBY' ? Statistics::SUSPEND_LENGTH : Statistics::OFFLINE_LENGTH, $lastSeen, $offtime);  				}  			}  		} @@ -164,7 +154,10 @@ if ($type{0} === '~') {  			// Log potential crash  			if ($old['state'] === 'IDLE' || $old['state'] === 'OCCUPIED') { -				writeClientLog('machine-mismatch-poweron', 'Client sent poweron event, but previous known state is ' . $old['state']); +				writeClientLog('machine-mismatch-poweron', 'Poweron event, but previous known state is ' . $old['state'] +					. '. RAM: ' . Util::readableFileSize($old['live_memfree'], -1, 2) +					. ', Swap: ' . Util::readableFileSize($old['live_swapfree'], -1, 2) +					. ', ID44: ' . Util::readableFileSize($old['live_tmpfree'], -1, 2));  			}  		} @@ -173,36 +166,45 @@ if ($type{0} === '~') {  	} else if ($type === '~runstate') {  		// Usage (occupied/free)  		$sessionLength = 0; +		$strUpdateBoottime = '';  		if ($old === false) die("Unknown machine.\n");  		if ($old['clientip'] !== $ip) {  			EventLog::warning("[runstate] IP address of client $uuid seems to have changed ({$old['clientip']} -> $ip)");  			die("Address changed.\n");  		}  		$used = Request::post('used', 0, 'integer'); -		if ($old['state'] === 'OFFLINE' && $NOW - $old['lastseen'] > 600) { -			$strUpdateBoottime = ' lastboot = UNIX_TIMESTAMP(), '; -		} else { -			$strUpdateBoottime = ''; -		} -		// 1) Log last session length if we didn't see the machine for a while -		if ($NOW - $old['lastseen'] > 610 && $old['lastseen'] !== 0) { -			// Old session timed out - might be caused by hard reboot -			if ($old['logintime'] !== 0) { -				if ($old['lastseen'] > $old['logintime']) { -					$sessionLength = $old['lastseen'] - $old['logintime']; -				} -				$old['logintime'] = 0; -			} -		} -		// Figure out what's happening - state changes  		$params = array(  			'uuid' => $uuid,  			'oldlastseen' => $old['lastseen'],  			'oldstate' => $old['state'],  		); +		if ($old['state'] === 'OFFLINE') { +			// This should never happen -- we expect a poweron event before runstate, which would set the state to IDLE +			// So it might be that the poweron event got lost, or that a couple of runstate events got lost, which +			// caused our cron.inc.php to time out the client and reset it to OFFLINE +			if ($NOW - $old['lastseen'] > 900) { +				$strUpdateBoottime = ' lastboot = UNIX_TIMESTAMP(), '; +			} +			// 1) Log last session length if we didn't see the machine for a while +			if ($NOW - $old['lastseen'] > 900 && $old['lastseen'] !== 0) { +				// Old session timed out - might be caused by hard reboot +				if ($old['logintime'] !== 0) { +					if ($old['lastseen'] > $old['logintime']) { +						$sessionLength = $old['lastseen'] - $old['logintime']; +					} +				} +			} +		} +		foreach (['memsize', 'tmpsize', 'swapsize', 'memfree', 'tmpfree', 'swapfree'] as $item) { +			$strUpdateBoottime .= ' live_' . $item . ' = :_' . $item . ', '; +			$params['_' . $item] = ceil(Request::post($item, 0, 'int') / 1024); +		} +		// Figure out what's happening - state changes  		if ($used === 0 && $old['state'] !== 'IDLE') { -			// Is not in use, was in use before -			$sessionLength = $NOW - $old['logintime']; +			if ($old['state'] === 'OCCUPIED' && $sessionLength === 0) { +				// Is not in use, was in use before +				$sessionLength = $NOW - $old['logintime']; +			}  			$res = Database::exec('UPDATE machine SET lastseen = UNIX_TIMESTAMP(),'  				. $strUpdateBoottime  				. " logintime = 0, currentuser = NULL, state = 'IDLE' " @@ -232,13 +234,7 @@ if ($type{0} === '~') {  		}  		// 9) Log last session length if applicable  		if ($mode === false && $sessionLength > 0 && $sessionLength < 86400*2 && $old['logintime'] !== 0) { -			Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)' -				. " VALUES (:start, '~session-length', :uuid, :clientip, '', :length)", array( -				'start'     =>  $old['logintime'], -				'uuid'     => $uuid, -				'clientip'  => $ip, -				'length'    => $sessionLength -			)); +			Statistics::logMachineState($uuid, $ip, Statistics::SESSION_LENGTH, $old['logintime'], $sessionLength);  		}  	} elseif ($type === '~poweroff') {  		if ($old === false) die("Unknown machine.\n"); @@ -246,16 +242,10 @@ if ($type{0} === '~') {  			EventLog::warning("[poweroff] IP address of client $uuid seems to have changed ({$old['clientip']} -> $ip)");  			die("Address changed.\n");  		} -		if ($mode === false && $old['logintime'] !== 0) { +		if ($mode === false && $old['state'] === 'OCCUPIED' && $old['logintime'] !== 0) {  			$sessionLength = $old['lastseen'] - $old['logintime'];  			if ($sessionLength > 0 && $sessionLength < 86400*2) { -				Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)' -					. " VALUES (:start, '~session-length', :uuid, :clientip, '', :length)", array( -					'start'     => $old['logintime'], -					'uuid'     => $uuid, -					'clientip'  => $ip, -					'length'    => $sessionLength -				)); +				Statistics::logMachineState($uuid, $ip, Statistics::SESSION_LENGTH, $old['logintime'], $sessionLength);  			}  		}  		Database::exec("UPDATE machine SET logintime = 0, lastseen = UNIX_TIMESTAMP(), state = 'OFFLINE' @@ -368,13 +358,7 @@ if ($type{0} === '~') {  				$lastSeen = $old['lastseen'];  				$duration = $NOW - $lastSeen;  				if ($duration > 500 && $duration < 86400 * 14) { -					Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)' -						. " VALUES (:suspend, '~suspend-length', :uuid, :clientip, '', :length)", array( -						'suspend' => $lastSeen, -						'uuid'     => $uuid, -						'clientip' => $ip, -						'length'   => $duration -					)); +					Statistics::logMachineState($uuid, $ip, Statistics::SUSPEND_LENGTH, $lastSeen, $duration);  				}  			}  		} else { @@ -449,9 +433,14 @@ if ($type{0} === '.') {  function checkHardwareChange($old, $new)  {  	if ($new['mbram'] !== 0) { -		if ($new['mbram'] + 1000 < $old['mbram']) { -			$ram1 = round($old['mbram'] / 512) / 2; -			$ram2 = round($new['mbram'] / 512) / 2; +		if ($new['mbram'] < 6200) { +			$ram1 = ceil($old['mbram'] / 512) / 2; +			$ram2 = ceil($new['mbram'] / 512) / 2; +		} else { +			$ram1 = ceil($old['mbram'] / 1024); +			$ram2 = ceil($new['mbram'] / 1024); +		} +		if ($ram1 !== $ram2) {  			EventLog::warning('[poweron] Client ' . $new['uuid'] . ' (' . $new['clientip'] . "): RAM decreased from {$ram1}GB to {$ram2}GB");  		}  		if (!empty($old['cpumodel']) && !empty($new['cpumodel']) && $new['cpumodel'] !== $old['cpumodel']) { diff --git a/modules-available/statistics/hooks/cron.inc.php b/modules-available/statistics/hooks/cron.inc.php index f05762bc..6393b2c6 100644 --- a/modules-available/statistics/hooks/cron.inc.php +++ b/modules-available/statistics/hooks/cron.inc.php @@ -21,22 +21,37 @@ function logstats()  function state_cleanup()  {  	// Fix online state of machines that crashed -	$standby = time() - 86400 * 2; // Reset standby machines after two days +	$standby = time() - 86400 * 4; // Reset standby machines after four days  	$on = time() - 610; // Reset others after ~10 minutes  	// Query for logging -	$res = Database::simpleQuery("SELECT machineuuid, clientip, state FROM machine WHERE lastseen < If(state = 'STANDBY', $standby, $on) AND state <> 'OFFLINE'"); +	$res = Database::simpleQuery("SELECT machineuuid, clientip, state, logintime, lastseen, live_memfree, live_swapfree, live_tmpfree +			FROM machine WHERE lastseen < If(state = 'STANDBY', $standby, $on) AND state <> 'OFFLINE'");  	while ($row = $res->fetch(PDO::FETCH_ASSOC)) {  		Database::exec('INSERT INTO clientlog (dateline, logtypeid, clientip, machineuuid, description, extra)  					VALUES (UNIX_TIMESTAMP(), :type, :client, :uuid, :description, :longdesc)', array(  			'type'        => 'machine-mismatch-cron',  			'client'      => $row['clientip'], -			'description' => 'Client timed out, last known state is ' . $row['state'], +			'description' => 'Client timed out, last known state is ' . $row['state'] +				. '. RAM: ' . Util::readableFileSize($row['live_memfree'], -1, 2) +				. ', Swap: ' . Util::readableFileSize($row['live_swapfree'], -1, 2) +				. ', ID44: ' . Util::readableFileSize($row['live_tmpfree'], -1, 2),  			'longdesc'    => '',  			'uuid'        => $row['machineuuid'],  		)); +		if ($row['state'] === 'OCCUPIED') { +			$length = $row['lastseen'] - $row['logintime']; +			if ($length > 0 && $length < 86400 * 7) { +				Statistics::logMachineState($row['machineuuid'], $row['clientip'], Statistics::SESSION_LENGTH, $row['logintime'], $length); +			} +		} elseif ($row['state'] === 'STANDBY') { +			$length = time() - $row['lastseen']; +			if ($length > 0 && $length < 86400 * 7) { +				Statistics::logMachineState($row['machineuuid'], $row['clientip'], Statistics::SUSPEND_LENGTH, $row['lastseen'], $length); +			} +		}  	} -	// Update -- yes this is not atomic. Should be sufficient for simple warnings though. -	Database::exec("UPDATE machine SET state = 'OFFLINE' WHERE lastseen < If(state = 'STANDBY', $standby, $on) AND state <> 'OFFLINE'"); +	// Update -- yes this is not atomic. Should be sufficient for simple warnings and bookkeeping though. +	Database::exec("UPDATE machine SET logintime = 0, state = 'OFFLINE' WHERE lastseen < If(state = 'STANDBY', $standby, $on) AND state <> 'OFFLINE'");  }  state_cleanup(); diff --git a/modules-available/statistics/inc/filter.inc.php b/modules-available/statistics/inc/filter.inc.php index 3ccea2c3..46de467b 100644 --- a/modules-available/statistics/inc/filter.inc.php +++ b/modules-available/statistics/inc/filter.inc.php @@ -14,6 +14,13 @@ class Filter  	public $operator;  	public $argument; +	private static $keyCounter = 0; + +	public static function getNewKey($colname) +	{ +		return $colname . '_' . (self::$keyCounter++); +	} +  	public function __construct($column, $operator, $argument = null)  	{  		$this->column = trim($column); @@ -24,8 +31,7 @@ class Filter  	/* returns a where clause and adds needed operators to the passed array */  	public function whereClause(&$args, &$joins)  	{ -		global $unique_key; -		$key = $this->column . '_arg' . ($unique_key++); +		$key = Filter::getNewKey($this->column);  		$addendum = '';  		/* check if we have to do some parsing*/ @@ -226,8 +232,7 @@ class StateFilter extends Filter  		$map = [ 'on' => ['IDLE', 'OCCUPIED'], 'off' => ['OFFLINE'], 'idle' => ['IDLE'], 'occupied' => ['OCCUPIED'], 'standby' => ['STANDBY'] ];  		$neg = $this->operator == '!=' ? 'NOT ' : '';  		if (array_key_exists($this->argument, $map)) { -			global $unique_key; -			$key = $this->column . '_arg' . ($unique_key++); +			$key = Filter::getNewKey($this->column);  			$args[$key] = $map[$this->argument];  			return " machine.state $neg IN ( :$key ) ";  		} else { @@ -259,8 +264,7 @@ class LocationFilter extends Filter  		if ($this->argument === 0) {  			return "machine.locationid IS $neg NULL";  		} else { -			global $unique_key; -			$key = $this->column . '_arg' . ($unique_key++); +			$key = Filter::getNewKey($this->column);  			if ($recursive) {  				$args[$key] = array_keys(Location::getRecursiveFlat($this->argument));  			} else { diff --git a/modules-available/statistics/inc/parser.inc.php b/modules-available/statistics/inc/parser.inc.php index b179b4a3..0d39079d 100644 --- a/modules-available/statistics/inc/parser.inc.php +++ b/modules-available/statistics/inc/parser.inc.php @@ -104,10 +104,12 @@ class Parser {  		foreach ($lines as $line) {  			if (preg_match('/^Disk (\S+):.* (\d+) bytes/i', $line, $out)) {  				// --- Beginning of MBR disk --- +				unset($hdd);  				if ($out[2] < 10000) // sometimes vmware reports lots of 512byte disks  					continue; +				if (substr($out[1], 0, 8) === '/dev/dm-') // Ignore device mapper +					continue;  				// disk total size and name -				unset($hdd);  				$mbrToMbFactor = 0; // This is != 0 for mbr  				$sectorToMbFactor = 0; // This is != for gpt  				$hdd = array( @@ -122,10 +124,12 @@ class Parser {  				$hdds[] = &$hdd;  			} elseif (preg_match('/^Disk (\S+):\s+(\d+)\s+sectors,/i', $line, $out)) {  				// --- Beginning of GPT disk --- +				unset($hdd);  				if ($out[2] < 1000) // sometimes vmware reports lots of 512byte disks  					continue; +				if (substr($out[1], 0, 8) === '/dev/dm-') // Ignore device mapper +					continue;  				// disk total size and name -				unset($hdd);  				$mbrToMbFactor = 0; // This is != 0 for mbr  				$sectorToMbFactor = 0; // This is != for gpt  				$hdd = array( @@ -213,7 +217,7 @@ class Parser {  				$hdd['size'] = round(($hdd['sectors'] * $sectorToMbFactor) / 1024);  			}  			$free = $hdd['size'] - $hdd['used']; -			if ($free > 5 || ($free / $hdd['size']) > 0.1) { +			if ($hdd['size'] > 0 && ($free > 5 || ($free / $hdd['size']) > 0.1)) {  				$hdd['partitions'][] = array(  					'id' => 'free-id-' . $i,  					'name' => Dictionary::translate('unused'), diff --git a/modules-available/statistics/inc/statistics.inc.php b/modules-available/statistics/inc/statistics.inc.php index 2500f16f..1f8a081a 100644 --- a/modules-available/statistics/inc/statistics.inc.php +++ b/modules-available/statistics/inc/statistics.inc.php @@ -70,4 +70,21 @@ class Statistics  		return $list;  	} +	const SESSION_LENGTH = '~session-length'; +	const OFFLINE_LENGTH = '~offline-length'; +	const SUSPEND_LENGTH = '~suspend-length'; + +	public static function logMachineState($uuid, $ip, $type, $start, $length, $username = '') +	{ +		return Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)' +			. " VALUES (:start, :type, :uuid, :clientip, :username, :length)", array( +			'start' => $start, +			'type'  => $type, +			'uuid'  => $uuid, +			'clientip' => $ip, +			'username' => $username, +			'length'   => $length, +		)); +	} +  } diff --git a/modules-available/statistics/install.inc.php b/modules-available/statistics/install.inc.php index 4e2dfcca..84e038a4 100644 --- a/modules-available/statistics/install.inc.php +++ b/modules-available/statistics/install.inc.php @@ -234,5 +234,22 @@ if (!tableHasColumn('machine', 'state')) {  	$res[] = UPDATE_DONE;  } +// 2019-01-25: Add memory/temp stats column +if (!tableHasColumn('machine', 'live_tmpsize')) { +	$ret = Database::exec("ALTER TABLE `machine` +		ADD COLUMN `live_tmpsize` int(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `id44mb`, +		ADD COLUMN `live_tmpfree` int(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `live_tmpsize`, +		ADD COLUMN `live_swapsize` int(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `live_tmpfree`, +		ADD COLUMN `live_swapfree` int(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `live_swapsize`, +		ADD COLUMN `live_memsize` int(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `live_swapfree`, +		ADD COLUMN `live_memfree` int(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `live_memsize`, +		ADD INDEX `live_tmpfree` (`live_tmpfree`), +		ADD INDEX `live_memfree` (`live_memfree`)"); +	if ($ret === false) { +		finalResponse(UPDATE_FAILED, 'Adding state column to machine table failed: ' . Database::lastError()); +	} +	$res[] = UPDATE_DONE; +} +  // Create response  responseFromArray($res); diff --git a/modules-available/statistics/lang/de/template-tags.json b/modules-available/statistics/lang/de/template-tags.json index 84c4690c..2567eea1 100644 --- a/modules-available/statistics/lang/de/template-tags.json +++ b/modules-available/statistics/lang/de/template-tags.json @@ -14,6 +14,7 @@      "lang_event": "Ereignis",      "lang_eventType": "Typ",      "lang_firstSeen": "Erste Aktivit\u00e4t", +    "lang_free": "frei",      "lang_gbRam": "RAM",      "lang_hardwareSummary": "Hardware",      "lang_hdds": "Festplatten", @@ -40,6 +41,7 @@      "lang_machineStandby": "Im Standby",      "lang_machineSummary": "Zusammenfassung",      "lang_maximumAbbrev": "Max.", +    "lang_memFree": "RAM frei (MB)",      "lang_memoryStats": "Arbeitsspeicher",      "lang_model": "Modell",      "lang_modelCount": "Anzahl", @@ -82,10 +84,12 @@      "lang_subnet": "Subnetz",      "lang_sureDeletePermanent": "M\u00f6chten Sie diese(n) Rechner wirklich unwiderruflich aus der Datenbank entfernen?\r\n\r\nWichtig: L\u00f6schen verhindert nicht, dass ein Rechner nach erneutem Starten von bwLehrpool wieder in die Datenbank aufgenommen wird.",      "lang_sureReplaceNoUndo": "Wollen Sie die Daten ausgew\u00e4hlten Rechner \u00fcbertragen? Diese Aktion kann nicht r\u00fcckg\u00e4ngig gemacht werden.", +    "lang_swapFree": "swap frei (MB)",      "lang_tempPart": "Temp. Partition",      "lang_tempPartStats": "Tempor\u00e4re Partition",      "lang_thoseAreProjectors": "Diese Modellnamen werden als Beamer behandelt, auch wenn die EDID-Informationen des Ger\u00e4tes anderes berichten.",      "lang_timebarDesc": "Visuelle Darstellung der letzten Tage. Rote Abschnitte zeigen, wann der Rechner belegt war, gr\u00fcne, wann er nicht verwendet wurde, aber eingeschaltet war. Die leicht abgedunkelten Abschnitte markieren N\u00e4chte (22 bis 8 Uhr).", +    "lang_tmpFree": "ID44 frei (MB)",      "lang_tmpGb": "Temp-HDD",      "lang_total": "Gesamt",      "lang_usageDetails": "Nutzungsdetails", diff --git a/modules-available/statistics/lang/en/template-tags.json b/modules-available/statistics/lang/en/template-tags.json index b064ee50..1d9cd4da 100644 --- a/modules-available/statistics/lang/en/template-tags.json +++ b/modules-available/statistics/lang/en/template-tags.json @@ -14,6 +14,7 @@      "lang_event": "Event",      "lang_eventType": "Type",      "lang_firstSeen": "First seen", +    "lang_free": "free",      "lang_gbRam": "RAM",      "lang_hardwareSummary": "Hardware",      "lang_hdds": "Hard disk drives", @@ -40,6 +41,7 @@      "lang_machineStandby": "In standby mode",      "lang_machineSummary": "Summary",      "lang_maximumAbbrev": "max.", +    "lang_memFree": "RAM free (MB)",      "lang_memoryStats": "Memory",      "lang_model": "Model",      "lang_modelCount": "Count", @@ -82,10 +84,12 @@      "lang_subnet": "Subnet",      "lang_sureDeletePermanent": "Are your sure you want to delete the selected machine(s) from the database? This cannot be undone.\r\n\r\nNote: Deleting machines from the database does not prevent booting up bwLehrpool again, which would recreate their respective database entries.",      "lang_sureReplaceNoUndo": "Are you sure you want to replace the selected machine pairs? This action cannot be undone.", +    "lang_swapFree": "swap free (MB)",      "lang_tempPart": "Temp. partition",      "lang_tempPartStats": "Temporary partition",      "lang_thoseAreProjectors": "These model names will always be treated as beamers, even if the device's EDID data says otherwise.",      "lang_timebarDesc": "Visual representation of the last few days. Red parts mark periods where the client was occupied, green parts where the client was idle. Dimmed parts mark nights (10pm to 8am).", +    "lang_tmpFree": "ID44 free (MB)",      "lang_tmpGb": "Temp HDD",      "lang_total": "Total",      "lang_usageDetails": "Detailed usage", diff --git a/modules-available/statistics/page.inc.php b/modules-available/statistics/page.inc.php index abacc8d2..a9cde6fb 100644 --- a/modules-available/statistics/page.inc.php +++ b/modules-available/statistics/page.inc.php @@ -1,7 +1,6 @@  <?php  global $STATS_COLORS, $SIZE_ID44, $SIZE_RAM; -global $unique_key;  $STATS_COLORS = array();  for ($i = 0; $i < 10; ++$i) { @@ -14,9 +13,9 @@ $SIZE_RAM = array(1, 2, 3, 4, 6, 8, 10, 12, 16, 24, 32, 48, 64, 96, 128, 192, 25  class Page_Statistics extends Page  {  	/* some constants, TODO: Find a better place */ -	public static $op_nominal; -	public static $op_ordinal; -	public static $op_stringcmp; +	const OP_NOMINAL = ['!=', '=']; +	const OP_ORDINAL = ['!=', '<=', '>=', '=', '<', '>']; +	const OP_STRCMP = ['!~', '~', '=', '!='];  	public static $columns;  	private $query; @@ -27,123 +26,130 @@ class Page_Statistics extends Page  	 */  	private $haveSubpage; -	/* PHP sucks, no static, const array definitions... Or am I missing something? */ -	public function initConstants() +	/** +	 * Do this here instead of const since we need to check for available modules while building array. +	 */ +	public static function initConstants()  	{ -		Page_Statistics::$op_nominal = ['!=', '=']; -		Page_Statistics::$op_ordinal = ['!=', '<=', '>=', '=', '<', '>']; -		Page_Statistics::$op_stringcmp = ['!~', '~', '=', '!='];  		Page_Statistics::$columns = [  			'machineuuid' => [ -				'op' => Page_Statistics::$op_nominal, +				'op' => Page_Statistics::OP_NOMINAL,  				'type' => 'string',  				'column' => true,  			],  			'macaddr' => [ -				'op' => Page_Statistics::$op_nominal, +				'op' => Page_Statistics::OP_NOMINAL,  				'type' => 'string',  				'column' => true,  			],  			'firstseen' => [ -				'op' => Page_Statistics::$op_ordinal, +				'op' => Page_Statistics::OP_ORDINAL,  				'type' => 'date',  				'column' => true,  			],  			'lastseen' => [ -				'op' => Page_Statistics::$op_ordinal, +				'op' => Page_Statistics::OP_ORDINAL,  				'type' => 'date',  				'column' => true,  			],  			'logintime' => [ -				'op' => Page_Statistics::$op_ordinal, +				'op' => Page_Statistics::OP_ORDINAL,  				'type' => 'date',  				'column' => true,  			],  			'realcores' => [ -				'op' => Page_Statistics::$op_ordinal, +				'op' => Page_Statistics::OP_ORDINAL,  				'type' => 'int',  				'column' => true,  			],  			'systemmodel' => [ -				'op' => Page_Statistics::$op_stringcmp, +				'op' => Page_Statistics::OP_STRCMP,  				'type' => 'string',  				'column' => true,  			],  			'cpumodel' => [ -				'op' => Page_Statistics::$op_stringcmp, +				'op' => Page_Statistics::OP_STRCMP,  				'type' => 'string',  				'column' => true,  			],  			'hddgb' => [ -				'op' => Page_Statistics::$op_ordinal, +				'op' => Page_Statistics::OP_ORDINAL,  				'type' => 'int',  				'column' => false,  				'map_sort' => 'id44mb'  			],  			'gbram' => [ -				'op' => Page_Statistics::$op_ordinal, +				'op' => Page_Statistics::OP_ORDINAL,  				'type' => 'int',  				'map_sort' => 'mbram',  				'column' => false,  			],  			'kvmstate' => [ -				'op' => Page_Statistics::$op_nominal, +				'op' => Page_Statistics::OP_NOMINAL,  				'type' => 'enum',  				'column' => true,  				'values' => ['ENABLED', 'DISABLED', 'UNSUPPORTED']  			],  			'badsectors' => [ -				'op' => Page_Statistics::$op_ordinal, +				'op' => Page_Statistics::OP_ORDINAL,  				'type' => 'int',  				'column' => true  			],  			'clientip' => [ -				'op' => Page_Statistics::$op_nominal, +				'op' => Page_Statistics::OP_NOMINAL,  				'type' => 'string',  				'column' => true  			],  			'hostname' => [ -				'op' => Page_Statistics::$op_stringcmp, +				'op' => Page_Statistics::OP_STRCMP,  				'type' => 'string',  				'column' => true  			],  			'subnet' => [ -				'op' => Page_Statistics::$op_nominal, +				'op' => Page_Statistics::OP_NOMINAL,  				'type' => 'string',  				'column' => false  			],  			'currentuser' => [ -				'op' => Page_Statistics::$op_nominal, +				'op' => Page_Statistics::OP_NOMINAL,  				'type' => 'string',  				'column' => true  			],  			'state' => [ -				'op' => Page_Statistics::$op_nominal, +				'op' => Page_Statistics::OP_NOMINAL,  				'type' => 'enum',  				'column' => true,  				'values' => ['occupied', 'on', 'off', 'idle', 'standby']  			], -			'runtime' => [ -				'op' => Page_Statistics::$op_ordinal, +			'live_swapfree' => [ +				'op' => Page_Statistics::OP_ORDINAL, +				'type' => 'int', +				'column' => true +			], +			'live_memfree' => [ +				'op' => Page_Statistics::OP_ORDINAL, +				'type' => 'int', +				'column' => true +			], +			'live_tmpfree' => [ +				'op' => Page_Statistics::OP_ORDINAL,  				'type' => 'int',  				'column' => true  			],  		];  		if (Module::isAvailable('locations')) {  			Page_Statistics::$columns['location'] = [ -				'op' => Page_Statistics::$op_stringcmp, +				'op' => Page_Statistics::OP_STRCMP,  				'type' => 'enum',  				'column' => false,  				'values' => array_keys(Location::getLocationsAssoc()),  			];  		} -		/* TODO ... */  	}  	protected function doPreprocess()  	{ -		$this->initConstants();  		User::load();  		if (!User::isLoggedIn()) {  			Message::addError('main.no-permission'); @@ -768,9 +774,9 @@ class Page_Statistics extends Page  					$row['currentsession'] = $lecture['displayname'];  					$row['lectureid'] = $lecture['lectureid'];  				} +				$row['session'] = $row['currentsession']; +				return;  			} -			$row['session'] = $row['currentsession']; -			return;  		}  		$res = Database::simpleQuery('SELECT dateline, username, data FROM statistic'  			. " WHERE clientip = :ip AND typeid = '.vmchooser-session-name'" @@ -787,14 +793,17 @@ class Page_Statistics extends Page  		}  		if ($session !== false) {  			$row['session'] = $session['data']; -			$row['username'] = $session['username']; +			if (empty($row['currentuser'])) { +				$row['username'] = $session['username']; +			}  		}  	}  	private function showMachine($uuid)  	{ -		$client = Database::queryFirst('SELECT machineuuid, locationid, macaddr, clientip, firstseen, lastseen, logintime, lastboot, state,' -			. ' mbram, kvmstate, cpumodel, id44mb, data, hostname, currentuser, currentsession, notes FROM machine WHERE machineuuid = :uuid', +		$client = Database::queryFirst('SELECT machineuuid, locationid, macaddr, clientip, firstseen, lastseen, logintime, lastboot, state, +			mbram, live_tmpsize, live_tmpfree, live_swapsize, live_swapfree, live_memsize, live_memfree, +			kvmstate, cpumodel, id44mb, data, hostname, currentuser, currentsession, notes FROM machine WHERE machineuuid = :uuid',  			array('uuid' => $uuid));  		if ($client === false) {  			Message::addError('unknown-machine', $uuid); @@ -830,6 +839,7 @@ class Page_Statistics extends Page  		$client['state_' . $client['state']] = true;  		$client['firstseen_s'] = date('d.m.Y H:i', $client['firstseen']);  		$client['lastseen_s'] = date('d.m.Y H:i', $client['lastseen']); +		$client['logintime_s'] = date('d.m.Y H:i', $client['logintime']);  		if ($client['lastboot'] == 0) {  			$client['lastboot_s'] = '-';  		} else { @@ -839,9 +849,14 @@ class Page_Statistics extends Page  				$client['lastboot_s'] .= ' (Up ' . floor($uptime / 86400) . 'd ' . gmdate('H:i', $uptime) . ')';  			}  		} -		$client['logintime_s'] = date('d.m.Y H:i', $client['logintime']); -		$client['gbram'] = round(round($client['mbram'] / 500) / 2, 1); +		$client['gbram'] = round(ceil($client['mbram'] / 512) / 2, 1);  		$client['gbtmp'] = round($client['id44mb'] / 1024); +		foreach (['tmp', 'swap', 'mem'] as $item) { +			if ($client['live_' . $item . 'size'] == 0) +				continue; +			$client['live_' . $item . 'percent'] = round(($client['live_' . $item . 'free'] / $client['live_' . $item . 'size']) * 100, 2); +			$client['live_' . $item . 'free_s'] = Util::readableFileSize($client['live_' . $item . 'free'], -1, 2); +		}  		$client['ramclass'] = $this->ramColorClass($client['mbram']);  		$client['kvmclass'] = $this->kvmColorClass($client['kvmstate']);  		$client['hddclass'] = $this->hddColorClass($client['gbtmp']); @@ -910,16 +925,18 @@ class Page_Statistics extends Page  		//if ($cutoff < $client['firstseen']) $cutoff = $client['firstseen'];  		$scale = 100 / ($NOW - $cutoff);  		$res = Database::simpleQuery('SELECT dateline, typeid, data FROM statistic' -			. " WHERE dateline > :cutoff AND typeid IN ('~session-length', '~offline-length') AND machineuuid = :uuid ORDER BY dateline ASC", array( +			. " WHERE dateline > :cutoff AND typeid IN (:sessionLength, :offlineLength) AND machineuuid = :uuid ORDER BY dateline ASC", array(  			'cutoff' => $cutoff - 86400 * 14,  			'uuid' => $uuid, +			'sessionLength' => Statistics::SESSION_LENGTH, +			'offlineLength' => Statistics::OFFLINE_LENGTH,  		));  		$spans['rows'] = array();  		$spans['graph'] = '';  		$last = false;  		$first = true;  		while ($row = $res->fetch(PDO::FETCH_ASSOC)) { -			if (!$client['isclient'] && $row['typeid'] === '~session-length') +			if (!$client['isclient'] && $row['typeid'] === Statistics::SESSION_LENGTH)  				continue; // Don't differentiate between session and idle for non-clients  			if ($first && $row['dateline'] > $cutoff && $client['lastboot'] > $cutoff) {  				// Special case: offline before @@ -945,9 +962,12 @@ class Page_Statistics extends Page  			}  			$row['from'] = Util::prettyTime($row['dateline']);  			$row['duration'] = floor($row['data'] / 86400) . 'd ' . gmdate('H:i', $row['data']); -			if ($row['typeid'] === '~offline-length') { +			if ($row['typeid'] === Statistics::OFFLINE_LENGTH) {  				$row['glyph'] = 'off';  				$color = '#444'; +			} elseif ($row['typeid'] === Statistics::SUSPEND_LENGTH) { +				$row['glyph'] = 'pause'; +				$color = '#686';  			} else {  				$row['glyph'] = 'user';  				$color = '#e77'; @@ -967,8 +987,26 @@ class Page_Statistics extends Page  		}  		if ($client['state'] === 'OCCUPIED') {  			$spans['graph'] .= '<div style="background:#e99;left:' . round(($client['logintime'] - $cutoff) * $scale, 2) . '%;width:' . round(($NOW - $client['logintime'] + 900) * $scale, 2) . '%"> </div>'; +			$spans['rows'][] = [ +				'from' => Util::prettyTime($client['logintime']), +				'duration' => '-', +				'glyph' => 'user', +			]; +			$row['duration'] = floor($row['data'] / 86400) . 'd ' . gmdate('H:i', $row['data']);  		} elseif ($client['state'] === 'OFFLINE') {  			$spans['graph'] .= '<div style="background:#444;left:' . round(($client['lastseen'] - $cutoff) * $scale, 2) . '%;width:' . round(($NOW - $client['lastseen'] + 900) * $scale, 2) . '%"> </div>'; +			$spans['rows'][] = [ +				'from' => Util::prettyTime($client['lastseen']), +				'duration' => '-', +				'glyph' => 'off', +			]; +		} elseif ($client['state'] === 'STANDBY') { +			$spans['graph'] .= '<div style="background:#686;left:' . round(($client['lastseen'] - $cutoff) * $scale, 2) . '%;width:' . round(($NOW - $client['lastseen'] + 900) * $scale, 2) . '%"> </div>'; +			$spans['rows'][] = [ +				'from' => Util::prettyTime($client['lastseen']), +				'duration' => '-', +				'glyph' => 'pause', +			];  		}  		$t = explode('-', date('Y-n-j-G', $cutoff));  		if ($t[3] >= 8 && $t[3] <= 22) { @@ -1104,3 +1142,5 @@ class Page_Statistics extends Page  			), true);  	}  } + +Page_Statistics::initConstants(); diff --git a/modules-available/statistics/style.css b/modules-available/statistics/style.css index 1496ac87..c48275ba 100644 --- a/modules-available/statistics/style.css +++ b/modules-available/statistics/style.css @@ -8,4 +8,36 @@      border-radius: 25px;      font-size: 20px;      line-height: 40px; +} + +.meter { +    position: relative; +    border-radius: 5px; +    border: 1px solid #999; +    background: linear-gradient(to right, #9e6 0%, #fb8 66%, #f32 100%); +    height: 1.25em; +    padding: 0; +    width: 100%; +    overflow: hidden; +} + +.meter .bar { +    position: absolute; +    top: 0; +    height: 100%; +    right: 0; +    background: #efe; +    display: inline-block; +    padding: 0; +    margin: 0 0 0 auto; +} + +.meter .text { +    position: absolute; +    right: 5px; +    overflow: visible; +    font-size: 8pt; +    white-space: nowrap; +    z-index: 1000; +    text-shadow: #fff 1px 1px;  }
\ No newline at end of file diff --git a/modules-available/statistics/templates/filterbox.html b/modules-available/statistics/templates/filterbox.html index cd8ec24d..07aa7320 100644 --- a/modules-available/statistics/templates/filterbox.html +++ b/modules-available/statistics/templates/filterbox.html @@ -101,7 +101,10 @@ var slxFilterNames = {  	currentuser: '{{lang_currentUser}}',  	subnet: '{{lang_subnet}}',  	runtime: '{{lang_runtimeHours}}', -	hostname: '{{lang_hostname}}' +	hostname: '{{lang_hostname}}', +	live_swapfree: '{{lang_swapFree}}', +	live_memfree: '{{lang_memFree}}', +	live_tmpfree: '{{lang_tmpFree}}'  };  slxLocations = {{{locations}}}; diff --git a/modules-available/statistics/templates/machine-main.html b/modules-available/statistics/templates/machine-main.html index f1021482..44f03a99 100644 --- a/modules-available/statistics/templates/machine-main.html +++ b/modules-available/statistics/templates/machine-main.html @@ -117,9 +117,23 @@  					<tr class="{{ramclass}}">  						<td class="text-nowrap">{{lang_ram}}</td>  						<td> -							{{gbram}} GiB -							{{#maxram}}({{lang_maximumAbbrev}} {{maxram}}){{/maxram}} -							{{ramtype}} +							<div> +								{{gbram}} GiB +								{{#maxram}}({{lang_maximumAbbrev}} {{maxram}}){{/maxram}} +								{{ramtype}} +							</div> +							{{#live_memsize}} +							<div class="meter"> +								<div class="text">{{live_memfree_s}} {{lang_free}}</div> +								<div class="bar" style="width:{{live_mempercent}}%"></div> +							</div> +							{{/live_memsize}} +							{{#live_swapsize}} +							<div class="meter"> +								<div class="text">{{live_swapfree_s}} {{lang_free}}</div> +								<div class="bar" style="width:{{live_swappercent}}%"></div> +							</div> +							{{/live_swapsize}}  						</td>  					</tr>  					{{#extram}} @@ -135,7 +149,17 @@  					{{/extram}}  					<tr class="{{hddclass}}">  						<td class="text-nowrap">{{lang_tempPart}}</td> -						<td>{{gbtmp}} GiB</td> +						<td> +							<div> +									{{gbtmp}} GiB +							</div> +							{{#live_tmpsize}} +								<div class="meter"> +									<div class="text">{{live_tmpfree_s}} {{lang_free}}</div> +									<div class="bar" style="width:{{live_tmppercent}}%"></div> +								</div> +							{{/live_tmpsize}} +						</td>  					</tr>  					<tr class="{{kvmclass}}">  						<td class="text-nowrap">{{lang_64bitSupport}}</td> diff --git a/modules-available/systemstatus/page.inc.php b/modules-available/systemstatus/page.inc.php index 958e763f..04423eaf 100644 --- a/modules-available/systemstatus/page.inc.php +++ b/modules-available/systemstatus/page.inc.php @@ -209,7 +209,7 @@ class Page_SystemStatus extends Page  			$data['swapTotal'] = Util::readableFileSize($info['SwapTotal'] * 1024);  			$data['swapUsed'] = Util::readableFileSize(($info['SwapTotal'] - $info['SwapFree']) * 1024);  			$data['swapPercent'] = 100 - round(($info['SwapFree'] / $info['SwapTotal']) * 100); -			$data['swapWarning'] = ($data['swapPercent'] > 50 || ($info['SwapTotal'] - $info['SwapFree']) > 200000); +			$data['swapWarning'] = ($data['swapPercent'] > 50 || $info['SwapFree'] < 400000);  		}  		if (isset($info['CpuIdle']) && isset($info['CpuSystem']) && isset($info['CpuTotal'])) {  			$data['cpuLoad'] = 100 - round(($info['CpuIdle'] / $info['CpuTotal']) * 100); diff --git a/style/default.css b/style/default.css index 6b0c28f6..6c2beb86 100644 --- a/style/default.css +++ b/style/default.css @@ -76,7 +76,7 @@ body {  }  .slx-table td { -	padding-right: 5px; +	padding-right: 7px;  	padding-bottom: 2px;  } | 
