Simple class documentation - ServerStatus

local links: Methods :: Properties :: Source

Class: ServerStatus

Methods

public __construct()
constructor (it does nothing)
@return boolean (true)
private _getServerData( $sStatus, $sHostname = '') ... 1 of 2 param(s) required
parse response of original apachestatus and put it to an array
@param string $sStatus response from serverstatus request (html code)
@param string $sHostname host is used as key for the result array
@return array
private _filter_lt( $a, $b) ... all param(s) required
filter lt - returns bool $a lower than $b
@param type $a
@param type $b
@return boolean
private _filter_le( $a, $b) ... all param(s) required
filter le - returns bool $a lower equal $b
@param type $a
@param type $b
@return boolean
private _filter_eq( $a, $b) ... all param(s) required
filter eq - returns bool $a equal $b
@param type $a
@param type $b
@return boolean
private _filter_ne( $a, $b) ... all param(s) required
filter ne - returns bool $a not equal $b
@param type $a
@param type $b
@return boolean
private _filter_ge( $a, $b) ... all param(s) required
filter ge - returns bool $a greater or equal $b
@param type $a
@param type $b
@return boolean
private _filter_gt( $a, $b) ... all param(s) required
filter gt - returns bool $a greater than $b
@param type $a
@param type $b
@return boolean
private _filter_regex( $a, $b) ... all param(s) required
filter regex - returns bool regex $b matches $a
@param type $a
@param type $b
@return boolean
private _dataFilterCheckRow( $aSingleRow, $aFilter = false) ... 1 of 2 param(s) required
check a line in tabledata and apply the filter rules to a table
this function is a helper function for dataFilter()
@param type $aSingleRow
@param type $aFilter array rules to reduce visible data
array( add/ remove , columnname , operator , value to compare )
public dataFilter( $a, $aFilter) ... all param(s) required
filterfunction to extract, filter and sort status data
@param array $a
@param filter $aFilter
$aFilter ... Array
'sServer' string name of server to fetch the data from; default: all servers
'sType' string one of "status" | "requests"
'aRows' array keys to display as rows
'sSortkey' string sort a specific row
'sortorder' const SORT_ASC, SORT_DESC, SORT_REGULAR, SORT_NUMERIC, SORT_STRING
'aRules' array rules to reduce visible data
array( action , columnname , operator , value )
action: add/ add_or/ remove
column: key of array
operator: lt le eq ne ge gt regex
value: value to compare
'bGroup' boolean sort a specific row
'iLimit' integer max count of returned rows
@return array
public getResponseTime()
get response time to fetch all statuspages of all webservers
@see $this->getStatus()
@return integer
public addServer( $servername, $serveropts = Array) ... 1 of 2 param(s) required
add a webserver
The parameter is the servername and array containing server config
'webserver-01', array('status-url' => 'http://webserver-01:8888/server-status')

@param string $servername
@param array $serveropts
private full_curl_multi_exec( $mh, &$still_running) ... all param(s) required
helper function for multi_curl_exec
hint from kempo19b
http://php.net/manual/en/function.curl-multi-select.php

@param handle $mh multicurl master handle
@param boolean $still_running
@return type
public getStatus()
make simultaneous requests to all server status pages
parse them and and merge all data of all servers to a single array
@return array with keys "data" (results) and "error" (error messages)
top

Properties

private $a = false
private $aServer = false
private $_fResponsetime = false
privatestatic $curl_opts = Array
top

Source


<?php

/*
 * PIMPED APACHE-STATUS
 * Serverstatus class
 *
 * @package pimped_apache_status
 * @author Axel Hahn
 */

class ServerStatus {

