summaryrefslogblamecommitdiffstats
path: root/modules-available/statistics/pages/list.inc.php
blob: f08cd71c811c4f4164b73d37a38cb29069a63c45 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12











                                                    
                                                          
                                                               




                                                                                  
                                                                      



                                                  
                                                                                     

                                                      
                                                                
                           


                                                                                  
                                                                                                        

                         
                                                                                                                     
                                                                                                                                      
                                                              
                                                                                           
                                                                                      
                                                                           











                                                                                                            






                                                                                                                       

                                                                                                                        






                                                                                                                                             
                                                                                              
                                                                                                
                                                                                             

                                                                                             
                                                        
                           
                                                                             
                                             
                                                           




                                                                                                      


                                                                                                                                                   

                                                                 
                                                                                    

                                                               
                                                                                                
                                                                                              
                                                                                                


                                                                    




                                                                                                    



                                                                                                                                       
                         






                                                                                                                

                                                                   
                         
                                                                                       









                                                                                                                                                                             


                                                                   
                         









                                                                                                                                                          
                                                              


                                                                                 





                                                                                                       
                                        
                 

                                                                                                                
                                
                           
                                                            


                                                                                                   








                                                                                                                                

                                                                            












                                                                               
                                 

                         


                                                   






                                                                                    
                                                                 
                                                                   
                                                                             

                                              



                                                         
                                                            








                                                                                       
 
<?php

class SubPage
{

	public static function doPreprocess()
	{
		User::assertPermission('view.list');
	}

	public static function doRender()
	{
		$filters = StatisticsFilter::parseQuery();
		$filterSet = new StatisticsFilterSet($filters);

		if (!$filterSet->setAllowedLocationsFromPermission('view.list')) {
			Message::addError('main.no-permission');
			Util::redirect('?do=main');
		}
		StatisticsFilter::renderFilterBox('list', $filterSet);
		self::showMachineList($filterSet);
	}


