From 40ecaa3c9faccadcfd3be156db04c1ecba6927db Mon Sep 17 00:00:00 2001
From: hahn <axel.hahn@iml.unibe.ch>
Date: Fri, 7 Jun 2019 12:00:43 +0200
Subject: [PATCH] update appmonitor client; add load and apache status

---
 .../classes/appmonitor-checks.class.php       | 164 ++++++++++++------
 .../classes/appmonitor-client.class.php       |  13 +-
 public_html/appmonitor/index.php              |  32 ++++
 .../plugins/checkApacheProcesses.php          | 146 ++++++++++++++++
 public_html/appmonitor/plugins/checkHello.php |  77 ++++++++
 .../appmonitor/plugins/checkLoadmeter.php     | 135 ++++++++++++++
 6 files changed, 511 insertions(+), 56 deletions(-)
 create mode 100644 public_html/appmonitor/plugins/checkApacheProcesses.php
 create mode 100644 public_html/appmonitor/plugins/checkHello.php
 create mode 100644 public_html/appmonitor/plugins/checkLoadmeter.php

diff --git a/public_html/appmonitor/classes/appmonitor-checks.class.php b/public_html/appmonitor/classes/appmonitor-checks.class.php
index 66d2375e..d0f7a764 100644
--- a/public_html/appmonitor/classes/appmonitor-checks.class.php
+++ b/public_html/appmonitor/classes/appmonitor-checks.class.php
@@ -46,8 +46,10 @@ if(!defined('RESULT_OK')){
  * 2018-08-23  0.50  axel.hahn@iml.unibe.ch  replace mysqli connect with mysqli real connect (to use a timeout)<br>
  * 2018-08-27  0.52  axel.hahn@iml.unibe.ch  add pdo connect (starting with mysql)<br>
  * 2018-11-05  0.58  axel.hahn@iml.unibe.ch  additional flag in http check to show content<br>
+ * 2019-05-31  0.87  axel.hahn@iml.unibe.ch  add timeout as param in connective checks (http, tcp, databases)<br>
+ * 2019-06-05  0.88  axel.hahn@iml.unibe.ch  add plugins<br>
  * --------------------------------------------------------------------------------<br>
- * @version 0.77
+ * @version 0.89
  * @author Axel Hahn
  * @link TODO
  * @license GPL
@@ -83,6 +85,12 @@ class appmonitorcheck {
      * @var type 
      */
     protected $_iTimeoutTcp=5;
+    
+    /**
+     * point to the plugin directory
+     * @var string
+     */
+    protected $_sPluginDir=__DIR__.'/../plugins';
 
     // ----------------------------------------------------------------------
     // CONSTRUCTOR
@@ -94,13 +102,13 @@ class appmonitorcheck {
     public function __construct() {
         
     }
-
+    
     // ----------------------------------------------------------------------
     // PRIVATE FUNCTIONS
     // ----------------------------------------------------------------------
 
     /**
-     * create basic array values for metadata
+     * internal: create basic array values for metadata
      * @return boolean
      */
     protected function _createDefaultMetadata() {
@@ -133,19 +141,43 @@ class appmonitorcheck {
     protected function _setOutput($s) {
         return $this->_aData["value"] = (string) $s;
     }
+    
+    /**
+     * put counter data to result set
+     * @param type $aParams
+     * @return boolean
+     */
+    protected function _setCounter($aParams){
+        if(count($aParams)){
+            foreach(array('type', 'count', 'visual') as $sMyKey){
+                if(isset($aParams[$sMyKey])){
+                    $this->_aData[$sMyKey]=$aParams[$sMyKey];
+                }
+            }
+        }
+        return true;
+    }
 
     /**
-     * set result and output
-     * @param type $iResult
-     * @param type $s
+     * set result and output 
+     * @param integer  $iResult   value; use a RESULT_XYZ constant
+     * @param string   $s         message text
+     * @param array    $aCounter  optional: counter with array keys type, count, visual
      * @return boolean
      */
-    protected function _setReturn($iResult, $s) {
+    protected function _setReturn($iResult, $s, $aCounter=array()) {
         $this->_setResult($iResult);
         $this->_setOutput($s);
+        $this->_setCounter($aCounter);
         return true;
     }
-
+    
+    /**
+     * check a given array if it contains wanted keys
+     * @param array  $aConfig   array to verify
+     * @param string $sKeyList  key or keys as comma seprated list 
+     * @return boolean
+     */
     protected function _checkArrayKeys($aConfig, $sKeyList) {
         foreach (explode(",", $sKeyList) as $sKey) {
             if (!array_key_exists($sKey, $aConfig)) {
@@ -188,37 +220,64 @@ class appmonitorcheck {
         $this->_aConfig = $aConfig;
         $this->_createDefaultMetadata();
 
-        $sCheck = "check" . $this->_aConfig["check"]["function"];
-        if (!method_exists($this, $sCheck)) {
-            header('HTTP/1.0 503 Service Unavailable');
-            die(__CLASS__ . " check not found: $sCheck <pre>" . print_r($aConfig, true));
-        }
+        $sCheck = "check" . preg_replace('/[^a-zA-Z0-9]/', '', $this->_aConfig["check"]["function"]);
         $aParams = array_key_exists("params", $this->_aConfig["check"]) ? $this->_aConfig["check"]["params"] : array();
-
-        // call the check ...
-        call_user_func(array($this, $sCheck), $aParams);
+        if (method_exists($this, $sCheck)) {
+            // load as internal function
+            call_user_func(array($this, $sCheck), $aParams);
+        } else {
+            // load as plugin
+            $sPluginFile=$this->_sPluginDir.'/'.$sCheck.'.php';
+            if (file_exists($sPluginFile)) {
+                
+                require_once($sPluginFile);
+                $oPlogin = new $sCheck;
+                $aResponse=$oPlogin->run($aParams); 
+                if(!is_array($aResponse)){
+                    header('HTTP/1.0 503 Service Unavailable');
+                    die(__CLASS__ . " plugin : $sCheck does not responses an array<pre>" . print_r($aResponse, true));
+                }
+                if(count($aResponse)<2){
+                    header('HTTP/1.0 503 Service Unavailable');
+                    die(__CLASS__ . " plugin : $sCheck does not responses the minimum of 2 array values<pre>" . print_r($aResponse, true));
+                }
+                if(!isset($aResponse[2])){
+                    $aResponse[2]=array();
+                }
+                $this->_setReturn($aResponse[0], $aResponse[1], $aResponse[2]);
+                
+            } else {
+                header('HTTP/1.0 503 Service Unavailable');
+                die(__CLASS__ . " check not found: $sCheck <pre>" . print_r($aConfig, true));
+            }
+        }
 
         $this->_aData['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms';
-        // echo "<pre>"; print_r($this->listChecks()); die();
         // ... and send response 
         return $this->respond();
     }
 
     /**
-     * list all available check functions. This is a helper class you cann call
-     * to get an overview overbuilt in functions. You get a flat array with
-     * all function names.
+     * list all available check functions. This is a helper class you can call
+     * to get an overview over built in functions and plugins. 
+     * You get a flat array with all function names.
      * @return array
      */
     public function listChecks() {
         $aReturn = array();
+        // return internal protected fuctions named "check[whatever]"
         $class = new ReflectionClass($this);
-        foreach ($class->getMethods(ReflectionMethod::IS_PRIVATE) as $oReflectionMethod) {
+        foreach ($class->getMethods(ReflectionMethod::IS_PROTECTED) as $oReflectionMethod) {
             if (strpos($oReflectionMethod->name, "check") === 0) {
-                $aReturn[] = (string) $oReflectionMethod->name;
+                $aReturn[(string) $oReflectionMethod->name]=1;
             }
         }
-        return $aReturn;
+        // return checks from plugins subdir
+        foreach(glob($this->_sPluginDir.'/check*.php') as $sPluginFile){
+            $aReturn[str_replace('.php', '', basename($sPluginFile))] = 1;
+        }
+        ksort($aReturn);
+        return array_keys($aReturn);
     }
 
     /**
@@ -230,7 +289,7 @@ class appmonitorcheck {
     }
 
     // ----------------------------------------------------------------------
-    // CHECK FUNCTIONS (private)
+    // CHECK FUNCTIONS (protected)
     // ----------------------------------------------------------------------
 
     /**
@@ -493,6 +552,7 @@ class appmonitorcheck {
      * @param array $aParams
      * array(
      *     url                 string   url to fetch
+     *     timeout             integer  optional timeout in sec; default: 5
      *     headeronly          boolean  optional flag to fetch http response herader only; default: false = returns header and body
      *     follow              boolean  optional flag to follow a location; default: false = do not follow
      *     status              integer  test for an expected http status code; if none is given then test fails on status 400 and greater
@@ -503,9 +563,8 @@ class appmonitorcheck {
      *     bodynotcontains     string   test for a string in the http response body; it returns OK if the text was not found
      *     bodyregex           string   test for a regex in the http response body; it returns OK if the regex matches; example: "headerregex"=>"/lowercasematch/i"
      * )
-     * @param integer $iTimeout  value in sec; default: 5sec
      */
-    protected function checkHttpContent($aParams, $iTimeout = 5) {
+    protected function checkHttpContent($aParams) {
         $this->_checkArrayKeys($aParams, "url");
         if (!function_exists("curl_init")) {
             header('HTTP/1.0 503 Service Unavailable');
@@ -519,7 +578,7 @@ class appmonitorcheck {
         curl_setopt($ch, CURLOPT_FOLLOWLOCATION, isset($aParams["follow"]) && $aParams["follow"]);
         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
-        curl_setopt($ch, CURLOPT_TIMEOUT, $iTimeout);
+        curl_setopt($ch, CURLOPT_TIMEOUT, (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp);
         $res = curl_exec($ch);
 
         if (!$res) {
@@ -560,7 +619,7 @@ class appmonitorcheck {
                     )
 
                 [primary_port] => 443
-                [local_ip] => 130.92.79.49
+                [local_ip] => 10.1.30.49
                 [local_port] => 63597
             )
          */
@@ -686,11 +745,12 @@ class appmonitorcheck {
      * check mysql connection to a database using mysqli realconnect
      * @param array $aParams
      * array(
-     *     "server" 
-     *     "user" 
-     *     "password" 
-     *     "db" 
-     *     "port"     <<< optional
+     *     server              string   database hostname / ip address
+     *     user                string   db user
+     *     password            string   password for db user
+     *     db                  string   schema / database name
+     *     port                integer  optional: port
+     *     timeout             integer  optional timeout in sec; default: 5
      * )
      */
     protected function checkMysqlConnect($aParams) {
@@ -700,7 +760,7 @@ class appmonitorcheck {
             $this->_setReturn(RESULT_ERROR, 'ERROR: mysqli_init failed.');
             return false;
         }
-        if (!$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, $this->_iTimeoutTcp)) {
+        if (!$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp)) {
             $this->_setReturn(RESULT_ERROR, 'ERROR: setting mysqli_init failed.');
             return false;
         }
@@ -724,9 +784,10 @@ class appmonitorcheck {
      * 
      * @param array $aParams
      * array(
-     *     "connect" 
-     *     "password" 
-     *     "user" 
+     *     connect             string   connect string
+     *     user                string   db user
+     *     password            string   password for db user
+     *     timeout             integer  optional timeout in sec; default: 5
      * )
      */
     protected function checkPdoConnect($aParams) {
@@ -742,7 +803,7 @@ class appmonitorcheck {
                     
                     // timeout
                     // Not all drivers support this option; mysqli does
-                    PDO::ATTR_TIMEOUT => $this->_iTimeoutTcp,                  
+                    PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp,                  
                     // mssql
                     // PDO::SQLSRV_ATTR_QUERY_TIMEOUT => $this->_iTimeoutTcp,  
                 )
@@ -760,8 +821,9 @@ class appmonitorcheck {
      * check if system is listening to a given port
      * @param array $aParams
      * array(
-     *     "port" 
-     *     "host"  (optional: 127.0.0.1 is default)
+     *     port                integer  port
+     *     host                string   optional hostname to connect; default: 127.0.0.1
+     *     timeout             integer  optional timeout in sec; default: 5
      * )
      * @return boolean
      */
@@ -784,7 +846,7 @@ class appmonitorcheck {
             SOL_SOCKET,  // socket level
             SO_SNDTIMEO, // timeout option
             array(
-              "sec"=>$this->_iTimeoutTcp, // timeout in seconds
+              "sec"=>(isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp, // timeout in seconds
               "usec"=>0
               )
             );
@@ -807,16 +869,15 @@ class appmonitorcheck {
      * 
      * @param array $aParams
      * array keys:
-     *     "result"   integer; RESULT_nn
-     *     "value"    description text
+     *     value               string   description text
+     *     result              integer  RESULT_nn
      * 
      *     brainstorming for a future release
      * 
      *     "counter"  optioal: array of counter values
-     *         - "label"  string: a label
-     *         - "value"  a number
-     *         - "unit"   string: unit, i.e. ms, MB/s
-     *         - "type"   one of counter | bars | bars-stacked | lines | ... ??
+     *         - label         string   a label
+     *         - value         float    a number
+     *         - type          string   one of simple | bar | line
      * 
      */
     protected function checkSimple($aParams) {
@@ -834,7 +895,8 @@ class appmonitorcheck {
      * check sqlite connection
      * @param array $aParams
      * array(
-     *     "db"  full path of sqlite file 
+     *     db                  string   full path of sqlite file 
+     *     timeout             integer  optional timeout in sec; default: 5
      * )
      * @return boolean
      */
@@ -847,7 +909,11 @@ class appmonitorcheck {
         try {
             // $db = new SQLite3($sqliteDB);
             // $db = new PDO("sqlite:".$sqliteDB);
-            $o = new PDO("sqlite:" . $aParams["db"]);
+            $o = new PDO("sqlite:" . $aParams["db"],
+                array(
+                    PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp,                  
+                )
+            );
             $this->_setReturn(RESULT_OK, "OK: Sqlite database " . $aParams["db"] . " was connected");
             return true;
         } catch (Exception $e) {
diff --git a/public_html/appmonitor/classes/appmonitor-client.class.php b/public_html/appmonitor/classes/appmonitor-client.class.php
index a73a52a3..a4dbe9d1 100644
--- a/public_html/appmonitor/classes/appmonitor-client.class.php
+++ b/public_html/appmonitor/classes/appmonitor-client.class.php
@@ -35,8 +35,9 @@
  * 2018-08-24  0.51  axel.hahn@iml.unibe.ch  method to show local status page<br>
  * 2018-08-27  0.52  axel.hahn@iml.unibe.ch  add pdo connect (starting with mysql)<br>
  * 2018-11-05  0.58  axel.hahn@iml.unibe.ch  additional flag in http check to show content<br>
+ * 2019-05-31  0.87  axel.hahn@iml.unibe.ch  add timeout as param in connective checks (http, tcp, databases)<br>
  * --------------------------------------------------------------------------------<br>
- * @version 0.85
+ * @version 0.88
  * @author Axel Hahn
  * @link TODO
  * @license GPL
@@ -50,7 +51,7 @@ class appmonitor {
      * value is in seconds
      * @var int
      */
-    protected $_sVersion = 'php-client-v0.85';
+    protected $_sVersion = 'php-client-v0.89';
 
     /**
      * config: default ttl for server before requesting the client check again
@@ -191,11 +192,9 @@ class appmonitor {
 
         require_once 'appmonitor-checks.class.php';
         $oCheck = new appmonitorcheck();
-        // print_r($aJob); die();
         $aCheck = $oCheck->makecheck($aJob);
         
-        // limit error code
-        // echo "DEBUG ".$aCheck["name"].": ".$aCheck["result"]."\n";
+        // limit result code
         $iMyResult=isset($aJob['worstresult']) 
                 ? min($aCheck["result"], $aJob['worstresult'])
                 : $aCheck["result"]
@@ -314,7 +313,7 @@ class appmonitor {
 
     /**
      * list all available check functions. This is a helper class you cann call
-     * to get an overview overbuilt in functions. You get a flat array with
+     * to get an overview over built in functions. You get a flat array with
      * all function names.
      * @return array
      */
@@ -329,7 +328,7 @@ class appmonitor {
     // ----------------------------------------------------------------------
 
     /**
-     * verify array values and abort with all found errors
+     * verify array values and in case of an error abort and show all found errors
      */
     protected function _checkData() {
         $aErrors = array();
diff --git a/public_html/appmonitor/index.php b/public_html/appmonitor/index.php
index bf51c134..070fe322 100644
--- a/public_html/appmonitor/index.php
+++ b/public_html/appmonitor/index.php
@@ -267,6 +267,38 @@ $oMonitor->addCheck(
     )
 );
 
+// ----------------------------------------------------------------------
+// system stuff
+// ----------------------------------------------------------------------
+$oMonitor->addCheck(
+    array(
+        "name" => "plugin Load",
+        "description" => "check current load",
+        "check" => array(
+            "function" => "Loadmeter",
+            "params" => array(
+                "warning" => 1.0,
+                "error" => 3,
+            ),
+        ),
+        "worstresult" => RESULT_OK
+    )
+);
+$oMonitor->addCheck(
+    array(
+        "name" => "plugin ApacheProcesses",
+        "description" => "check count running Apache processes",
+        "check" => array(
+            "function" => "ApacheProcesses",
+            "params" => array(
+                "warning" => 50,
+                "error" => 75,
+            ),
+        ),
+        "worstresult" => RESULT_OK
+    )
+);
+
 
 // Gesamt-Ergebnis - ohne Param=aut. max. Wert nehmen
 $oMonitor->setResult();
diff --git a/public_html/appmonitor/plugins/checkApacheProcesses.php b/public_html/appmonitor/plugins/checkApacheProcesses.php
new file mode 100644
index 00000000..e935ae36
--- /dev/null
+++ b/public_html/appmonitor/plugins/checkApacheProcesses.php
@@ -0,0 +1,146 @@
+<?php
+/**
+ * ____________________________________________________________________________
+ * 
+ *  _____ _____ __                   _____         _ _           
+ * |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
+ * |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
+ * |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
+ *                          |_| |_|                              
+ *                           _ _         _                                            
+ *                       ___| |_|___ ___| |_                                          
+ *                      |  _| | | -_|   |  _|                                         
+ *                      |___|_|_|___|_|_|_|   
+ *                                                               
+ * ____________________________________________________________________________
+ * 
+ * SHOW LOAD AS LINE
+ * 
+ * A plugin always is loaded in clases/appmonitor-checks.class.php
+ * Have look there for the used protected classes
+ * ____________________________________________________________________________
+ * 
+ * PARAMS:
+ *   url     {string}   optional: override https server-status page; default is http://localhost/server-status
+ *   warning {integer}  optional: limit to switch to warning (in percent); default: 50
+ *   error   {integer}  optional: limit to switch to error (in percent); default: 75
+ * 
+ * USAGE:
+ * Example with overriding all existing params
+ * 
+ * $oMonitor->addCheck(
+ *    array(
+ *         "name" => "plugin ApacheProcesses",
+ *         "description" => "check count running Apache processes",
+ *         "check" => array(
+ *             "function" => "ApacheProcesses",
+ *             "params" => array(
+ *                 "url" => "https://localhost/status",
+ *                 "warning" => 30,
+ *                 "error" => 50,
+ *             ),
+ *         ),
+ *         "worstresult" => RESULT_OK
+ *     )
+ * );
+ * ____________________________________________________________________________
+ * 
+ * 2019-06-07  <axel.hahn@iml.unibe.ch>
+ * 
+ */
+class checkApacheProcesses extends appmonitorcheck{
+    
+    protected $_sServerStatusUrl = 'http://localhost/server-status';
+    protected $_iWarn = 50;
+    protected $_iError = 75;
+    
+    /**
+     * fetch http server status and return slots, active and waiting processes
+     * as array i.e. [total] => 256 \n    [free] => 247\n    [waiting] => 7\n    [active] => 2
+     * @return boolean
+     */
+    protected function _getApacheProcesses() {
+
+        $sBody = file_get_contents($this->_sServerStatusUrl);
+        if(!$sBody){
+            return false;
+        }
+        $sRegexScoreboard = '/<pre>(.*)\<\/pre\>/U';
+        $aScore=array();
+        $sStatusNobr = str_replace("\n", "", $sBody);
+
+        if (preg_match_all($sRegexScoreboard, $sStatusNobr, $aTmpTable)) {
+            $sScoreString=$aTmpTable[1][0];
+            // $aScore['scoreboard']=$sScoreString;
+            $aScore['total']=strlen($sScoreString);
+            $aScore['free']=substr_count($sScoreString, '.');
+            $aScore['waiting']=substr_count($sScoreString, '_');
+            $aScore['active']=$aScore['total']-$aScore['free']-$aScore['waiting'];
+        }
+        return $aScore;
+    }
+
+
+    /**
+     * 
+     * @param array   $aParams
+     * @return array
+     */
+    public function run($aParams){
+        
+        // --- (1) verify if array key(s) exist:
+        // $this->_checkArrayKeys($aParams, "...");
+        if(isset($aParams['url']) && $aParams['url']){
+            $this->_sServerStatusUrl=$aParams['url'];
+        }
+        if(isset($aParams['warning']) && (int)$aParams['warning']){
+            $this->_iWarn=(int)$aParams['warning'];
+        }
+        if(isset($aParams['error']) && (int)$aParams['error']){
+            $this->_iError=(int)$aParams['error'];
+        }
+    
+
+        // --- (2) do something magic
+        $aProcesses=$this->_getApacheProcesses();
+        $iActive=$aProcesses ? $aProcesses['active'] : false;
+        
+        // set result code
+        if($iActive===false){
+            $iResult=RESULT_UNKNOWN;
+        } else {
+            $iResult=RESULT_OK;
+            if($iActive>$this->_iWarn){
+                $iResult=RESULT_WARNING;
+            }
+            if($iActive>$this->_iError){
+                $iResult=RESULT_ERROR;
+            }
+        }
+
+
+        // --- (3) response
+        // see method appmonitorcheck->_setReturn()
+        // 
+        // {integer} you should use a RESULT_XYZ constant:
+        //              RESULT_OK|RESULT_UNKNOWN|RESULT_WARNING|RESULT_ERROR
+        // {string}  output text 
+        // {array}   optional: counter data
+        //              type   => {string} "counter"
+        //              count  => {float}  value
+        //              visual => {string} one of bar|line|simple (+params)
+        //           
+        return array(
+            $iResult, 
+            ($iActive===false ? 'Apache httpd server status is not available' : 'apache processes: '.print_r($aProcesses, 1)),
+            ($iActive===false 
+                ? array()
+                : array(
+                    'type'=>'counter',
+                    'count'=>$iActive,
+                    'type'=>'line',
+                )
+            )
+        );
+    }
+}
diff --git a/public_html/appmonitor/plugins/checkHello.php b/public_html/appmonitor/plugins/checkHello.php
new file mode 100644
index 00000000..8e1d9349
--- /dev/null
+++ b/public_html/appmonitor/plugins/checkHello.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * ____________________________________________________________________________
+ * 
+ *  _____ _____ __                   _____         _ _           
+ * |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
+ * |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
+ * |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
+ *                          |_| |_|                              
+ *                           _ _         _                                            
+ *                       ___| |_|___ ___| |_                                          
+ *                      |  _| | | -_|   |  _|                                         
+ *                      |___|_|_|___|_|_|_|   
+ *                                                               
+ * ____________________________________________________________________________
+ * 
+ * EXAMPLE CUSTOM CHECK THAT SENDS A HELLO
+ * 
+ * A plugin always is loaded in clases/appmonitor-checks.class.php
+ * Have look there for the used protected classes
+ * ____________________________________________________________________________
+ * 
+ * PARAMS:
+ *   message {string}  a custom message to display
+ * 
+ * USAGE:
+ * 
+ * $oMonitor->addCheck(
+ *     array(
+ *         "name" => "hello plugin",
+ *         "description" => "test a plugin ... plugins/checkHello.php",
+ *         "check" => array(
+ *             "function" => "Hello",
+ *             "params" => array(
+ *                 "message" => "Here I am",
+ *             ),
+ *         ),
+ *     )
+ * );
+ * ____________________________________________________________________________
+ * 
+ * 2019-06-05  <axel.hahn@iml.unibe.ch>
+ * 
+ */
+class checkHello extends appmonitorcheck{
+    
+    /**
+     * 
+     * @param array   $aParams
+     * @return array
+     */
+    public function run($aParams){
+        
+        // --- (1) verify if array key(s) exist:
+        $this->_checkArrayKeys($aParams, "message");
+
+
+        // --- (2) do something magic
+
+
+        // --- (3) response
+        // see method appmonitorcheck->_setReturn()
+        // 
+        // {integer} you should use a RESULT_XYZ constant:
+        //              RESULT_OK|RESULT_UNKNOWN|RESULT_WARNING|RESULT_ERROR
+        // {string}  output text 
+        // {array}   optional: counter data
+        //              type   => {string} "counter"
+        //              count  => {float}  value
+        //              visual => {string} one of bar|line|simple (+params)
+        //           
+        return array(
+            RESULT_OK, 
+            'Hello world! My message is: ' .$aParams['message']
+        );
+    }
+}
diff --git a/public_html/appmonitor/plugins/checkLoadmeter.php b/public_html/appmonitor/plugins/checkLoadmeter.php
new file mode 100644
index 00000000..1bc56521
--- /dev/null
+++ b/public_html/appmonitor/plugins/checkLoadmeter.php
@@ -0,0 +1,135 @@
+<?php
+/**
+ * ____________________________________________________________________________
+ * 
+ *  _____ _____ __                   _____         _ _           
+ * |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
+ * |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
+ * |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
+ *                          |_| |_|                              
+ *                           _ _         _                                            
+ *                       ___| |_|___ ___| |_                                          
+ *                      |  _| | | -_|   |  _|                                         
+ *                      |___|_|_|___|_|_|_|   
+ *                                                               
+ * ____________________________________________________________________________
+ * 
+ * SHOW LOAD AS LINE
+ * 
+ * A plugin always is loaded in clases/appmonitor-checks.class.php
+ * Have look there for the used protected classes
+ * ____________________________________________________________________________
+ * 
+ * PARAMS:
+ *   warning {float}  limit to switch to warning
+ *   error   {float}  limit to switch to error
+ * 
+ * USAGE:
+ * 
+ * $oMonitor->addCheck(
+ *     array(
+ *         "name" => "plugin Load",
+ *         "description" => "check current load",
+ *         "check" => array(
+ *             "function" => "Loadmeter",
+ *             "params" => array(
+ *                "warning" => 1.0,
+ *                "error" => 3,
+ *             ),
+ *         ),
+ *         "worstresult" => RESULT_OK
+ *     )
+ * );
+ * ____________________________________________________________________________
+ * 
+ * 2019-06-06  <axel.hahn@iml.unibe.ch>
+ * 
+ */
+class checkLoadmeter extends appmonitorcheck{
+    
+    /**
+     * detect load of a machine and return a float value
+     * windows part was taken from https://stackoverflow.com/questions/5588616/how-do-you-calculate-server-load-in-php
+     * @return float
+     */
+    protected function _getLoad() {
+        if (function_exists('sys_getloadavg')){
+            $load = sys_getloadavg();
+            return $load[0];
+        } else {
+            // Only MS Windows has not implemented sys_getloadavg
+            // try something else
+            if(class_exists('COM')){
+                $wmi=new COM('WinMgmts:\\\\.');
+                $cpus=$wmi->InstancesOf('Win32_Processor');
+                $load=0;
+                if(version_compare('4.50.0', PHP_VERSION) == 1){
+                    while($cpu = $cpus->Next()){
+                        $load += $cpu->LoadPercentage;
+                    }
+                }else{
+                    foreach($cpus as $cpu){
+                        $load += $cpu->LoadPercentage;
+                    }
+                }
+                return $load;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * 
+     * @param array   $aParams
+     * @return array
+     */
+    public function run($aParams){
+        
+        // --- (1) verify if array key(s) exist:
+        // $this->_checkArrayKeys($aParams, "...");
+
+
+        // --- (2) do something magic
+        // $fLoad=rand(0, 1.3);
+        // $fLoad=$this->_getServerLoad();
+        $fLoad=$this->_getLoad();
+        
+        // set result code
+        if($fLoad===false){
+            $iResult=RESULT_UNKNOWN;
+        } else {
+            $iResult=RESULT_OK;
+            if(isset($aParams['warning']) && $aParams['warning'] && $fLoad>$aParams['warning']){
+                $iResult=RESULT_WARNING;
+            }
+            if(isset($aParams['error']) && $aParams['error'] && $fLoad>$aParams['error']){
+                $iResult=RESULT_ERROR;
+            }
+        }
+
+
+        // --- (3) response
+        // see method appmonitorcheck->_setReturn()
+        // 
+        // {integer} you should use a RESULT_XYZ constant:
+        //              RESULT_OK|RESULT_UNKNOWN|RESULT_WARNING|RESULT_ERROR
+        // {string}  output text 
+        // {array}   optional: counter data
+        //              type   => {string} "counter"
+        //              count  => {float}  value
+        //              visual => {string} one of bar|line|simple (+params)
+        //           
+        return array(
+            $iResult, 
+            ($fLoad===false ? 'load value is not available' : 'current load is: '.$fLoad),
+            ($fLoad===false 
+                ? array()
+                : array(
+                    'type'=>'counter',
+                    'count'=>$fLoad,
+                    'type'=>'line',
+                )
+            )
+        );
+    }
+}
-- 
GitLab