    private 
$a = array();
    private 
$aServer = array();
    private 
$_fResponsetime false;
    private static 
$curl_opts = array(
        
CURLOPT_RETURNTRANSFER => true,
        
CURLOPT_TIMEOUT => 5,
        
CURLOPT_FAILONERROR => 1,
        
CURLOPT_SSL_VERIFYHOST => 0,
        
CURLOPT_SSL_VERIFYPEER => 0,
            
// CURLMOPT_MAXCONNECTS => 10
    
);

    
/**
     * constructor (it does nothing)
     * @return boolean (true)
     */
    
public function __construct() {
        return 
true;
    }

    
/**
     * parse response of original apachestatus and put it to an array
     * @param string $sStatus    response from serverstatus request (html code)
     * @param string $sHostname  host is used as key for the result array
     * @return array
     */
    
private function _getServerData($sStatus$sHostname '') {
        if (!
$sStatus)
            return 
false;
        if (!
$sHostname)
            return 
false;
        if (!
strpos($sStatus"Apache Server Status for"))
            return 
false;

        
$sStatusNobr str_replace("\n"""$sStatus);
        
$aReturn = array();

        
// regex to fetch server infos
        
$sRegexStatus '/\<dt\>(.*)\<\/dt\>/U'// status data are in dt tags
        
$sRegexStatus2 '/(.*)\:\ (.*)/U'// status data are in dt tags
        // regex to fetch the current requests
        // isolate the table with requests
        
$sRegexGetRequestsTable '#(<table border="0">.*</table>)#';
        
$sRegFields '/\<th\>(.*)\<\/th\>/Um'// to fetch column names in th
        
$sRegexRequests '/\<tr\>\<td\>\<b\>(.*\n)*.*\<\/td\>\<\/tr\>/U';
        
$sRegexRequests2 '/<td[\ nowrap]*\>(.*)\<\/td\>/mU';

        
// ----- get status infos
        
if (preg_match_all($sRegexStatus$sStatusNobr$aStatusinfos)) {
            if (
$aStatusinfos[1]) {
                foreach (
$aStatusinfos[1] as $sStatusInfo) {

                    
// devide infos from line [varianble]: [value]
                    
$aStatusinfos2 explode(" - "$sStatusInfo);
                    foreach (
$aStatusinfos2 as $sStatusinfo2) {
                        
$aStatusinfos3 explode(": "$sStatusinfo2);
                        
// found a key value pair
                        
if (count($aStatusinfos3) == 2) {
                            
$aReturn[$sHostname]['status'][$aStatusinfos3[0]] = $aStatusinfos3[1];
                        } else {
                            
$sLine strip_tags($aStatusinfos3[0]);
                            if (
strpos($sLine" CPU load") > 0) {
                                
$aReturn[$sHostname]['status']['CPU load'] = str_replace(" CPU load"""$sLine);
                            }
                            if (
strpos($sLine" requests/sec") > 0) {
                                
$aReturn[$sHostname]['status']['requests/sec'] = str_replace(" requests/sec"""$sLine);
                            }
                            if (
strpos($sLine"/second") > 0) {
                                
$aReturn[$sHostname]['status']['size/sec'] = str_replace("/second"""$sLine);
                            }
                            
// TODO: this line is left - what are u, s, cu, cs?
                            // CPU Usage: u1548.32 s194.7 cu.15 cs0 - 3.9% CPU load
                            // CPU Usage: u380.625 s1014.43 cu0 cs0 - 2.67% CPU load
                        
}
                        
// print_r($aStatusinfos3);
                    
}
                }
            }
        }

        
// print_r($aReturn); die();
        // echo "<hr>"; print_r($aStatusinfos); die();
        // ----- get list of requests:
        
$sRequestTable false;
        
$dummy preg_match_all($sRegexGetRequestsTable$sStatusNobr$aTmpTable);
        if (
$dummy) {
            
$sRequestTable $aTmpTable[0][0];
        }

        if (
$sRequestTable) {
            
$formatOk preg_match_all($sRegFields$sRequestTable$aFieldnames);
            
$aStatusfields $aFieldnames[1];

            
$formatOk preg_match_all($sRegexRequests$sRequestTable$matches);
            if (
$formatOk) {
                foreach (
$matches[0] as $sRequestData) {
                    
$formatOk preg_match_all($sRegexRequests2str_replace("\n"""$sRequestData), $matches2);

                    
$aTmp = array();
                    
$aTmp['Webserver'] = $sHostname;
                    foreach (
$matches2[0] as $iKey => $aEntry) {
                        
$aTmp[strip_tags($aStatusfields[$iKey])] = strip_tags($aEntry);
                    }

                    
// START extract Method from column "Request"
                    
if (array_key_exists("Request"$aTmp)) {
                        
$sR preg_replace('/([A-Z]*)\ .*/'"$1"$aTmp['Request']);
                    }
                    
$aTmp['Method'] = $sR;
                    
// END extract Method from column "Request"

                    
$aReturn[$sHostname]['requests'][] = $aTmp;
                }
            }
        }
        
$aReturn[$sHostname]['orig'] = $sStatus;

        return 
$aReturn;
    }

    
// --- helpers for dataFilterCheckRow(): compare functions

