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

Last change on this file since 6876 was 6876, checked in by mabarracus, 19 months ago

Whitelist implementation

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