	private static function showMachineList(StatisticsFilterSet $filterSet): void
	{
		Module::isAvailable('js_stupidtable');
		$filterSet->makeFragments($where, $join, $args);
		$xtra = '';
		if (Module::isAvailable('runmode')) {
			$xtra .= ', runmode.module AS rmmodule, runmode.isclient';
			if (strpos($join, 'runmode') === false) {
				$join .= ' LEFT JOIN runmode ON (m.machineuuid = runmode.machineuuid) ';
			}
		}
		$allRows = Database::queryAll("SELECT m.machineuuid, m.locationid, m.macaddr, m.clientip, m.lastseen,
			m.logintime, m.state, m.currentuser, m.currentrunmode, m.realcores, m.mbram, m.kvmstate, m.cpumodel, m.id44mb,
       	m.id45mb, m.hostname, m.notes IS NOT NULL AS hasnotes,
			m.badsectors, Count(s.machineuuid) AS confvars $xtra FROM machine m
			LEFT JOIN setting_machine s ON (m.machineuuid = s.machineuuid)
			$join WHERE $where GROUP BY m.machineuuid", $args);
		// If filter results in just one result, redirect to machine details
		if (count($allRows) === 1) {
			Util::redirect('?do=statistics&uuid=' . $allRows[0]['machineuuid']);
		}
		// Gather additional info that would be ugly to fetch via joins above
		$uuids = array_column($allRows, 'machineuuid');
		$machineWithHdds = Database::queryKeyValueList("SELECT mxx.machineuuid, Count(s.hwid) AS num
			FROM statistic_hw s
			INNER JOIN machine_x_hw AS mxx ON (s.hwid = mxx.hwid AND s.hwtype = :type
				AND mxx.disconnecttime = 0 AND mxx.machineuuid IN (:ids))
			GROUP BY mxx.machineuuid",
		['type' => HardwareInfo::HDD, 'ids' => $uuids]);
		$machineNicSpeed = Database::queryKeyValueList("SELECT mxx.machineuuid, Max(mxhp.`numeric`) AS num
			FROM statistic_hw s
			INNER JOIN machine_x_hw AS mxx ON (s.hwid = mxx.hwid AND s.hwtype = :type
				AND mxx.disconnecttime = 0 AND mxx.machineuuid IN (:ids))
			INNER JOIN machine_x_hw_prop mxhp ON (mxx.machinehwid = mxhp.machinehwid AND mxhp.prop = :prop)
			GROUP BY mxx.machineuuid",
			['type' => HardwareInfo::MAINBOARD, 'prop' => 'nic-speed', 'ids' => $uuids]);
		$machineWithConfigOverrides = Database::queryKeyValueList("SELECT machineuuid, Count(machineuuid) AS num
			FROM setting_machine WHERE machineuuid IN (:ids) GROUP BY machineuuid", ['ids' => $uuids]);
		// TODO: Cannot disable checkbox for those where user has no permission, since we got multiple actions now
		// We should pass these lists to the output and add some JS magic
		// Either disable the delete/reboot/... buttons as soon as at least one "forbidden" client is selected (potentially annoying)
		// or add a notice to the confirmation dialog of the according action (nicer but a little more work)
		$deleteAllowedLocations = User::getAllowedLocations("machine.delete");
		$rebootAllowedLocations = User::getAllowedLocations('.rebootcontrol.action.reboot');
		$shutdownAllowedLocations = User::getAllowedLocations('.rebootcontrol.action.reboot');
		$wolAllowedLocations = User::getAllowedLocations('.rebootcontrol.action.wol');
		$execAllowedLocations = User::getAllowedLocations('.rebootcontrol.action.exec');
		$benchmarkAllowedLocations = User::getAllowedLocations('.vmstore.benchmark');
		// Only make client clickable if user is allowed to view details page
		$detailsAllowedLocations = User::getAllowedLocations("machine.view-details");
		$location = self::buildLocationLookup();
		$rows = [];
		$colValCount = []; // Count unique values for several columns
		foreach ($allRows as &$row) {
			settype($row['locationid'], 'int');
			$row['link_details'] = in_array($row['locationid'], $detailsAllowedLocations);
			//$row['firstseen'] = Util::prettyTime($row['firstseen']);
			$row['lastseen_int'] = $row['lastseen'];
			$row['lastseen'] = Util::prettyTime($row['lastseen']);
			//$row['lastboot'] = Util::prettyTime($row['lastboot']);
			$row['gbram'] = Dictionary::number(ceil($row['mbram'] / 512) / 2, 1); // Trial and error until we got "expected" rounding..
			$row['gbtmp'] = Dictionary::number($row['id44mb'] / 1024);
			$row['gbpersist'] = Dictionary::number($row['id45mb'] / 1024);
			$octets = explode('.', $row['clientip']);
			if (count($octets) === 4) {
				$row['subnet'] = "$octets[0].$octets[1].$octets[2]";
				$row['lastoctet'] = $octets[3];
			}
			$row['ramclass'] = StatisticsStyling::ramColorClass((int)$row['mbram']);
			$row['kvmclass'] = StatisticsStyling::kvmColorClass($row['kvmstate']);
			$row['hddclass'] = StatisticsStyling::hddColorClass((int)$row['gbtmp']);
			if (empty($row['hostname'])) {
				$row['hostname'] = $row['clientip'];
			}
			if (isset($machineWithConfigOverrides[$row['machineuuid']])) {
				$row['confvars'] = $machineWithConfigOverrides[$row['machineuuid']];
			}
			if (isset($machineWithHdds[$row['machineuuid']])) {
				$row['hddcount'] = $machineWithHdds[$row['machineuuid']];
			} else if ($row['id44mb'] > 0) {
				// This might be a machine that wasn't booted with a recent system, and hence doesn't have HWinfo in DB
				// If we have ID44 space in our main table, we most likely got an HDD, so fake a count of 1
				$row['hddcount'] = 1;
			}
			if (!isset($machineNicSpeed[$row['machineuuid']])) {
				$row['nic-speed'] = 0;
				$row['nic-speed_s'] = '???';
			} else {
				$row['nic-speed'] = $machineNicSpeed[$row['machineuuid']];
				$row['nic-speed_s'] = Dictionary::number($machineNicSpeed[$row['machineuuid']]);
			}
			if (isset($row['data']) && !$row['data']) {
				$row['nohdd'] = true;
			}
			// Shorten CPU names a bit for prettier display in small column
			$row['cpumodel'] = preg_replace('/\(R\)|\(TM\)|\bintel\b|\bamd\b|\bcpu\b|dual-core|\bdual\s+core\b|\bdual\b|\bprocessor\b/i', ' ', $row['cpumodel']);
			if (!empty($row['rmmodule'])) {
				$data = RunMode::getRunMode($row['machineuuid'], RunMode::DATA_STRINGS);
				if ($data !== false) {
					$row['moduleName'] = $data['moduleName'];
					$row['modeName'] = $data['modeName'];
				}
				if (!$row['isclient'] && $row['state'] === 'IDLE') {
					$row['state'] = 'OCCUPIED';
				}
				if (!$row['isclient']) {
					unset($row['currentuser']);
				}
			}
			if ($row['state'] === 'IDLE' || $row['state'] === 'OCCUPIED') {
				if ((!empty($row['currentrunmode']) || !empty($row['rmmodule']))
						&& $row['currentrunmode'] !== $row['rmmodule']) {
					$row['wrongRunMode'] = true;
					if (!empty($row['currentrunmode'])) {
						$wrongModule = Module::get($row['currentrunmode']);
						$row['currentrunmode'] = $wrongModule === false ? $row['currentrunmode'] : $wrongModule->getDisplayName();
					}
				}
			}
			$row['state_' . $row['state']] = true;
			if ($row['locationid'] > 0) {
				$row['location'] = $location[$row['locationid']];
			}
			foreach (['locationid', 'cpumodel', 'nic-speed_s', 'gbram', 'gbtmp'] as $key) {
				if (!isset($colValCount[$key][$row[$key]])) {
					$colValCount[$key][$row[$key]] = [];
				}
				$colValCount[$key][$row[$key]][] = $row['machineuuid'];
			}
			$rows[] =& $row;
		}
		// Now if all machines are from the same location, try to load the roomplan
		// Also, collect all properties that are the same across all machines for display in the sidebar
		$roomsvg = null;
		$side = [];
		if (!empty($rows) && !empty($colValCount)) {
			if (count($colValCount['locationid']) === 1
					&& ($lid = array_key_first($colValCount['locationid'])) > 0
					&& Module::isAvailable('roomplanner')) {
				$roomsvg = PvsGenerator::generateSvg($lid, false, 0, 1, true, $colValCount['locationid'][$lid]);
			}
			// Handle our selected attributes
			foreach (['locationid', 'cpumodel', 'nic-speed_s', 'gbram', 'gbtmp'] as $key) {
				if (count($colValCount[$key]) === 1) {
					$val = array_key_first($colValCount[$key]);
					// Suffixes are not localized, but hopefully generic enough for now
					switch ($key) {
					case 'locationid':
						if (!isset($location[$val]))
							continue 2;
						$val = $location[$val]['name'];
						break;
					case 'gbram':
						$val .= ' GiB RAM';
						break;
					case 'gbtmp':
						$val .= ' GiB ID-44';
						break;
					case 'nic-speed_s':
						$val .= ' MBit/s';
						break;
					}
					$side[] = $val;
				}
			}
		}
		$data = array(
			'rowCount' => count($rows),
			'rows' => $rows,
			'showList' => 1,
			'show' => 'list',
			'redirect' => $_SERVER['QUERY_STRING'],
			'rebootcontrol' => (Module::get('rebootcontrol') !== false),
			'canReboot' => !empty($rebootAllowedLocations),
			'canShutdown' => !empty($shutdownAllowedLocations),
			'canDelete' => !empty($deleteAllowedLocations),
			'canWol' => !empty($wolAllowedLocations),
			'canExec' => !empty($execAllowedLocations),
			'canBenchmark' => !empty($benchmarkAllowedLocations),
			'roomsvg' => $roomsvg,
			'sidebar' => $side,
		);
		Render::addTemplate('clientlist', $data);
	}

	private static function buildLocationLookup(): array
	{
		$ret = [];
		$i = 0;
		foreach (Location::getLocationsAssoc() as $lid => $data) {
			$ret[$lid] = ['sort' => ++$i, 'name' => $data['locationname']];
		}
		return $ret;
	}

}