    /**
     * filter lt - returns bool $a lower than $b
     * @param type $a
     * @param type $b
     * @return boolean
     */
    
private function _filter_lt($a$b) {
        return 
$a $b;
    }

    
/**
     * filter le - returns bool $a lower equal $b
     * @param type $a
     * @param type $b
     * @return boolean
     */
    
private function _filter_le($a$b) {
        return 
$a <= $b;
    }

    
/**
     * filter eq - returns bool $a equal $b
     * @param type $a
     * @param type $b
     * @return boolean
     */
    
private function _filter_eq($a$b) {
        return 
$a == $b;
    }

    
/**
     * filter ne - returns bool $a not equal $b
     * @param type $a
     * @param type $b
     * @return boolean
     */
    
private function _filter_ne($a$b) {
        return 
$a != $b;
    }

    
/**
     * filter ge - returns bool $a greater or equal $b
     * @param type $a
     * @param type $b
     * @return boolean
     */
    
private function _filter_ge($a$b) {
        return 
$a >= $b;
    }

    
/**
     * filter gt - returns bool $a greater than $b
     * @param type $a
     * @param type $b
     * @return boolean
     */
    
private function _filter_gt($a$b) {
        return 
$a $b;
    }

    
/**
     * filter regex - returns bool regex $b matches $a
     * @param type $a
     * @param type $b
     * @return boolean
     */
    
private function _filter_regex($a$b) {
        return 
preg_match($b$a);
    }

    
/**
     * check a line in tabledata and apply the filter rules to a table
     * this function is a helper function for dataFilter()
     * @param type $aSingleRow
     * @param type $aFilter array    rules to reduce visible data
     *     array( add/ remove , columnname , operator , value to compare )
     */
    
private function _dataFilterCheckRow($aSingleRow$aFilter false) {
        if (!
$aFilter) {
            return 
$aSingleRow;
        }
        
$bAdd false;
        
$bRemove false;
        foreach (
$aSingleRow as $sDataKey => $value) {

            foreach (
$aFilter as $aFilterRule) {
                list(
$cmd$col$op$val) = $aFilterRule;
                if (
$cmd == "add") {
                    if (
$sDataKey == $col) {
                        
// echo "rule ADD filter_$op, $value, $val<br>";
                        
$bAdd $bAdd || $this->{'_filter_' $op}($value$val);
                    }
                }
                if (
$cmd == "add_and") {
                    if (
$sDataKey == $col) {
                        
// echo "rule ADD filter_$op, $value, $val<br>";
                        
$bAdd $bAdd && $this->{'_filter_' $op}($value$val);
                    }
                }
                if (
$cmd == "remove") {
                    if (
$sDataKey == $col) {
                        
// echo "rule REMOVE filter_$op, $value, $val<br>";
                        
$bRemove $bRemove || $this->{'_filter_' $op}($value$val);
                    }
                }
            }
        }

        
// debugging stuff
        /*
          echo "DATA: " . print_r($aSingleRow, true);
          echo "FILTER: " . print_r($aFilter, true);
          echo "add: $bAdd - remove: $bRemove - "; if ($bRemove || !$bAdd) echo " SKIP"; else echo " OK "; echo "<hr>";
         */
        
if ($bRemove || !$bAdd) {
            return 
false;
        }
        return 
$aSingleRow;
    }

    
/**
     * filterfunction to extract, filter and sort status data
     * @param array  $a
     * @param filter $aFilter 
     *      $aFilter ... Array
     *         'sServer'      string   name of server to fetch the data from; default: all servers
     *         'sType'        string   one of "status" | "requests"
     *         'aRows'        array    keys to display as rows 
     *         'sSortkey'     string   sort a specific row 
     *         'sortorder'    const    SORT_ASC, SORT_DESC, SORT_REGULAR, SORT_NUMERIC, SORT_STRING
     *         'aRules'       array    rules to reduce visible data
     *              array( action , columnname , operator , value )
     *                  action:   add/ add_or/ remove
     *                  column:   key of array
     *                  operator: lt le eq ne ge gt regex
     *                  value:    value to compare
     *         'bGroup'       boolean  sort a specific row 
     *         'iLimit'       integer  max count of returned rows
     * @return array
     */
    
function dataFilter($a false$aFilter) {

        global 
$aLangTxt;
        
$aReturn = array();
        if (!
$a) {
            
$a $this->a;
        }

        
// ------------------------------------------------------------
        // check parameters
        // ------------------------------------------------------------


        
if (!array_key_exists('sType'$aFilter)) {
            die(
"ERROR: " __FUNCTION__ " requires sType.");
        }
        if (!
array_key_exists('aRows'$aFilter)) {
            die(
"ERROR: " __FUNCTION__ " requires aRows.");
        }

        if (
$aFilter['sType'] != "status" && $aFilter['sType'] != "requests") {
            die(
"ERROR: function " __FUNCTION__ " does not support value in sType=>" $aFilter['sType'] . ".");
        }
        if (
array_key_exists('sortorder'$aFilter)) {
            if (
                    
$aFilter['sortorder'] != SORT_ASC && $aFilter['sortorder'] != SORT_DESC && $aFilter['sortorder'] != SORT_DESC && $aFilter['sortorder'] != SORT_REGULAR && $aFilter['sortorder'] != SORT_NUMERIC && $aFilter['sortorder'] != SORT_STRING
            
)
                die(
"ERROR: function " __FUNCTION__ " does not support value in sortorder=>" $aFilter['sortorder'] . ".");
        }

        
// ------------------------------------------------------------
        // flatten data
        // ------------------------------------------------------------
        
foreach ($a as $sHost => $aData) {
            if (
                    (
array_key_exists('sServer'$aFilter) && $aFilter['sServer'] == $sHost) ||
                    (!
array_key_exists('sServer'$aFilter))
            ) {
                
$aSingleRow = array();
                if (
$aFilter['sType'] == "status") {
                    
$aSingleRow['Webserver'] = $sHost;
                    foreach (
$aFilter['aRows'] as $key) {
                        
$aSingleRow[$key] = array_key_exists($key$aData[$aFilter['sType']]) ? $aData[$aFilter['sType']][$key] : false;
                    }
                    
$aSingleRow $this->_dataFilterCheckRow($aSingleRowarray_key_exists('aRules'$aFilter) ? $aFilter['aRules'] : false);
                    if (
$aSingleRow) {
                        
$aReturn[] = $aSingleRow;
                    }
                }
                if (
$aFilter['sType'] == "requests" && array_key_exists("requests"$aData)
                ) {
                    foreach (
$aData[$aFilter['sType']] as $aRequest) {
                        
$aSingleRow['Webserver'] = $sHost;
                        foreach (
$aFilter['aRows'] as $key) {
                            
$aSingleRow[$key] = array_key_exists($key$aRequest) ? $aRequest[$key] : '';
                        }
                        
$aSingleRow $this->_dataFilterCheckRow($aSingleRowarray_key_exists('aRules'$aFilter) ? $aFilter['aRules'] : false);
                        if (
$aSingleRow) {
                            
$aReturn[] = $aSingleRow;
                        }
                    }
                }
            }
        }


        
// ------------------------------------------------------------
        // sort array by a given key
        // http://php.net/manual/en/function.array-multisort.php
        // ------------------------------------------------------------
        
$aSortCol = array();
        if (
array_key_exists('sSortkey'$aFilter)) {
            foreach (
$aReturn as $key => $row) {
                
$aSortCol[$key] = $row[$aFilter['sSortkey']];
            }
            
$sortorder SORT_ASC;
            if (
array_key_exists('sortorder'$aFilter)) {
                
$sortorder $aFilter['sortorder'];
            }
            if (
$aSortCol && count($aSortCol)) {
                
array_multisort($aSortCol$sortorder$aReturn);
            }
        }

        
// ------------------------------------------------------------
        // group a column
        // ------------------------------------------------------------
        
if (array_key_exists('bGroup'$aFilter)) {
            
$aGroup = array();
            foreach (
$aReturn as $key => $row) {
                
// $aGroup[$row[$aFilter['sSortkey']]]++;
                
if (array_key_exists($row[$aFilter['sSortkey']], $aGroup)) {
                    
$aGroup[$row[$aFilter['sSortkey']]] ++;
                } else {
                    
$aGroup[$row[$aFilter['sSortkey']]] = 1;
                }
            }
            
arsort($aGroup);


            
// create a new return array: count and grouped column
            // $aReturn=array("count"=>true, $aFilter['sSortkey']=>true);
            
$aReturn = array();
            foreach (
$aGroup as $key => $value) {
                
$aReturn[] = array(
                    
$aLangTxt['thCount'] => $value,
                    
$aFilter['sSortkey'] => $key,
                );
            }
        }

        
// ------------------------------------------------------------
        // limit
        // ------------------------------------------------------------
        
if (array_key_exists('iLimit'$aFilter)) {
            while (
count($aReturn) > $aFilter['iLimit']) {
                
array_pop($aReturn);
            }
        }

        return 
$aReturn;
    }

    
// ----------------------------------------------------------------------
    // GETTER
    // ----------------------------------------------------------------------
    /**
     * get response time to fetch all statuspages of all webservers
     * @see $this->getStatus()
     * @return integer
     */
    
public function getResponseTime() {
        return 
$this->_fResponsetime;
    }

    
// ----------------------------------------------------------------------
    // SETTER
    // ----------------------------------------------------------------------

