1 <?php
3 class statistics extends plugin
4 {
5 var $plHeadline = 'Statistics';
6 var $plDescription = 'GOsa usage statistics';
7 var $plShortIcon = 'statistics.png';
8 var $plIcon = 'plugin.png';
10 var $rpcHandle = NULL;
11 var $rpcConfigured = FALSE;
13 // Graph data
14 var $statisticData = array(); // Via rpc received stats
16 // Font used in graphs
17 var $font = "./themes/default/fonts/LiberationSans-Regular.ttf";
19 // Datepicker initial
20 var $graph1DatePicker1 = 0;
21 var $graph1DatePicker2 = 0;
23 // A collection of timestamps for unsubmitted statistics data.
24 var $unsbmittedFiles = NULL;
26 var $graphs = array();
27 var $selectedGraphType = 0;
29 var $serverAccessible = FALSE;
30 var $instanceRegistered = FALSE;
31 var $registrationNotWanted = FALSE;
33 var $initialized = FALSE;
35 function __construct($config)
36 {
37 plugin::plugin($config, NULL);
38 }
40 function init()
41 {
42 // Detect registration status first
43 $this->serverAccessible = $this->config->registration->isServerAccessible();
44 $this->registrationNotWanted = $this->config->registration->registrationNotWanted();
45 $this->instanceRegistered = $this->config->registration->isInstanceRegistered();
46 if($this->instanceRegistered){
47 $this->graphs[] = new categoryActionsOverTime($this->config);
48 $this->graphs[] = new memoryUsageChart($this->config);
49 $this->graphs[] = new cpuLoadChart($this->config);
50 $this->graphs[] = new renderTimeChart($this->config);
51 $this->graphs[] = new durationTimeChart($this->config);
52 $this->graphs[] = new objectCountChart($this->config);
53 $this->graphs[] = new actionSelectChart($this->config);
54 $this->graphs[] = new passwordChangeChart($this->config);
55 $this->staticChart1 = new pieChart1($this->config);
56 $this->staticChart2 = new pieChart2($this->config);
59 // Init start and stop times for graph 1
60 $this->graph1DatePicker1 = date('d.m.Y', time() - 28 * 24 * 60 *60);
61 $this->graph1DatePicker2 = date('d.m.Y', time());
63 // First try to retrieve values via RPC
64 $this->rpcConfigured = TRUE;
65 $this->rpcHandle = $this->config->getRpcHandle(
66 $this->config->registration->getRegistrationServer(),
67 $this->config->getInstanceUUID(),
68 $this->config->configRegistry->getPropertyValue('GOsaRegistration','instancePassword'),
69 TRUE);
71 $this->initialized = TRUE;
72 }
73 }
76 /*! \brief Returns a list local stored statistic files
77 @param Array A list of filenames and dates.
78 */
79 function getLocalStatisticsFiles()
80 {
81 $res = stats::getLocalStatFiles();
82 $tmp = array();
83 if(count($res)){
84 foreach($res as $file){
85 $date = strtotime($file);
86 if($date){
87 $tmp[$file] = $date;
88 }
89 }
90 }
91 return($tmp);
92 }
95 /*! \brief Returns a list of not transmitted stat files (except files for the current day)
96 * @return Array A list of unsubmitted statistic files.
97 */
98 function getUnsubmittedStatistics()
99 {
100 $alreadyTransmitted = $this->getStatisticsDatesFromServer();
101 if($alreadyTransmitted == NULL){
102 return(NULL);
103 }
105 $available = $this->getLocalStatisticsFiles();
107 $unsubmitted = array();
108 foreach($available as $key => $day){
109 if(!isset($alreadyTransmitted[$key])) $unsubmitted [$key] = $day;
110 }
112 // Exclude statistic collection from today, they are still active and cannot be submitted.
113 $curDate = date('Y-m-d');
114 if(isset($unsubmitted)) unset($unsubmitted[$curDate]);
115 return($unsubmitted);
116 }
119 /*! \brief Request a list of dates for which the server can return statistics.
120 @param Array A list of dates $ret=[iso-str] = timestamp
121 */
122 function getStatisticsDatesFromServer()
123 {
124 // Do not request anything while rpc isn't configured.
125 if(!$this->rpcConfigured){
126 return(array());
127 }
129 // Try to gather statistic dates from the backenbd.
130 $res = $this->rpcHandle->getInstanceStatDates();
131 $dates = array();
132 if(!$this->rpcHandle->success()){
133 msg_dialog::display(_("Error"),msgPool::rpcError($this->rpcHandle->get_error()),ERROR_DIALOG);
134 return(NULL);
135 }else{
136 foreach($res as $date){
137 $dates[$date] = strtotime($date);
138 }
139 }
140 $this->rpcHandle_Error = !$this->rpcHandle->success();
141 return($dates);
142 }
145 function execute()
146 {
148 if(!$this->initialized) $this->init();
150 $smarty = get_smarty();
152 // Check registration status, we need at least 'registered'.
153 $smarty->assign("instanceRegistered", $this->instanceRegistered);
154 $smarty->assign("serverAccessible", $this->serverAccessible);
155 $smarty->assign("registrationNotWanted", $this->registrationNotWanted);
156 $plist = session::get('plist');
157 $id = array_search('dashBoard', $plist->pluginList);
158 $smarty->assign('dashBoardId', $id);
160 // If we have an unregistered instance of GOsa, display a message which
161 // allows to registrate GOsa
162 if(!$this->instanceRegistered || !$this->serverAccessible){
163 return($smarty->fetch(get_template_path('statistics.tpl', TRUE)));
164 }
166 $smarty->assign('graph1DatePicker1', $this->graph1DatePicker1);
167 $smarty->assign('graph1DatePicker2', $this->graph1DatePicker2);
169 // Get list of unsubmitted files.
170 if($this->unsbmittedFiles == NULL){
171 $this->unsbmittedFiles = $this->getUnsubmittedStatistics();
172 }
174 // Do not render anything if we are not prepared to send and receive data via rpc.
175 $smarty->assign("rpcConfigured", $this->rpcConfigured);
176 $smarty->assign("validRpcHandle", TRUE);
177 if(!$this->rpcHandle){
178 $smarty->assign("validRpcHandle", FALSE);
179 return($smarty->fetch(get_template_path('statistics.tpl', TRUE)));
180 }
182 // Assign list of selectable graphs
183 $tmp = array();
184 foreach($this->graphs as $id => $gClass){
185 $tmp[$id] = $gClass->getTitle();
186 }
187 $smarty->assign("selectedGraphType", $this->selectedGraphType);
188 $smarty->assign("availableGraphs", $tmp);
190 // Send stats
191 if(isset($_POST['transmitStatistics'])){
192 $this->unsbmittedFiles = $this->getUnsubmittedStatistics();
193 foreach($this->unsbmittedFiles as $filename => $date){
195 $strDate = date('Y-m-d', $date);
196 $tmp = stats::generateStatisticDump($filename);
197 $dump = array();
198 $invalidDateCount = 0;
199 foreach($tmp as $entry){
201 // Check if the result date equals the report file date
202 // - If not update the entry.
203 if($entry['date'] != $strDate){
204 $entry['date'] = $strDate;
205 $invalidDateCount ++;
206 }
207 $dump[] = array_values($entry);
208 }
210 // Merge result with ldap object count
211 $objectCount = stats::getLdapObjectCount($this->config, TRUE, date('Y-m-d', $date));
212 foreach($objectCount as $entry){
214 // Check if the result date equals the report file date
215 // - If not update the entry.
216 if($entry['date'] != $strDate){
217 $entry['date'] = $strDate;
218 $invalidDateCount ++;
219 }
220 $dump[] = array_values($entry);
221 }
223 // Warn about invalid dates transmitted
224 if($invalidDateCount) trigger_error(sprintf("Report contained %s entries with invalid date string!",$invalidDateCount));
226 // Send in our report now.
227 $res = $this->rpcHandle->updateInstanceStatus($dump);
228 if(!$this->rpcHandle->success()){
229 msg_dialog::display(_("Error"),msgPool::rpcError($this->rpcHandle->get_error()),ERROR_DIALOG);
230 }else{
231 stats::removeStatsFile($filename);
232 }
233 $this->rpcHandle_Error = !$this->rpcHandle->success();
234 }
235 $this->unsbmittedFiles = $this->getUnsubmittedStatistics();
236 }
238 // Transmit daily statistics to GOsa-Server
239 if((isset($_POST['receiveStatistics']) || !count($this->statisticData)) && $this->rpcConfigured){
240 $start = strtotime($this->graph1DatePicker1);
241 $stop = strtotime($this->graph1DatePicker2);
242 $res = $this->rpcHandle->getInstanceStats($start,$stop);
243 if(!$this->rpcHandle->success()){
244 msg_dialog::display(_("Error"),msgPool::rpcError($this->rpcHandle->get_error()),ERROR_DIALOG);
245 }elseif($res){
246 $this->statisticData = $this->prepareGraphData($res);
247 }
248 $this->rpcHandle_Error = !$this->rpcHandle->success();
249 }
251 // Update graphs
252 $this->reloadGraphs();
254 $smarty->assign('staticChart1_ID', $this->staticChart1->getGraphID());
255 $smarty->assign('staticChart2_ID', $this->staticChart2->getGraphID());
257 $curGraph = $this->graphs[$this->selectedGraphType];
258 $smarty->assign('curGraphID', $curGraph->getGraphID());
259 $smarty->assign('curGraphOptions', $curGraph->getGraphOptions());
260 $smarty->assign('curSeriesSelector', $curGraph->getSeriesSelector());
261 $smarty->assign('unsbmittedFiles', count($this->unsbmittedFiles));
262 $smarty->assign('unsbmittedFilesMsg', sprintf(
263 _("You have currently %s unsubmitted statistic collection, do you want to transmit them now?"),
264 count($this->unsbmittedFiles)));
266 $smarty->assign('rpcHandle_Error', $this->rpcHandle_Error);
267 return($smarty->fetch(get_template_path('statistics.tpl', TRUE)));
268 }
271 /*! \brief Prepares the graph data we've received from the rpc-service.
272 * This method will construct a usable data-array with converted
273 * date strings.
274 */
275 function prepareGraphData($res)
276 {
277 // Object categories which has to mapped to 'systems'
278 $mapSystems = array('server','terminal','workstation', 'opsi',
279 'component','phone', 'winworkstation', 'printer', 'incoming');
281 /* Build up array which represents the amount of errors per
282 * interval.
283 */
284 $gData = array();
285 foreach($res['errorsPerInterval'] as $dateStr => $data){
286 $date = strtotime($dateStr);
287 $gData['errorsPerInterval'][$date] = $data;
288 }
289 ksort($gData['errorsPerInterval']);
292 /* Build up timeline
293 */
294 $Xam = 5;
295 $cnt = 0;
296 $numCnt = $res['errorsPerInterval'];
297 foreach($gData['errorsPerInterval'] as $date => $data){
298 if((count($numCnt) <= $Xam) ||
299 ($cnt % (floor(count($numCnt) / $Xam )) == 0)){
300 $gData['dates'][$date] = date('d.m.Y', $date);
301 }else{
302 $gData['dates'][$date] = ' ';
303 }
304 $cnt ++;
305 }
306 ksort($gData['dates']);
308 /* Build up 'actions per category' array, this will later
309 * be represented using a pie chart.
310 */
311 $gData['actionsPerCategory'] = $res['actionsPerCategory'];
312 arsort($gData['actionsPerCategory']);
315 /* Build up system-info array per interval.
316 */
317 foreach($res['usagePerInterval'] as $dateStr => $data){
318 $date = strtotime($dateStr);
319 foreach($data as $type => $count){
320 $gData['usagePerInterval'][$type][$date] = $count;
321 }
322 }
323 foreach($gData['usagePerInterval'] as $key => $data)
324 ksort($gData['usagePerInterval'][$key]);
327 /* Prepare actions-per-interval array.
328 */
329 $gData['actionsGraph'] = array();
330 foreach($res['actionsGraph'] as $category => $data){
331 if(empty($category)) continue;
332 foreach($data as $dateStr => $count){
333 $date = strtotime($dateStr);
334 $gData['actionsGraph'][$category][$date]=$count;
335 }
336 ksort($gData['actionsGraph'][$category]);
337 }
339 /* Prepare actions-per-interval array.
340 */
341 $gData['actionTypeGraph'] = array();
342 foreach($res['actionTypeGraph'] as $action => $aData){
343 foreach($aData as $category => $data){
344 if(empty($category)) continue;
346 $list = preg_split("/, /", $category);
347 if(count(array_intersect($mapSystems, $list))){
348 $category = 'systems';
349 }
351 // Add missing date stamps
352 foreach($gData['dates'] as $timestamp => $value){
353 $gData['actionTypeGraph'][$action][$category][$timestamp]=0;
354 }
356 foreach($data as $dateStr => $count){
357 $date = strtotime($dateStr);
358 $gData['actionTypeGraph'][$action][$category][$date]=$count;
359 }
360 ksort($gData['actionTypeGraph'][$action][$category]);
361 }
362 }
364 /* Prepare object count per interval array.
365 */
366 $gData['objectCountPerInterval'] = array();
367 foreach($res['objectCountPerInterval'] as $category => $data){
368 if(empty($category)) continue;
369 if(in_array_strict($category,$mapSystems)){
370 $category = 'systems';
371 }
373 // Skip series which are not interesting for us
374 if(!in_array_strict($category,array('users','groups','department','systems','ogroups','fai'))){
375 $category = 'remaining';
376 }
378 foreach($data as $dateStr => $count){
379 $date = strtotime($dateStr);
381 if(!isset($gData['objectCountPerInterval'][$category][$date])){
382 $gData['objectCountPerInterval'][$category][$date]=0;
383 }
384 $gData['objectCountPerInterval'][$category][$date] += $count;
385 }
386 ksort($gData['objectCountPerInterval'][$category]);
387 }
388 // Move remaining to the end of the list
389 if(isset($gData['objectCountPerInterval']['remaining'])){
390 $data = $gData['objectCountPerInterval']['remaining'];
391 unset($gData['objectCountPerInterval']['remaining']);
392 $gData['objectCountPerInterval']['remaining'] = $data;
393 }
395 // Clean data from unusable categories like ('terminals workstations, ...')
396 foreach($gData as $serieName => $seriesData){
397 foreach($seriesData as $key => $data){
398 $list = preg_split("/, /", $key);
399 if(count(array_intersect($mapSystems, $list))){
400 unset($gData[$serieName][$key]);
401 $gData[$serieName]['systems'] = $data;
402 }
403 }
404 }
406 // Get info about used password hashes
407 $gData['usedPasswordHashes'] = array();
408 foreach($res['usedPasswordHashes'] as $category => $data){
410 // Add missing date stamps
411 foreach($gData['dates'] as $timestamp => $value){
412 $gData['usedPasswordHashes'][$category][$timestamp]=0;
413 }
415 foreach($data as $dateStr => $count){
416 $date = strtotime($dateStr);
417 $gData['usedPasswordHashes'][$category][$date]=$count;
418 }
419 ksort($gData['usedPasswordHashes'][$category]);
420 }
422 // Get action usage, to be able to render a pie chart about most done actions.
423 $gData['actionsPerPluginAction'] = $res['actionsPerPluginAction'];
424 return($gData);
425 }
428 function check()
429 {
430 $messages = plugin::check();
431 return($messages);
432 }
435 function save_object()
436 {
437 // Nothing to do for not registered accounts.
438 if(!$this->serverAccessible || !$this->instanceRegistered) return;
440 plugin::save_object();
441 if(isset($_POST['graph1DatePicker1'])) $this->graph1DatePicker1 = get_post('graph1DatePicker1');
442 if(isset($_POST['graph1DatePicker2'])) $this->graph1DatePicker2 = get_post('graph1DatePicker2');
444 if(isset($_POST['selectedGraphType'])) $this->selectedGraphType = get_post('selectedGraphType');
446 $this->staticChart1->save_object();
447 $this->staticChart2->save_object();
449 $curGraph = $this->graphs[$this->selectedGraphType];
450 $curGraph->save_object();
451 }
454 /*! \brief Reload the graph images.
455 */
456 function reloadGraphs()
457 {
458 new pChartInclude();
460 $gData = $this->statisticData;
461 if(!count($gData)){
462 return;
463 }
464 $curGraph = $this->graphs[$this->selectedGraphType];
465 $curGraph->setGraphData($gData);
466 $curGraph->render();
468 $this->staticChart1->setGraphData($gData);
469 $this->staticChart1->render();
470 $this->staticChart2->setGraphData($gData);
471 $this->staticChart2->render();
472 }
473 }
474 ?>