source: lliurex-analytics-server/trunk/fuentes/lliurex-analytics-server/usr/lib/analytics-server/analytics/db.php @ 6850

Last change on this file since 6850 was 6850, checked in by mabarracus, 21 months ago

Add partial whitelist implementation

File size: 20.7 KB
Line 
1<?php
2class Cache{
3    private $cache_file;
4    private $cache_dir;
5    private $data_file;
6    private $data;
7    private $cache_timeout=60*60;
8
9    function Cache(){
10        $this->cache_dir = sys_get_temp_dir();
11        $this->cache_file = "cache_analytics.db.tmp";
12        $this->datafile=$this->cache_dir.'/'.$this->cache_file;
13        $this->data = array();
14    }
15    function readFile(){
16        if (is_file($this->datafile) and is_writable($this->datafile)){
17            $this->filecontents = file_get_contents($this->datafile);
18        }else{
19            // do nothing or throw error
20            return false;
21        }
22        return true;
23    }
24    function newFile($data=array()){
25        // create new file
26        $obj=$data;
27        $json='';
28        try{
29            $json=json_encode($obj);
30        }catch(Exception $e){
31            return false;
32        }
33        file_put_contents($this->datafile,$json);
34    }
35    function parseJson(){
36        try{
37            $json=json_decode($this->filecontents,true);
38            $this->data=$json;
39        }catch(Exception $e){
40            return false;
41        }
42        return true;
43    }
44    function process(){
45        $continue = false;
46        if ($this->readFile()){
47            $continue = true;
48        }else{
49            $this->newFile();
50            if ($this->readFile()){
51                $continue=true;
52            }
53        }
54        if (! $continue){
55            return false;
56        }
57        if (! $this->parseJson()){
58            return false;
59        }
60        return true;
61    }
62    function get($key){
63        if (! $this->process()){
64            return false;
65        }
66        try{
67            $data=$this->data;
68            if ($data == null or ! is_array($data) or ! array_key_exists($key,$data))
69                return false;
70            $data=$data[$key];
71            if (time() - (int)$data['timestamp'] > $this->cache_timeout){
72                return false; //'invalid';
73            }else{
74                return $data['value'];
75            }
76        }catch(Exception $e){
77            return false;
78        }
79    }
80    function store($obj,$key){
81        if (! $this->process()){
82            return false;
83        }
84        try{
85            $this->data[$key]=['timestamp'=>time(),'value'=>$obj];
86            return $this->newFile($this->data);
87        }catch(Exception $e){
88            return false;
89        }
90    }
91}
92
93class DB{
94
95     private $dbhost;
96     private $dbname;
97     private $dbuser;
98     private $dbpass;
99     private $ka_file='/var/run/analyticsd.keepalive';
100     private $alias;
101     public $dbconn;
102
103     function DB(){
104         require_once('config.php');
105         global $dbhost,$dbname,$dbpass,$dbuser,$distros;
106
107         $this->dbhost=$dbhost;
108         $this->dbname=$dbname;
109         $this->dbpass=$dbpass;
110         $this->dbuser=$dbuser;
111         $this->alias=array();
112         $this->info_distro=json_decode($distros,true);
113         if ($this->info_distro == NULL){
114             die('Error: Wrong json in Config.php');
115         }
116         $this->init_dates();
117         $this->times=0;
118     }
119     function init_dates(){
120         $this->dates=array();
121         $this->dates['today']=date("Y-m-d");
122         $this->dates['first_current']=date("Y-m-").'01';
123         $this->dates['last_old']=date("Y-m-d",strtotime($this->dates['first_current']." -1 days"));
124         $this->dates['first_old']=date("Y-m-",strtotime($this->dates['today']. "-1 months")).'01';
125         $this->dates['last_very_old']=date("Y-m-d",strtotime($this->dates['first_old']." -1 days"));
126         $this->dates['first_very_old']=date("Y-m-",strtotime($this->dates['first_old']." -1 days")).'01';
127         $this->dates['date_current']="(date between '".$this->dates['first_current']."' and '".$this->dates['today']."')";
128         $this->dates['date_old']="(date between '".$this->dates['first_old']."' and '".$this->dates['last_old']."')";
129         $this->dates['date_very_old']="(date between '".$this->dates['first_very_old']."' and '".$this->dates['last_very_old']."')";
130         $this->dates['date_range_last_three_months']="(date between '".$this->dates['first_very_old']."' and '".$this->dates['today']."')";
131     }
132     function connect(){
133        $this->dbconn=new mysqli($this->dbhost, $this->dbuser , $this->dbpass, $this->dbname);
134        if ($this->dbconn->connect_error) {
135            die('Connect Error:'. $this->dbconn->connect_error);
136        }
137     }
138     function disconnect(){
139         $this->dbconn->close();
140     }
141     function init_trans(){
142         $this->dbconn->autocommit(FALSE);
143         $this->dbconn->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);
144     }
145
146    function send_data($user,$version,$sabor,$apps,$specs,$date=''){
147        $spec_sql_names = '';
148        $spec_sql_values = '';
149        if ($specs != false){
150            try{
151                $arch = $specs['arch'];
152                $mem = $specs['mem'];
153                if (is_numeric($mem)){
154                    $mem=(int)$mem;
155                }
156                $vga = substr($specs['vga'],0,80);
157                $cpu = substr($specs['cpu']['model'],0,80);
158                $ncpu = $specs['cpu']['ncpus'];
159                if (is_numeric($ncpu)){
160                    $ncpu=(int)$ncpu;
161                }
162                $spec_sql_names = ',arch,mem,vga,cpu,ncpu';
163                $spec_sql_values = ",'$arch',$mem,'$vga','$cpu',$ncpu";
164            }catch(Exception $e){
165                $spec_sql_names = '';
166                $spec_sql_values = '';
167            }
168        }
169        if ($date == ''){
170            $sql="INSERT INTO tmp_clients(user,version,sabor,status $spec_sql_names) values ('$user','$version','$sabor',0 $spec_sql_values)";
171        }else{
172            $sql="INSERT INTO tmp_clients(user,version,sabor,status,date $spec_sql_names) values ('$user','$version','$sabor',0,'$date' $spec_sql_values)";
173        }
174        $retry=1;
175        $done=false;
176        $cli_id=false;
177        while (! $done and $retry < 4){
178            $res=$this->dbconn->query($sql);
179            if ($res){
180                $cli_id=$this->dbconn->insert_id; 
181                $done=true;
182            }else{
183                $retry+=1;
184                sleep($retry);
185            }
186        }
187        if ($retry == 4 or $cli_id == false)
188            throw new Exception('Error sending client data: '.$this->dbconn->error);
189        $err_apps=false;
190        $err_exception=false;
191        if (count($apps) != 0){
192            if ($date == ''){
193                $sql="insert into tmp_packages(client,app,value) values";
194            }else{
195                $sql="insert into tmp_packages(client,app,value,date) values";
196            }
197            $values=array();
198            // Prevent DoS attack
199            $i = 1000;
200            foreach ($apps as $app => $value){
201                // Max 1000 apps
202                if ( $i > 0 ){
203                    $i = $i - 1;
204                }else{
205                    throw new Exception('*** DoS detected, aborting more processing on this request ***');
206                } 
207
208                if (trim($app) == '' or trim($value) == ''){
209                    $err_apps=true;
210                    $err_exception=new Exception('Wrong application values');
211                    continue;
212                }
213                if ($date == ''){
214                    $values[]="($cli_id,'$app',$value)";
215                }else{
216                    $values[]="($cli_id,'$app',$value,'$date')";
217                }
218            }
219            if (count($values) > 0){
220                $sql.=implode(',',$values);
221                $done=false;
222                $retry=1;
223                while (! $done and $retry < 4){
224                    $res=$this->dbconn->query($sql);
225                    if ($res){
226                        $done=true;
227                    }else{
228                        $retry += 1;
229                        sleep($retry);
230                    }
231                }
232                if ($retry == 4 or ! $done){
233                    $err_apps=true;
234                    $err_exception=new Exception('Error sending client app data: '.$this->dbconn->error.' QUERY='.$sql);
235                }
236            }
237        }
238        //End operations
239        $sql = "Update tmp_clients set status=1 where id = $cli_id and status=0";
240        $retry=1;
241        $done=false;
242        while (! $done and $retry < 4){
243            $res=$this->dbconn->query($sql);
244            if ($res){
245                $done=true;
246            }else{
247                $retry+=1;
248                sleep($retry);
249            }
250        }
251        if ($retry == 4 or $cli_id == false){
252            throw new Exception('Error commiting client data: '.$this->dbconn->error);
253        }
254        if ($err_apps){
255            throw $err_exception;
256        }
257    }
258
259     private function load_alias(){
260         $sql="SELECT name,alias from Alias";
261         $result=$this->dbconn->query($sql);
262         while($row=$result->fetch_array(MYSQLI_ASSOC)){
263             $this->alias[$row['name']]=$row['alias'];
264         }
265     }
266        function get_system_data(){
267            $sql="select * from Config";
268            $result=$this->dbconn->query($sql);
269            if ($result){
270                $tmp=[];
271                foreach ($result->fetch_all(MYSQLI_ASSOC) as $value){
272                    $tmp[$value['name']]=$value['value'];
273                }
274                return json_encode($tmp);
275            }
276        }
277     function get_extended_data($app){
278         $today=date("Y-m-d");
279         $min_date=date("Y-m",strtotime($today." -1 year")).'-01';
280         $this->times=0;
281         // CLIENTS DISTRIBUTION PER RELEASE/FLAVOUR
282         $sql="select year(date) as year,month(date) as month,Releases_name,Flavours_name,count(*) as num_hosts from (select distinct Client_uid,date,Releases_name,Flavours_name from Client_Versions where date >= '$min_date' )t group by year,month,Releases_name,Flavours_name order by year Desc,month desc,Releases_name asc,num_hosts desc";
283
284            $cache = new Cache;
285            $cache_key='extended_1';
286            $data = $cache->get($cache_key);
287            $stime=microtime(true);
288            if ($data != false){
289                $clients_month=$data;
290            }else{
291             $result=$this->dbconn->query($sql);
292                if ($result){
293                 $this->times+=microtime(true)-$stime;
294                 $clients_month=[];
295                 $tmp=[];
296                 while($row=$result->fetch_array(MYSQLI_ASSOC)){
297                     $date=$row['year'].'_'.$row['month'];
298                     $tmp[$date][$row['Releases_name']][$row['Flavours_name']]=intval($row['num_hosts']);
299                 }
300                 foreach ($tmp as $date){
301                     $clients_month[]=$date;
302                 }
303                    $cache->store($clients_month,$cache_key);
304             }else{
305                 $clients_month=$this->dbconn->error;
306             }
307            }
308
309         
310         // CLIENT UPDATES
311         $sql="select year,month,count(*) as nclients,sum(cnt)-count(*) as nclients_updated from (select Client_uid,count(Client_uid) as cnt,year(date) as year,month(date) as month from Client_Versions where date >= '$min_date' GROUP by Client_uid,year,month having count(Client_uid) >= 1 ) t group by year,month order by year desc,month desc";
312         $sql="select year,month,count(*) as nclients,sum(cnt)-count(*) as nclients_updated,Releases_name as rel,Flavours_name as fla from (select Client_uid,count(Client_uid) as cnt,year(date) as year,month(date) as month,Releases_name,Flavours_name from Client_Versions where date >= '$min_date' GROUP by Client_uid,year,month,Releases_name,Flavours_name having count(Client_uid) >= 1 ) t group by year,month,Releases_name,Flavours_name order by year desc,month desc";
313
314            $cache_key='extended_2';
315            $data = $cache->get($cache_key);
316            $stime=microtime(true);
317            if ($data != false){
318                $num_updates_month=$data;
319            }else{
320                $result=$this->dbconn->query($sql);
321
322             if ($result){
323                 $this->times+=microtime(true)-$stime;
324                 $i=0;
325             $tmp=[];
326                 while($row=$result->fetch_array(MYSQLI_ASSOC)){
327                     $date=$row['year'].'_'.$row['month'];
328                 $tmp[$date][$row['rel']][$row['fla']]=intval($row['nclients_updated']);
329                 }
330             foreach($tmp as $date){
331                 $num_updates_month[]=$date;
332             }
333                    $cache->store($num_updates_month,$cache_key);
334             }else{
335                 $num_updates_month=$this->dbconn->error;
336             }
337            }
338
339
340         // CLIENT CHANGE RELEASE
341         $sql="select year,month,count(*) as upgrades_en_mes from (select year,month,Client_uid as cliente_upgradeado from (select Client_uid,year(date) as year,month(date) as month from Client_Versions where date >= '$min_date' GROUP by Client_uid,Releases_name,year,month)t group by month,year,Client_uid having(count(*))>1)t group by year,month order by year desc,month desc limit 12";
342
343
344            $cache_key='extended_3';
345            $data = $cache->get($cache_key);
346            $stime=microtime(true);
347            if ($data != false){
348                $change_releases=$data;
349            }else{
350                $result=$this->dbconn->query($sql);
351
352                if ($result){
353                 $this->times+=microtime(true)-$stime;
354                 $change_releases=[0,0,0,0,0,0,0,0,0,0,0,0];
355                 $i=0;
356                 while($row=$result->fetch_array(MYSQLI_ASSOC)){
357                     $change_releases[$i++]=intval($row['upgrades_en_mes']);
358                 }
359                    $cache->store($change_releases,$cache_key);
360             }else{
361                 $change_releases=$this->dbconn->error;
362             }
363            }
364
365         
366         // CLIENT CHANGE FLAVOUR
367         $sql="select year,month,count(*) as cambio_sabor_en_mes from (select year,month,Client_uid as cliente_upgradeado from (select Client_uid,year(date) as year,month(date) as month from Client_Versions where date >= '$min_date' GROUP by Client_uid,Flavours_name,year,month)t group by month,year,Client_uid having(count(*))>1)t group by year,month order by year desc,month desc limit 12";
368
369            $cache_key='extended_4';
370            $data = $cache->get($cache_key);
371            $stime=microtime(true);
372            if ($data != false){
373                $change_flavour=$data;
374            }else{
375                $result=$this->dbconn->query($sql);
376
377             if ($result){
378                 $this->times+=microtime(true)-$stime;
379                 $i=0;
380                 $change_flavour=[0,0,0,0,0,0,0,0,0,0,0,0];
381                 while($row=$result->fetch_array(MYSQLI_ASSOC)){
382                     $change_flavour[$i++]=intval($row['cambio_sabor_en_mes']);
383                 }
384                    $cache->store($change_flavour,$cache_key);
385             }else{
386                 $change_flavour=$this->dbconn->error;
387             }
388            }
389         
390         //sanitize input
391         if ($app != NULL){
392             $app=preg_grep('/^[a-zA-Z0-9\-_]+$/',array($app));
393             if ($app != NULL and isset($app[0])){
394                 $app=$this->dbconn->real_escape_string($app[0]);
395                 $stats['apps']=[];
396                 $stats['apps']['app']=$app;
397                 $sql="select year(date) as year,month(date) as month,string,Releases_name as rel,Flavours_name as fla,sum(count) as count from RecvPackages where string='$app' and date >= '$min_date' group by year,month,Releases_name,Flavours_name order by year desc,month desc,sum(count) desc";
398
399                    $cache_key="extended_app_$app";
400                    $data = $cache->get($cache_key);
401                    $stime=microtime(true);
402                    if ($data != false){
403                        $app_use=$data;
404                        $stats['apps']['app_use']=$app_use;
405                    }else{
406                        $result=$this->dbconn->query($sql);
407                        if ($result){
408                         $this->times+=microtime(true)-$stime;
409                         $tmp=[];
410                         while($row=$result->fetch_array(MYSQLI_ASSOC)){
411                             $date=$row['year'].'_'.$row['month'];
412                         $tmp[$date][$row['rel']][$row['fla']]=intval($row['count']);
413                         }
414                     foreach($tmp as $date){
415                         $app_use[]=$date;
416                     }
417                            $cache->store($app_use,$cache_key);
418                         $stats['apps']['app_use']=$app_use;
419                     }else{
420                         $app_use=$this->dbconn->error;
421                     }
422                    }
423
424             }
425         }
426         // FINALIZATION & WRITE STRUCTURE
427         if (isset($clients_month)){
428             $stats['clients']['clients_per_month']=$clients_month;
429         }
430         if (isset($num_updates_month)){
431             $stats['clients']['freq_updates_per_month']=$num_updates_month;
432         }
433         if (isset($change_releases)){
434             $stats['clients']['change_releases']=$change_releases;
435         }
436         if (isset($change_flavour)){
437             $stats['clients']['change_flavours']=$change_flavour;
438         }
439         $stats['debug_query_time']=strval(number_format($this->times,5));
440         if (file_exists($this->ka_file)){
441             $stats['debug_keep_alive']=date('Y-m-d H:i',file_get_contents($this->ka_file));
442         }
443         return json_encode($stats);
444     }
445
446     function get_historic_data(){
447         $this->load_alias();
448         $obj=[];
449         $this->times=0;
450         foreach ($this->info_distro['distros'] as $distro){
451             $dname=$distro['name'];
452             $dlike=$distro['like'];
453             $obj[$dname]=array();
454             foreach ($distro['sabor'] as $sabor){
455                 $sname=$sabor['name'];
456                 $slike=$sabor['like'];
457                 $obj[$dname][$sname][]=$this->get_chart($dlike,$slike,'current');
458                 $obj[$dname][$sname][]=$this->get_chart($dlike,$slike,'old');
459                 $obj[$dname][$sname][]=$this->get_chart($dlike,$slike,'very_old');
460             }
461         }
462         $obj['debug_query_time']=strval(number_format($this->times,3));
463         
464         if (file_exists($this->ka_file)){
465             $obj['debug_keep_alive']=date('Y-m-d H:i',file_get_contents($this->ka_file));
466         }
467         return json_encode($obj);
468     }
469    function get_chart($version='',$sabor='',$type='current'){
470        $cache_key="$version"."_"."$sabor"."_"."$type";
471        if ($version != ''){
472            $version = " and Releases_name = '$version' ";
473        }
474        if ($sabor != ''){
475            $sabor = " and Flavours_name = '$sabor' ";
476        }
477        $order=" order by count desc limit 10 ";
478        $group=" group by app ";
479
480        $where=$this->dates['date_'.$type]." $version $sabor ";
481        $where_clients=$this->dates['date_'.$type]." $version $sabor ";
482
483        $sql="SELECT string as app,sum(count) as count from RecvPackages where $where $group $order";
484        $sql_clients = "select count(distinct Client_uid) as count from Client_Versions where $where_clients $order";
485
486        $cache = new Cache;
487        $data = $cache->get($cache_key);
488        if ($data != false){
489            return $data;
490        }else{
491            $data=array($this->get_result_from_sql($sql),$this->get_clients_from_sql($sql_clients));
492            $cache->store($data,$cache_key);
493            return $data;
494        }
495     }
496     function get_clients_from_sql($sql){
497             $stime=microtime(true);
498         if ($result=$this->dbconn->query($sql)){
499                 $etime=microtime(true);
500                 $this->times+=($etime-$stime);
501             while($row=$result->fetch_array(MYSQLI_ASSOC)){
502                 if (isset($row['count'])){
503                     return array('nclients'=>$row['count']);
504                 }
505             }
506             return array('nclients'=>'not available');
507         }else{
508             return array('nclients'=>$this->dbconn->error);
509         }
510
511     }
512     function get_result_from_sql($sql){
513             $stime=microtime(true);
514         if ($result=$this->dbconn->query($sql)){
515                 $etime=microtime(true);
516                 $this->times+=($etime-$stime);
517             $obj2=[];
518             $nobj=0;
519             while($row=$result->fetch_array(MYSQLI_ASSOC)){
520                 if (array_key_exists($row['app'],$this->alias)){
521                     if (! empty($this->alias[$row['app']])){
522                         if ($nobj < 10)
523                             $obj2[$this->alias[$row['app']]]=$row['count'];
524                         $nobj++;
525                     }
526                 }else{
527                     if ($nobj < 10)
528                         $obj2[$row['app']]=$row['count'];
529                     $nobj++;
530                 }
531             }
532             return $obj2;
533         }else{
534             return $this->dbconn->error;
535         }
536     }
537
538} 
539
540?>
Note: See TracBrowser for help on using the repository browser.