    /**
     * add a webserver
     * The parameter is the servername and array containing server config
     *     'webserver-01', array('status-url' => 'http://webserver-01:8888/server-status')
     * 
     * @param string $servername
     * @param array $serveropts
     */
    
public function addServer($servername$serveropts = array()) {
        if (
$serveropts === null) {
            
$serveropts = array();
        }
        if (!
array_key_exists('status-url'$serveropts)) {
            
$serveropts['status-url'] = "http://$servername/server-status";
        }
        
$this->aServer[$servername] = $serveropts;
    }

    
/**
     * helper function for multi_curl_exec
     * hint from kempo19b
     * http://php.net/manual/en/function.curl-multi-select.php
     * 
     * @param handle  $mh             multicurl master handle
     * @param boolean $still_running  
     * @return type
     */
    
private function full_curl_multi_exec($mh, &$still_running) {
        do {
            
$rv curl_multi_exec($mh$still_running);
        } while (
$rv == CURLM_CALL_MULTI_PERFORM);
        return 
$rv;
    }

    
// ----------------------------------------------------------------------
    // ACTIONS
    // ----------------------------------------------------------------------
    /**
     * make simultaneous requests to all server status pages 
     * parse them and and merge all data of all servers to a single array
     * @return array with keys "data" (results) and "error" (error messages)
     */
    
public function getStatus() {

        
$this->= array();
        
$aErrors = array();
        
$running false;

        
$this->_fResponsetime false;
        
$iStart microtime(true);

        
// prepare curl object
        
$master curl_multi_init();

        
// requires php>=5.5:
        
if (function_exists('curl_multi_setopt')) {
            
// force parallel requests
            
curl_multi_setopt($masterCURLMOPT_PIPELINING0);
            
// curl_multi_setopt($master, CURLMOPT_MAXCONNECTS, 50);
        
}

        
$curl_arr = array();
        
$i 0;
        foreach (
$this->aServer as $sServer => $aData) {
            
$sUrl $aData['status-url'];
            
$curl_arr[$i] = curl_init($sUrl);
            
curl_setopt_array($curl_arr[$i], self::$curl_opts);
            if (
array_key_exists('userpwd'$aData)) {
                
curl_setopt($curl_arr[$i], CURLOPT_HTTPAUTHCURLAUTH_BASIC);
                
curl_setopt($curl_arr[$i], CURLOPT_USERPWD$aData['userpwd']);
            }
            
curl_multi_add_handle($master$curl_arr[$i]);
            
$i++;
        }

        
// make all requests
        
self::full_curl_multi_exec($master$running);
        do {
            
curl_multi_select($master);
            
self::full_curl_multi_exec($master$running);
            while (
$info curl_multi_info_read($master)) {
                
            }
        } while (
$running);

        
// get results
        
$i 0;
        foreach (
$this->aServer as $sServer => $aData) {
            
$sUrl $aData['status-url'];

            
$s curl_multi_getcontent($curl_arr[$i]);
            if (!
$s) {
                if (
curl_error($curl_arr[$i])) {
                    
$aErrors[] = 'failed to fetch ' $sUrl ' - ' curl_error($curl_arr[$i]) . ' - Maybe you need to check your config in config/config_user.php.';
                } else {
                    
$aErrors[] = 'failed to fetch ' $sUrl ' - Maybe you need to check your config in config/config_user.php.';
                }
            } else {
                
$aServerdata $this->_getServerData($s$sServer);
                if (
$aServerdata && is_array($aServerdata)) {
                    
// echo "<pre>"; print_r($aServerdata[$sServer]);
                    
if (array_key_exists("requests"$aServerdata[$sServer])) {
                        
$this->array_merge($this->a$aServerdata);
                    } else {
                        
$this->array_merge($this->a$aServerdata);
                        
$aErrors[] = 'Url ' $sUrl ' was found and is a server-status page - but you need to enable "Extended Status On".';
                    }
                } else {
                    
$aErrors[] = 'Url ' $sUrl ' was found but this is not a server-status page.';
                }
            }
            
curl_multi_remove_handle($master$curl_arr[$i]);
            
$i++;
        }
        
curl_multi_close($master);
        
$this->_fResponsetime microtime(true) - $iStart;
        return array(
            
'data' => $this->a,
            
'errors' => $aErrors,
            
'meta' => array(
                
'servers' => $i,
                
'responsetime' => $this->_fResponsetime,
            )
        );
    }

}

?>
top