Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • iml-open-source/imldeployment
1 result
Select Git revision
Show changes
Commits on Source (4)
Showing
with 1285 additions and 172 deletions
<?php
require_once 'validateparam.class.php';
if (!defined('RESULT_OK')) {
define("RESULT_OK", 0);
......@@ -36,24 +37,28 @@ if (!defined('RESULT_OK')) {
* --------------------------------------------------------------------------------<br>
* <br>
* --- HISTORY:<br>
* 2014-10-24 0.5 axel.hahn@iml.unibe.ch<br>
* 2015-04-08 0.9 axel.hahn@iml.unibe.ch added sochket test: checkPortTcp<br>
* 2018-06-29 0.24 axel.hahn@iml.unibe.ch add file and directory checks<br>
* 2018-07-17 0.42 axel.hahn@iml.unibe.ch add port on mysqli check<br>
* 2018-07-26 0.46 axel.hahn@iml.unibe.ch fix mysql connection check with empty port param<br>
* 2018-08-14 0.47 axel.hahn@iml.unibe.ch appmonitor client: use timeout of 5 sec for tcp socket connections<br>
* 2018-08-15 0.49 axel.hahn@iml.unibe.ch cert check: added flag to skip verification<br>
* 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>
* 2021-10-28 0.93 axel.hahn@iml.unibe.ch add plugins<br>
* 2021-12-14 0.93 axel.hahn@iml.unibe.ch split plugins into single files; added key group in a check<br>
* 2023-06-02 0.125 axel.hahn@unibe.ch replace array_key_exists for better readability
* 2024-07-22 0.137 axel.hahn@unibe.ch php 8 only: use typed variables
* 2014-10-24 0.5 axel.hahn@iml.unibe.ch<br>
* 2015-04-08 0.9 axel.hahn@iml.unibe.ch added sochket test: checkPortTcp<br>
* 2018-06-29 0.24 axel.hahn@iml.unibe.ch add file and directory checks<br>
* 2018-07-17 0.42 axel.hahn@iml.unibe.ch add port on mysqli check<br>
* 2018-07-26 0.46 axel.hahn@iml.unibe.ch fix mysql connection check with empty port param<br>
* 2018-08-14 0.47 axel.hahn@iml.unibe.ch appmonitor client: use timeout of 5 sec for tcp socket connections<br>
* 2018-08-15 0.49 axel.hahn@iml.unibe.ch cert check: added flag to skip verification<br>
* 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>
* 2021-10-28 0.93 axel.hahn@iml.unibe.ch add plugins<br>
* 2021-12-14 0.93 axel.hahn@iml.unibe.ch split plugins into single files; added key group in a check<br>
* 2023-06-02 0.125 axel.hahn@unibe.ch replace array_key_exists for better readability
* 2024-07-22 0.137 axel.hahn@unibe.ch php 8 only: use typed variables
* 2025-02-28 0.152 axel.hahn@unibe.ch listChecks: add loop over currently loaded classes
* 2025-03-03 0.153 axel.hahn@unibe.ch getSize() preg_replace did not work in compiled binary
* 2025-03-04 0.154 axel.hahn@unibe.ch finish with existcode instead of die()
* 2025-03-18 0.156 axel.hahn@unibe.ch add validation class
* --------------------------------------------------------------------------------<br>
* @version 0.137
* @version 0.156-dev
* @author Axel Hahn
* @link TODO
* @license GPL
......@@ -66,6 +71,47 @@ class appmonitorcheck
// CONFIG
// ----------------------------------------------------------------------
protected array $_aCheckDocs = [
'name' => [
'type' => 'string',
'required' => true,
'description' => 'Name of a check',
'regex'=>'/./',
'example' => 'configfile',
],
'description' => [
'type' => 'string',
'required' => true,
'description' => 'A short description to describe what is tested',
'regex'=>'/./',
'example' => 'Check if config file inc_config.php exists, is readable and writable',
],
'check' => [
'type' => 'array',
'required' => true,
'description' => 'Array of the check',
],
'parent' => [
'type' => 'string',
'required' => false,
'description' => 'Reference a \'name\' of another check to generate a dependency tree',
'regex'=>'/./',
],
'group' => [
'type' => 'string',
'required' => false,
'description' => 'Name of a group to be nedsted in',
'regex'=>'/./',
],
'worstresult' => [
'type' => 'int',
'required' => false,
'description' => 'A failed check is max counted as given result. Use it on not required but optional checks',
'min'=>RESULT_OK,
'max'=>RESULT_ERROR,
],
];
/**
* starting time using microtime
* @var float
......@@ -118,6 +164,25 @@ class appmonitorcheck
// PRIVATE FUNCTIONS
// ----------------------------------------------------------------------
/**
* Exit execution with error message and given exicode.
* @param int $iHttpcode http statuscode
* @param string $sMessage detailed message
* @param int $iExitcode exitcode
* @return never
*/
protected function _exit($iHttpcode, $sMessage, $iExitcode): never
{
$aStatus=[
503 => 'Service Unavailable',
];
header("HTTP/1.1 $iHttpcode $aStatus[$iHttpcode]");
echo "<h1>$iHttpcode $aStatus[$iHttpcode]</h1>
<h2>Details</h2>
$sMessage\n";
exit($iExitcode);
}
/**
* Internal: create basic array values for metadata
* @return boolean
......@@ -195,36 +260,52 @@ class appmonitorcheck
/**
* 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
* @param string $sMustKeys key or keys as comma seprated list
* @return boolean
*/
protected function _checkArrayKeys($aConfig, $sKeyList)
protected function _checkArrayKeys($aConfig, $sMustKeys, $sOptionalKeys = '')
{
foreach (explode(",", $sKeyList) as $sKey) {
$aTmp=$aConfig;
foreach (explode(",", $sMustKeys) as $sKey) {
if (!isset($aConfig[$sKey])) {
header('HTTP/1.0 503 Service Unavailable');
die('<h1>503 Service Unavailable</h1>'
. '<h2>Details</h2>'
. __METHOD__ . " - array of check parameters requires the keys [$sKeyList] - but key <code>$sKey</code> was not found in config array."
. "<pre>" . print_r($aConfig, true) . '</pre>'
$this->_exit(
503,
__METHOD__ . " - array of check parameters requires the keys [$sMustKeys] - but key <code>$sKey</code> was not found in config array."
. "<pre>" . print_r($aConfig, true) . '</pre>',
20
);
}
if (is_null($aConfig[$sKey])) {
header('HTTP/1.0 503 Service Unavailable');
die('<h1>503 Service Unavailable</h1>'
. '<h2>Details</h2>'
. __METHOD__ . " - key <code>$sKey</code> is empty in config array"
. "<pre>" . print_r($aConfig, true) . '</pre>'
$this->_exit(
503,
__METHOD__ . " - key <code>$sKey</code> is empty in config array"
. "<pre>" . print_r($aConfig, true) . '</pre>',
21
);
}
unset($aTmp[$sKey]);
}
return true;
}
// ----------------------------------------------------------------------
// PUBLIC FUNCTIONS
// ----------------------------------------------------------------------
/**
* Self documentation of a check. The array is defined in
* plugins/checks/*.php files
*
* @return array
*/
public function explain(): array
{
return $this->_aDoc??[];
}
/**
* Perform a check
* @param array $aConfig configuration array for a check, eg.
......@@ -237,6 +318,9 @@ class appmonitorcheck
* [params] => [array] // optional; arguments for Check function
* // its keys depend on the function
* ]
* [group] => Group A
* [parent] => field "name" of another check
* [worstresult] => RESULT_WARNING
* ]
* </code>
* @return array
......@@ -244,8 +328,24 @@ class appmonitorcheck
public function makeCheck(array $aConfig): array
{
$this->_iStart = microtime(true);
$this->_checkArrayKeys($aConfig, "name,description,check");
$this->_checkArrayKeys($aConfig["check"], "function");
$oVal=new validateparam();
$aErrors=$oVal->validateArray($this->_aCheckDocs, $aConfig);
if(count($aErrors)){
$this->_exit(
503,
__METHOD__ . " - validation of params failed\n"
. "<pre>Errors: "
. print_r($aErrors, true)
. "Input array was: "
. print_r($aConfig, true)
. '</pre>',
22
);
}
$this->_checkArrayKeys($aConfig, "name,description,check", "group,parent,worstresult", true);
$this->_checkArrayKeys($aConfig["check"], "function", "params", true);
$this->_aConfig = $aConfig;
$this->_createDefaultMetadata();
......@@ -264,33 +364,49 @@ class appmonitorcheck
}
if (!class_exists($sCheckClass)) {
header('HTTP/1.0 503 Service Unavailable');
die('<h1>503 Service Unavailable</h1>'
. '<h2>Details</h2>'
. __METHOD__ . " - check class not found: <code>$sCheckClass</code>"
. "<pre>" . print_r($aConfig, true) . '</pre>'
. "<h2>Known checks</h2>\n" . print_r($this->listChecks(), 1)
$this->_exit(
503,
__METHOD__ . " - [$aConfig[name]] - check class not found: <code>$sCheckClass</code>"
. "<pre>" . print_r($aConfig, true) . '</pre>',
22
);
}
$oPlugin = new $sCheckClass;
$aCheckDoc=$oPlugin->explain();
$aErrors=$oVal->validateArray($oPlugin->explain()['parameters']??[], $aParams);
if(count($aErrors)){
$this->_exit(
503,
__METHOD__ . " - [$aConfig[name]] - validation of check -> params failed\n"
. "<pre>Errors: "
. print_r($aErrors, true)
. "Input array: "
. print_r($aConfig, true)
. '</pre>',
22
);
}
// die(__FILE__.":".__LINE__);
$aResponse = $oPlugin->run($aParams);
if (!is_array($aResponse)) {
header('HTTP/1.0 503 Service Unavailable');
die('<h1>503 Service Unavailable</h1>'
. '<h2>Details</h2>'
. __METHOD__ . " - plugin : $sCheck does not responses an array"
$this->_exit(
503,
__METHOD__ . " - plugin : $sCheck does not responses an array"
. "<pre>INPUT " . print_r($aConfig, true) . '</pre>'
. "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>'
. "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>',
23
);
}
if (count($aResponse) < 2) {
header('HTTP/1.0 503 Service Unavailable');
die('<h1>503 Service Unavailable</h1>'
. '<h2>Details</h2>'
. __METHOD__ . " - plugin : $sCheck does not responses the minimum of 2 array values"
. "<pre>INPUT " . print_r($aConfig, true) . '</pre>'
. "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>'
$this->_exit(
503,
__METHOD__ . " - plugin : $sCheck does not responses the minimum of 2 array values"
. "<pre>INPUT " . print_r($aConfig, true) . '</pre>'
. "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>',
24
);
}
if (!isset($aResponse[2]) || !$aResponse[2]) {
......@@ -315,6 +431,8 @@ class appmonitorcheck
public function listChecks(): array
{
$aReturn = [];
// **DEPRECATED**
// return internal protected fuctions named "check[whatever]"
$class = new ReflectionClass($this);
foreach ($class->getMethods(ReflectionMethod::IS_PROTECTED) as $oReflectionMethod) {
......@@ -326,6 +444,15 @@ class appmonitorcheck
foreach (glob($this->_sPluginDir . '/checks/*.php') as $sPluginFile) {
$aReturn[str_replace('.php', '', basename($sPluginFile))] = 1;
}
// from currently loaded classes
foreach(get_declared_classes() as $sClass){
if (strpos($sClass, "check") === 0) {
$aReturn[str_replace('check','',$sClass)] = 1;
}
}
array_unique($aReturn);
ksort($aReturn);
return array_keys($aReturn);
}
......@@ -406,17 +533,18 @@ class appmonitorcheck
$power = 0;
foreach ($this->_units as $sUnit) {
if (preg_match('/^[0-9\.\ ]*' . $sUnit . '/', $sValue)) {
$i = preg_replace('/([0-9\.]*).*/', '$1', $sValue);
// $i = preg_replace('/([0-9\.]*).*/', '$1', $sValue);
$i = str_replace($sUnit, '', $sValue);
$iReal = $i * pow(1024, $power);
// die("FOUND: $sValue with unit ${sUnit} - 1024^$power * $i = $iReal");
return $iReal;
}
$power++;
}
header('HTTP/1.0 503 Service Unavailable');
die('<h1>503 Service Unavailable</h1>'
. '<h2>Details</h2>'
. __METHOD__ . " ERROR in space value parameter - there is no size unit in [$sValue] - allowed size units are " . implode('|', $this->_units)
$this->_exit(
503,
__METHOD__ . " ERROR in space value parameter - there is no size unit in [$sValue] - allowed size units are " . implode('|', $this->_units),
25
);
}
......
......@@ -44,8 +44,12 @@ if (!class_exists('appmonitorcheck')) {
* 2024-07-19 0.137 axel.hahn@unibe.ch php 8 only: use typed variables
* 2024-11-22 0.141 axel.hahn@unibe.ch Set client version to server version after updating http, mysqli and app checks
* 2025-01-02 0.149 axel.hahn@unibe.ch add getChecks method
* 2025-03-03 0.153 axel.hahn@unibe.ch fix client checks during development of a compiled binary
* 2025-03-04 0.154 axel.hahn@unibe.ch finish with exitcode instead of die()
* 2025-03-17 0.155 axel.hahn@unibe.ch added: getVersion() and setVersion()
* 2025-03-19 0.156 axel.hahn@unibe.ch added: validation rules for parameters in all checks
* --------------------------------------------------------------------------------<br>
* @version 0.149
* @version 0.155
* @author Axel Hahn
* @link TODO
* @license GPL
......@@ -59,7 +63,7 @@ class appmonitor
* Name and Version number
* @var string
*/
protected string $_sVersion = 'php-client-v0.149';
protected string $_sVersion = '0.156';
/**
* config: default ttl for server before requesting the client check again
......@@ -119,13 +123,14 @@ class appmonitor
"ttl" => false,
"result" => false,
"time" => false,
"version" => $this->_sVersion,
"version" => false,
];
// fill with default values
$this->setHost();
$this->setWebsite();
$this->setTTL();
$this->setVersion();
return true;
}
......@@ -151,28 +156,19 @@ class appmonitor
$this->_aMeta["host"] = $s;
return true;
}
/**
* Set a name for this website or application and its environment
* (dev, test, prod);
*
* If you have several application in subdirectories, i.e. /blog, /shop...
* then you should the path or any description to identify them too
*
* if no argument is given the name of HTTP_HOST will be used
* Set final result in meta data; if no value was given then it
* sets the biggest value of any check.
*
* @param string $sWebsite Name of the website or web application
* @param integer $iResult set resultcode; one of RESULT_OK|RESULT_WARNING|RESULT_ERROR|RESULT_UNKNOWN
* @return boolean
*/
public function setWebsite($sWebsite = ''): bool
public function setResult(int $iResult = -1): bool
{
if (!$sWebsite && isset($_SERVER["HTTP_HOST"])) {
$sWebsite = $_SERVER["HTTP_HOST"];
}
if (!$sWebsite) {
return false;
if ($iResult === -1) {
$iResult = $this->_iMaxResult; // see addCheck()
}
$this->_aMeta["website"] = $sWebsite;
$this->_aMeta["result"] = $iResult;
return true;
}
......@@ -192,18 +188,38 @@ class appmonitor
}
/**
* Set final result in meta data; if no value was given then it
* sets the biggest value of any check.
* Set a prefix for meta -> version ... before "-php-client-v<VERSION>"
* @param string $sVersionPrefix new prefix
* @return bool
*/
public function setVersion($sVersionPrefix = ""): bool
{
$this->_aMeta["version"] = ($sVersionPrefix ? "$sVersionPrefix-" : "")
. "php-client-v$this->_sVersion"
;
return true;
}
/**
* Set a name for this website or application and its environment
* (dev, test, prod);
*
* @param integer $iResult set resultcode; one of RESULT_OK|RESULT_WARNING|RESULT_ERROR|RESULT_UNKNOWN
* If you have several application in subdirectories, i.e. /blog, /shop...
* then you should the path or any description to identify them too
*
* if no argument is given the name of HTTP_HOST will be used
*
* @param string $sWebsite Name of the website or web application
* @return boolean
*/
public function setResult(int $iResult = -1): bool
public function setWebsite($sWebsite = ''): bool
{
if ($iResult === -1) {
$iResult = $this->_iMaxResult; // see addCheck()
if (!$sWebsite && isset($_SERVER["HTTP_HOST"])) {
$sWebsite = $_SERVER["HTTP_HOST"];
}
$this->_aMeta["result"] = $iResult;
if (!$sWebsite) {
return false;
}
$this->_aMeta["website"] = $sWebsite;
return true;
}
......@@ -318,8 +334,11 @@ class appmonitor
return true;
}
}
header('HTTP/1.0 403 Forbidden');
die('ERROR: Your ip address [' . $sIP . '] has no access.');
$this->_exit(
403,
'ERROR: Your ip address [' . $sIP . '] has no access.',
11
);
}
/**
......@@ -339,14 +358,26 @@ class appmonitor
if (isset($_GET[$sVarname]) && $_GET[$sVarname] === $sToken) {
return true;
}
header('HTTP/1.0 403 Forbidden');
die('ERROR: A token is required.');
$this->_exit(
403,
'ERROR: A token is required.',
12
);
}
// ----------------------------------------------------------------------
// getter
// ----------------------------------------------------------------------
/**
* Get version of the appmoinitor php client
* @return string
*/
public function getVersion(): string
{
return $this->_sVersion;
}
/**
* list all available check functions. This is a helper class you cann call
* to get an overview over built in functions. You get a flat array with
......@@ -381,9 +412,11 @@ class appmonitor
}
if (count($aErrors)) {
$this->abort(
'<h2>Error: client check is not complete</h2><p>Found errors:</p><ol><li>' . implode('<li>', $aErrors) . '</ol><br><br>'
// .'Dump of your data so far:<pre>' . json_encode($this->getResults(), JSON_PRETTY_PRINT) . '</pre><hr>'
$this->_exit(
503,
'Client check is not complete<br>Found errors:<br>'
. '<ol><li>' . implode('<li>', $aErrors) . '</ol><br><br>',
10
);
}
return true;
......@@ -394,14 +427,24 @@ class appmonitor
// ----------------------------------------------------------------------
/**
* Stop processing the client checks and abort with an error
* @param string $sMessage text to show after a 503 headline
* @return void
* Exit execution with error message and given exicode.
* @param int $iHttpcode http statuscode
* @param string $sMessage detailed message
* @param int $iExitcode exitcode
* @return never
*/
public function abort(string $sMessage): void
protected function _exit($iHttpcode, $sMessage, $iExitcode): never
{
header('HTTP/1.0 503 Service Unavailable');
die('<h1>503 Service Unavailable</h1>' . $sMessage);
$aStatus = [
403 => 'Forbidden',
503 => 'Service Unavailable',
];
header("HTTP/1.1 $iHttpcode $aStatus[$iHttpcode]");
echo "<h1>$iHttpcode $aStatus[$iHttpcode]</h1>
<h2>Details</h2>
$sMessage\n";
exit($iExitcode);
}
/**
......@@ -433,7 +476,7 @@ class appmonitor
{
$this->_checkData();
$this->_aMeta['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms';
$sOut=json_encode($this->getResults());
$sOut = json_encode($this->getResults());
header('Content-type: application/json');
header('Cache-Control: cache');
......@@ -508,7 +551,7 @@ class appmonitor
$sOut .= '<span class="result' . $i . '">' . $sText . '</span> ';
}
$sRaw=json_encode($aData, JSON_PRETTY_PRINT);
$sRaw = json_encode($aData, JSON_PRETTY_PRINT);
$sRaw = preg_replace('/:\ \"(.*)\"/U', ': "<span class="string">$1</span>"', $sRaw);
$sRaw = preg_replace('/:\ ([0-9]*)/', ': <span class="int">$1</span>', $sRaw);
$sRaw = preg_replace('/\"(.*)\":/U', '"<span class="key">$1</span>":', $sRaw);
......
<?php
class validateparam
{
/**
* Summary of _aValidationDefs
* @var array
*/
protected array $_aValidationDefs = [];
protected bool $_flag_debug = false;
/**
* Write debug line if _flag_debug is set to true
* @param string $s message to show
* @return void
*/
protected function _wd(string $s)
{
if ($this->_flag_debug) {
echo "DEBUG: $s<br>\n";
}
}
/**
* Include the default validation rules defined in validateparam.settings.php
*/
public function __construct(){
// IMPORTANT:
// This style is not so nice like <variable> = include <file>;
// keep the include line unchanged - it is detected by installer
// CLI client project (amcli)
include 'validateparam.settings.php';
}
/**
* shared validaten checks for floor and integer
*
* @param array $aOpt Validation rules
* @param mixed $value value to verify
* @return string with found error
*/
protected function _checkCount(array $aOpt, mixed $value)
{
$sError = "";
if ($aOpt['min'] ?? false) {
if ($value < $aOpt['min']) {
$sError .= "Value is too small; minimum is $aOpt[min]. ";
}
}
if ($aOpt['max'] ?? false) {
if ($value > $aOpt['max']) {
$sError .= "Value is too big; maximum is $aOpt[max]. ";
}
}
if (isset($aOpt['oneof']) && is_array($aOpt['oneof'])) {
if (array_search($value, $aOpt['oneof']) == false) {
$sError .= "Value is invalid. Value doesn't match one of these values " . print_r($aOpt['oneof']);
}
}
return $sError;
}
/**
* Validate a single value. It returns a string with an errormessage.
* No error means ok.
*
* @param array $aOpt Validation rules
* @param mixed $value value to verify
* @return string with found error
*/
public function validateValue(array $aOpt, mixed $value): string
{
$sError = '';
if (isset($aOpt['validate'])){
if ($this->_aValidationDefs[$aOpt['validate']] ?? false) {
$this->_wd("adding options ".print_r($this->_aValidationDefs[$aOpt['validate']],true));
$aOpt = array_merge($aOpt, $this->_aValidationDefs[$aOpt['validate']]);
} else {
$sError .= "Unknown value in 'validate'='$aOpt[validate]'";
}
}
// check type
if (isset($aOpt['type'])) {
$this->_wd("check type $aOpt[type]");
switch ($aOpt['type']) {
case 'array':
if (!is_array($value)) {
$sError .= "Value isn't an array";
}
break;
case 'bool':
if (!is_bool($value)) {
$sError .= "Value '$value' isn't a bool";
}
break;
case 'float':
if (!is_float($value) && !is_int($value)) {
$sError .= "Value isn't a float";
} else {
$sError .= $this->_checkCount($aOpt, $value);
}
break;
case 'int':
case 'integer':
if (!is_int($value)) {
$sError .= "Value '$value' isn't an integer";
} else {
$sError .= $this->_checkCount($aOpt, $value);
}
break;
case 'string':
if (!is_string($value)) {
$sError .= "Value '$value' isn't a string";
} else {
if ($aOpt['regex'] ?? false) {
if (!preg_match($aOpt['regex'], $value)) {
$sError .= "Value is invalid. Regex doesn't match: $aOpt[regex]";
}
}
if (isset($aOpt['oneof']) && is_array($aOpt['oneof'])) {
if (array_search($value, $aOpt['oneof']) == false) {
$sError .= "Value is invalid. Value doesn't match one of these values " . print_r($aOpt['oneof']);
}
}
}
break;
default:
$sError .= "ERROR Cannot validate unknown type: '$aOpt[type]'<br>\n";
}
}
// if string: verify regex
return $sError;
}
/**
* Validate an array of parameter definitions. It returns an array of all error messages
* No error / an empty array means ok.
*
* @param mixed $aDefs array with definitions
* @param mixed $aParams array of given values
* @param mixed $bStrict flag: strict checking to detect wrong keys.
* @return array with errors
*/
public function validateArray(array $aDefs, array $aParams, bool $bStrict = true)
{
$aErrors = [];
// echo "<pre>";
// echo "aDefs = "; print_r($aDefs);
// echo "aParams = "; print_r($aParams);
// echo "<hr>";
if (!count($aDefs)) {
return ['Defs' => 'No validation rules given.'];
}
$aTmp = $aParams;
foreach ($aDefs as $sKey => $aOpt) {
unset($aTmp[$sKey]);
if ($aOpt['required'] ?? false) {
$this->_wd("Check MUST $sKey");
// verify MUST field
if (!isset($aParams[$sKey])) {
$aErrors[$sKey] = "Missing required key '$sKey'";
} else {
$this->_wd("MUST field exists: $sKey");
$sError = $this->validateValue($aOpt, $aParams[$sKey]);
if ($sError) {
$aErrors[$sKey] = $sError;
} else {
$this->_wd("$sKey was successfully validated.<hr>");
}
}
}
if (isset($aParams[$sKey])) {
$this->_wd("Check OPTIONAL $sKey");
$sError = $this->validateValue($aOpt, $aParams[$sKey]);
if ($sError) {
$aErrors[$sKey] = $sError;
}
}
}
if ($bStrict && isset($aTmp) && count($aTmp)) {
foreach (array_keys($aTmp) as $sKey) {
$aErrors[$sKey] = "Invalid key was found: '$sKey'; allowed keys are '" . implode("', '", array_keys($aDefs ?? [])) . "'";
}
}
$this->_wd("found errors: " . print_r($aErrors, 1));
return $aErrors;
}
}
\ No newline at end of file
# validateparam.class.php
## Description
This class can validate values and arrays.
You define type and validateion rules. A validation method checks the given value against your rules. It returns the error message(s). No error means, the value is ok.
## Usage
```php
$oVal=new validateparam();
// check an array
$aErrors=$oVal->validateArray($aRules, $aGivenValues, $bStrict);
if(count($aErrors)){
echo "Something is wrong: <pre>".print_r($aErrors, 1)."</pre>";
exit(1);
}
```
## Validate Arrays
First something visual - an example:
```txt
Array
(
[name] => Array
(
[type] => string
[required] => true
[regex] => /./
)
[description] => Array
(
[type] => string
[required] => true
[regex] => /./
)
:
[worstresult] => Array
(
'type' => 'int',
'required' => false,
'description' => 'A failed check is max counted as given result. Use it on not required but optional checks',
'min'=>RESULT_OK,
'max'=>RESULT_ERROR,
)
)
```
### Type check
Each key can be marked to be a value of a given type.
It can be a mandantory value or optional.
| Name | Type | Description
|-- |-- |--
| 'type' | {string} | variable type that must match; one of "array", "bool", "count", "float", "int", "integer", "string"
| 'required' | {bool} | define value as required
Next to these keys per type you can define validation rules in dependency of the type.
### Validate numbers
This section describes values of the `type`
* int|integer - integer values
* float - float values (or integer)
Values you can verify if it is in a wanted range.
| Name | Type | Description
|-- |-- |--
| 'min' | {float\|int} | allowed minimum value
| 'max' | {float\|int} | allowed maximum value
| 'oneof' | {array} | value must match one of the given values
### Validate string
Values of type "string" can be verified
* against a given regex
* with a set of allowed values
| Name | Type | Description
|-- |-- |--
| 'regex' | {string} | value must match given regex
| 'oneof' | {array} | value must match one of the given values
### Validate with presets
In the file *validateparam.settings.php* you can put presets and their validation rules for elements that repeat.
```php
<?php
/*
validation rules
if a param has the key 'validate' that matches a key then its values will
be added for validation.
SYNTAX
KEY - value for'validate' key
VALUE - array with these possible keys
- type - set a type
- regex - define a regex for type sting
- min, max - range for types float and int
- oneof
*/
return [
'hostname' => [ 'type' => 'string', 'regex' => '/^[a-z0-9\_\-\.]/i'],
'portnumber' => [ 'type' => 'int', 'min' => 0, 'max' => 65535],
'website' => [ 'type' => 'string', 'regex' => '/https?:\/\//'],
];
```
```txt
[port] => Array
(
[type] => int
[required] => true
[validate] => portnumber
)
```
<?php
/*
validation rules
if a param has the key 'validate' that matches a key then its values will
be added for validation.
This file is included in the constructor of the validateparam class
SYNTAX:
KEY - value for'validate' key
VALUE - array with these possible keys
- type - set a type
- regex - define a regex for type sting
- min, max - range for types float and int
- oneof
*/
$this->_aValidationDefs = [
'hostname' => [ 'type' => 'string', 'regex' => '/^[a-z0-9\_\-\.]/i'],
'portnumber' => [ 'type' => 'int', 'min' => 0, 'max' => 65535],
'website' => [ 'type' => 'string', 'regex' => '/https?:\/\//'],
];
<?php
/*
validation rules
if a param has the key 'validate' that matches a key then its values will
be added for validation.
SYNTAX
KEY - value for'validate' key
VALUE - array with these possible keys
- type - set a type
- regex - define a regex for type sting
- min, max - range for types float and int
- oneof
*/
$this->_aValidationDefs = [
'hostname' => [ 'type' => 'string', 'regex' => '/^[a-z0-9\_\-\.]/i'],
'portnumber' => [ 'type' => 'int', 'min' => 0, 'max' => 65535],
'website' => [ 'type' => 'string', 'regex' => '/https?:\/\//'],
];
......@@ -362,7 +362,7 @@ $oMonitor->addCheck(
[
"name" => "plugin Load",
"description" => "current load",
"parent" => false,
"parent" => null,
"check" => [
"function" => "Loadmeter",
"params" => [
......@@ -377,7 +377,7 @@ $oMonitor->addCheck(
[
"name" => "plugin ApacheProcesses",
"description" => "Apache processes",
"parent" => false,
"parent" => null,
"check" => [
"function" => "ApacheProcesses",
"params" => [
......
......@@ -14,6 +14,7 @@
* 2022-03-28 move checks into plugins/apps/
* 2024-07-23 php 8: short array syntax
* 2024-12-28 added check for custom config and url file (if they exist)
* 2025-02-24 add checks for sqlite and data dir
*/
// ----------------------------------------------------------------------
......@@ -28,7 +29,7 @@ $oMonitor->addCheck(
"check" => [
"function" => "Phpmodules",
"params" => [
"required" => ["curl"],
"required" => ["curl", "json", "pdo_sqlite"],
"optional" => [],
],
],
......@@ -123,6 +124,22 @@ if (is_file("$sApproot/server/config/appmonitor-server-urls.json")) {
);
}
$oMonitor->addCheck(
[
"name" => "write to ./data/",
"description" => "Check sqlite data directory",
// "group" => "folder",
"check" => [
"function" => "File",
"params" => [
"filename" => "$sApproot/server/data",
"dir" => true,
"writable" => true,
],
],
]
);
// ----------------------------------------------------------------------
// protect dirs against web access
// specialty: if the test results in an error, the total result switches
......@@ -132,7 +149,7 @@ $sBaseUrl = 'http' . (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] ? 's' : '')
. '://' . $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT']
. dirname(dirname($_SERVER['REQUEST_URI']));
foreach (['server/config', 'server/tmp'] as $sMyDir) {
foreach (['server/config', 'server/data', 'server/tmp'] as $sMyDir) {
$oMonitor->addCheck(
[
"name" => "http to $sMyDir",
......@@ -154,14 +171,58 @@ foreach (['server/config', 'server/tmp'] as $sMyDir) {
// count of current projects
// ----------------------------------------------------------------------
require_once($sApproot . '/server/classes/appmonitor-server.class.php');
$oServer = new appmonitorserver();
$oServer = new appmonitorserver(false);
$aCfg=$oServer->getConfigVars();
$oMonitor->addCheck(
[
"name" => "db-config",
"description" => "Check if the service is running",
// "group" => "service",
"parent" => "check custom config file",
"check" => [
"function" => "Simple",
"params" => [
"result" => ($aCfg['db']['dsn'] ? RESULT_OK : RESULT_ERROR),
"value" => ($aCfg['db']['dsn']
? "OK: dsn was set"
:"Error: database dsn was not found in config."
)
],
]
]
);
if($aCfg['db']['dsn']){
$aDB=[
'connect'=>$aCfg['db']['dsn'] ?? '',
'user'=>$aCfg['db']['user'] ?? '',
'password'=> $aCfg['db']['password'] ?? ''
];
// print_r($aDB);
$oMonitor->addCheck(
[
"name" => "connect db",
"description" => "Connnect PDO database",
"group" => "database",
"parent" => "db-config",
"check" => [
"function" => "PdoConnect",
"params" => $aDB
],
]
);
}
$iCount = count($oServer->getAppIds());
$oMonitor->addCheck(
[
"name" => "appcounter",
"description" => "Monitored apps",
"group" => "monitor",
"parent" => false,
"parent" => null,
"check" => [
"function" => "Simple",
"params" => [
......@@ -173,6 +234,7 @@ $oMonitor->addCheck(
],
]
);
// ----------------------------------------------------------------------
// check running service
// ----------------------------------------------------------------------
......@@ -211,7 +273,7 @@ $oMonitor->addCheck(
"name" => "plugin Load",
"description" => "current load",
"group" => 'monitor',
"parent" => false,
"parent" => null,
"check" => [
"function" => "Loadmeter",
"params" => [
......
......@@ -64,8 +64,10 @@ $aDb = [
// checks
// ----------------------------------------------------------------------
// required php modules
// see https://ertano.com/required-php-modules-for-wordpress/
// required php modules * WIP
// see https://wordpress.org/about/requirements/ << doesn't say anything about php modules
// see https://ertano.com/rired-php-modules-for-wordpress/ << too many modules
// see https://zeropointdevelopment.com/required-php-extensions-for-wordpress-wpquickies/
$oMonitor->addCheck(
[
"name" => "PHP modules",
......@@ -76,22 +78,24 @@ $oMonitor->addCheck(
"params" => [
"required" => [
// "cmath",
"cli",
// "cli",
"curl",
"date",
"dom",
"fileinfo",
"filter",
"gd",
// "filter",
// "gd",
"gettext",
"hash",
"iconv",
// "iconv",
"imagick",
"json",
// "libsodium",
"mysql",
"mbstring",
"mysqli",
"openssl",
"pcre",
"sodium",
// "opcache",
// "readline",
"xml",
......
......@@ -45,7 +45,8 @@
* 2019-06-07 <axel.hahn@iml.unibe.ch>
* 2022-07-06 <axel.hahn@iml.unibe.ch> set group "monitor"
* 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables
*
* 2025-03-04 <axel.hahn@unibe.ch> fix sComment error on failed apache status
* 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description
*/
class checkApacheProcesses extends appmonitorcheck
{
......@@ -69,39 +70,37 @@ class checkApacheProcesses extends appmonitorcheck
protected float $_iError = 75;
/**
* Self documentation (as idea)
* @return array
* Self documentation and validation rules
* @var array
*/
public function explain(): array
{
return [
'name' => 'Plugin ApacheProcesses',
'descriptionm' => 'Check count running Apache processes',
'parameters' => [
'url' => [
'type' => 'string',
'required' => false,
'decsription' => 'Override https server-status page; default is http://localhost/server-status; Use it if the protocol to localhost is not http, but https or if it requires an authentication',
'default' => $this->_sServerStatusUrl,
'example' => '',
],
'warning' => [
'type' => 'float',
'required' => false,
'decsription' => 'Limit to switch to warning (in percent)',
'default' => $this->_iWarn,
'example' => 30,
],
'error' => [
'type' => 'float',
'required' => false,
'decsription' => 'Limit to switch to critical (in percent)',
'default' => $this->_iError,
'example' => 50,
],
protected array $_aDoc = [
'name' => 'Plugin ApacheProcesses',
'description' => 'Check count running Apache processes',
'parameters' => [
'url' => [
'type' => 'string',
'required' => false,
'description' => 'Override https server-status page; default is http://localhost/server-status; Use it if the protocol to localhost is not http, but https or if it requires an authentication',
'default' => "http://localhost/server-status",
'regex'=>'/^https?:\/\/[^\s]+/',
'example' => '',
],
];
}
'warning' => [
'type' => 'float',
'required' => false,
'description' => 'Limit to switch to warning (in percent)',
'default' => "50",
'example' => 30,
],
'error' => [
'type' => 'float',
'required' => false,
'description' => 'Limit to switch to critical (in percent)',
'default' => "75",
'example' => 50,
],
],
];
/**
* Fetch http server status and return slots, active and waiting processes
......@@ -112,7 +111,7 @@ class checkApacheProcesses extends appmonitorcheck
*/
protected function _getApacheProcesses(): bool|array
{
$sBody = file_get_contents($this->_sServerStatusUrl);
$sBody = @file_get_contents($this->_sServerStatusUrl);
if (!$sBody) {
return false;
}
......@@ -163,12 +162,12 @@ class checkApacheProcesses extends appmonitorcheck
// --- (2) do something magic
$aProcesses = $this->_getApacheProcesses();
$iActive = $aProcesses ? $aProcesses['active'] : false;
$sComment = '';
// set result code
if ($iActive === false) {
$iResult = RESULT_UNKNOWN;
} else {
$sComment = '';
$iTotal = $aProcesses['total'];
$iResult = RESULT_OK;
if (($iActive / $iTotal * 100) > $this->_iWarn) {
......
......@@ -40,10 +40,52 @@
* 2022-05-02 <axel.hahn@iml.unibe.ch> set warning to 21 days (old value was 30); add "critical" param
* 2022-05-03 <axel.hahn@iml.unibe.ch> critical limit is a warning only (because app is still functional)
* 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables
*
* 2025-03-03 <axel.hahn@unibe.ch> comment block for host check in DND names
* 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description
*/
class checkCert extends appmonitorcheck
{
/**
* Self documentation and validation rules
* @var array
*/
protected array $_aDoc = [
'name' => 'Plugin Cert',
'description' => 'Check if a SSL certificate is still valid … and does not expire soon.',
'parameters' => [
'url' => [
'type' => 'string',
'required' => true,
'description' => 'Url to check https://[server}[:{port}] or ssl://[server}[:{port}]',
'default' => null,
'regex'=>'/^(https|ssl):\/\/[^\s]+/',
'example' => '',
],
'verify' => [
'type' => 'bool',
'required' => false,
'description' => 'optional: flag verify certificate; default = true',
'default' => true,
'example' => "false",
],
'warning' => [
'type' => 'int',
'required' => false,
'description' => 'optional: count of days to warn; default=21',
'default' => 21,
'example' => 30,
],
'critical' => [
'type' => 'int',
'required' => false,
'description' => 'optional: count of days to raise critical; default=5',
'default' => 5,
'example' => "7",
],
],
];
/**
* Get default group of this check
* @return string
......@@ -80,6 +122,11 @@ class checkCert extends appmonitorcheck
];
}
/*
unneeded:
when verify is true (=default) then it cannot connect with wrong certificate
$sDNS = $certinfo['extensions']['subjectAltName'] ?? false;
$sHost = parse_url($sUrl, PHP_URL_HOST);
if (strstr($sDNS, "DNS:$sHost") === false) {
......@@ -88,6 +135,7 @@ class checkCert extends appmonitorcheck
"Wrong certificate: $sHost is not listed as DNS alias in [$sDNS]. $sMessage"
];
}
*/
$iDaysleft = round(($certinfo['validTo_time_t'] - date('U')) / 60 / 60 / 24);
$sMessage .= 'Issuer: ' . $certinfo['issuer']['O']
......
......@@ -20,9 +20,44 @@
* 2021-10-26 <axel.hahn@iml.unibe.ch>
* 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables
* 2025-01-02 <www.axel-hahn.de> update output
* 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description
*/
class checkDiskfree extends appmonitorcheck
{
/**
* Self documentation and validation rules
* @var array
*/
protected array $_aDoc = [
'name' => 'Plugin Diskfree',
'description' => 'Check if a given filesystem / directory that it has enough space.',
'parameters' => [
'directory' => [
'type' => 'string',
'required' => true,
'description' => 'directory to check',
'default' => null,
'regex'=>'/./',
'example' => '',
],
'warning' => [
'type' => 'string',
'required' => false,
'description' => 'size for warning level',
'default' => 21,
'example' => "1.25GB",
],
'critical' => [
'type' => 'string',
'required' => true,
'description' => 'size for critical level',
'default' => 5,
'example' => "500.7MB",
],
],
];
/**
* Get default group of this check
* @return string
......
......@@ -21,10 +21,58 @@
*
* 2022-09-19 <axel.hahn@iml.unibe.ch>
* 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables
*
* 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description
*/
class checkExec extends appmonitorcheck
{
/**
* Self documentation and validation rules
* @var array
*/
protected array $_aDoc = [
'name' => 'Plugin Exec',
'description' => 'Execute a shell command.',
'parameters' => [
'command' => [
'type' => 'string',
'required' => true,
'description' => 'Command line',
'default' => null,
'regex'=>'/./',
'example' => '[command] [parameters] 2>&1',
],
'output' => [
'type' => 'bool',
'required' => false,
'description' => 'size for warning level',
'default' => 21,
'example' => "1.25GB",
],
'exitOk' => [
'type' => 'array',
'required' => false,
'description' => 'array of integers for ok exitcodes',
'default' => null,
'example' => "",
],
'exitWarn' => [
'type' => 'array',
'required' => false,
'description' => 'array of integers for warning exitcodes',
'default' => null,
'example' => "",
],
'exitCritical' => [
'type' => 'array',
'required' => false,
'description' => 'array of integers for critical exitcodes',
'default' => null,
'example' => "",
],
],
];
/**
* Get default group of this check
* @return string
......
......@@ -22,9 +22,79 @@
*
* 2021-10-26 <axel.hahn@iml.unibe.ch>
* 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables
* 2025-03-01 <axel.hahn@unibe.ch> fix check for files that must be absent
* 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description
*/
class checkFile extends appmonitorcheck
{
/**
* Self documentation and validation rules
* @var array
*/
protected array $_aDoc = [
'name' => 'Plugin File',
'description' => 'Check existing or absent file objects: files, directories, softlinks',
'parameters' => [
'filename' => [
'type' => 'string',
'required' => true,
'description' => 'filename or directory to check',
'default' => "",
'regex' => '/./',
'example' => '/var/www/myfile.txt',
],
'exists' => [
'type' => 'bool',
'required' => false,
'description' => 'true = file object must exist (default); false = file object must not exist',
'default' => true,
'example' => "true",
],
'dir' => [
'type' => 'bool',
'required' => false,
'description' => 'If true then check if fileobject is a directory',
'default' => null,
'example' => "false",
],
'file' => [
'type' => 'bool',
'required' => false,
'description' => 'If true then check if fileobject is a file',
'default' => null,
'example' => "true",
],
'link' => [
'type' => 'bool',
'required' => false,
'description' => 'If true then check if fileobject is a file',
'default' => null,
'example' => "true",
],
'executable' => [
'type' => 'bool',
'required' => false,
'description' => 'If true then check if fileobject has executable permissions; if false it must be not executable',
'default' => null,
'example' => "false",
],
'readable' => [
'type' => 'bool',
'required' => false,
'description' => 'If true then check if fileobject is readable; if false it must be not readable',
'default' => null,
'example' => "true",
],
'writable' => [
'type' => 'bool',
'required' => false,
'description' => 'If true then check if fileobject is writable; ; if false it must be not writable',
'default' => null,
'example' => "true",
],
],
];
/**
* Get default group of this check
* @param array $aParams - see run() method
......@@ -68,7 +138,10 @@ class checkFile extends appmonitorcheck
if (isset($aParams['exists'])) {
$sMyflag = 'exists=' . ($aParams['exists'] ? 'yes' : 'no');
if (file_exists($sFile) && $aParams['exists']) {
if (
file_exists($sFile) && $aParams['exists']
|| !file_exists($sFile) && !$aParams['exists']
) {
$aOK[] = $sMyflag;
} else {
$aErrors[] = $sMyflag;
......
......@@ -41,10 +41,28 @@
*
* 2019-06-05 <axel.hahn@iml.unibe.ch>
* 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables
*
* 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description
*/
class checkHello extends appmonitorcheck
{
/**
* Self documentation and validation rules
* @var array
*/
protected array $_aDoc = [
'name' => 'Plugin Hello',
'description' => 'Show a simple message',
'parameters' => [
'message' => [
'type' => 'string',
'required' => true,
'description' => 'Message to show',
'default' => "",
'regex' => '/./',
'example' => 'Here I am',
],
],
];
/**
* Run the check
......
......@@ -22,9 +22,115 @@
* 2023-07-06 <axel.hahn@unibe.ch> add flag userpwd
* 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables
* 2024-11-22 <axel.hahn@unibe.ch> Return unknown if curl module is not active
* 2025-03-17 <axel.hahn@unibe.ch> Fix check for http status code
* 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description
*/
class checkHttpContent extends appmonitorcheck
{
/**
* Self documentation and validation rules
* @var array
*/
protected array $_aDoc = [
'name' => 'Plugin HttpContent',
'description' => 'This check verifies if a given url can be requested. Optionally you can test if it follows wanted rules.',
'parameters' => [
'url' => [
'type' => 'string',
'required' => true,
'description' => 'Url to fetch',
'default' => "",
'regex' => '/https?:\/\//',
'example' => 'https://www.example.com/',
],
'userpwd' => [
'type' => 'string',
'required' => false,
'description' => 'User and password; syntax: “[username]:[password]”',
'regex' => '/.*:.*/',
'default' => "",
'example' => "myuser:aVerySecretPassword",
],
'timeout' => [
'type' => 'int',
'required' => false,
'description' => 'Timeout in sec',
'default' => 5,
'example' => "10",
],
'headeronly' => [
'type' => 'bool',
'required' => false,
'description' => 'flag to fetch http response herader only (HEAD request); default: false = returns header and body;',
'default' => false,
'example' => "true",
],
'follow' => [
'type' => 'bool',
'required' => false,
'description' => 'flag to follow a location; default: false = do not follow; If you set it to true it ries to follow (but this is not a safe method)',
'default' => false,
'example' => "true",
],
'sslverify' => [
'type' => 'bool',
'required' => false,
'description' => 'Enable/ disable verification of ssl certificate; default: true (verification is on)',
'default' => true,
'example' => "false",
],
'status' => [
'type' => 'int',
'required' => false,
'description' => 'Test for an expected http status code; if none is given then test fails on status 400 and greater.',
'default' => null,
'example' => "401",
],
'headercontains' => [
'type' => 'string',
'required' => false,
'description' => 'Test for a string in the http response header; it returns OK if the text was found',
'default' => null,
'example' => "Content-Type: text/css",
],
'headernotcontains' => [
'type' => 'string',
'required' => false,
'description' => 'Test for a string in the http response header; it returns OK if the text was not found',
'default' => null,
'example' => "Server:",
],
'headerregex' => [
'type' => 'string',
'required' => false,
'description' => 'Test for a regex in the http response header; it returns OK if the regex matches',
'default' => null,
'example' => "",
],
'bodycontains' => [
'type' => 'string',
'required' => false,
'description' => 'Test for a string in the http response body; it returns OK if the text was found',
'default' => null,
'example' => "Content-Type: text/css",
],
'bodynotcontains' => [
'type' => 'string',
'required' => false,
'description' => 'Test for a string in the http response body; it returns OK if the text was not found',
'default' => null,
'example' => "Server:",
],
'bodyregex' => [
'type' => 'string',
'required' => false,
'description' => 'Test for a regex in the http response body; it returns OK if the regex matches',
'default' => null,
'example' => "",
],
],
];
/**
* Get default group of this check
* It is a "service" icon or "deny" for expected failures
......@@ -32,7 +138,7 @@ class checkHttpContent extends appmonitorcheck
* @param array $aParams with optional 'status' containing http response code
* @return string
*/
public function getGroup(array $aParams=[]): string
public function getGroup(array $aParams = []): string
{
$sReturn = 'service';
if (isset($aParams['status']) && $aParams['status'] > 300 && $aParams['status'] < 500) {
......@@ -67,7 +173,7 @@ class checkHttpContent extends appmonitorcheck
{
$this->_checkArrayKeys($aParams, "url");
if (!function_exists("curl_init")) {
return [RESULT_UNKNOWN, "UNKNOWN: Unable to perform mysqli test. The php-curl module is not active."];
return [RESULT_UNKNOWN, "UNKNOWN: Unable to perform http test. The php-curl module is not active."];
}
$bShowContent = (isset($aParams["content"]) && $aParams["content"]) ? true : false;
$ch = curl_init($aParams["url"]);
......@@ -141,12 +247,12 @@ class checkHttpContent extends appmonitorcheck
// ---------- check functions
// --- http status code
$sOut .= "Http status: " . $aInfos['http_code'] . " - ";
$sOut .= "Http status: $aInfos[http_code] - ";
if (isset($aParams["status"])) {
if ($aInfos['http_code'] === $aParams["status"]) {
$sOut .= "compare OK<br>";
if ($aInfos['http_code'] == $aParams["status"]) {
$sOut .= "as expected - OK<br>";
} else {
$sOut .= "compare failed<br>";
$sOut .= "compare failed - not eaqual $aParams[status]<br>";
$bError = true;
}
} else {
......@@ -159,7 +265,7 @@ class checkHttpContent extends appmonitorcheck
}
// --- http header
if (isset($aParams["headercontains"]) && $aParams["headercontains"]) {
$sOut .= "Http header contains &quot;" . $aParams["headercontains"] . "&quot; - ";
$sOut .= "Http header contains '$aParams[headercontains]' - ";
if (!strstr($sHttpHeader, $aParams["headercontains"]) === false) {
$sOut .= "compare OK<br>";
} else {
......@@ -168,7 +274,7 @@ class checkHttpContent extends appmonitorcheck
}
}
if (isset($aParams["headernotcontains"]) && $aParams["headernotcontains"]) {
$sOut .= "Http header does not contain &quot;" . $aParams["headernotcontains"] . "&quot; - ";
$sOut .= "Http header does not contain '$aParams[headernotcontains]' - ";
if (strstr($sHttpHeader, $aParams["headernotcontains"]) === false) {
$sOut .= "compare OK<br>";
} else {
......@@ -177,7 +283,7 @@ class checkHttpContent extends appmonitorcheck
}
}
if (isset($aParams["headerregex"]) && $aParams["headerregex"]) {
$sOut .= "Http header regex test &quot;" . $aParams["headerregex"] . "&quot; - ";
$sOut .= "Http header regex test '$aParams[headerregex]' - ";
try {
$bRegex = preg_match($aParams["headerregex"], $sHttpHeader);
if ($bRegex) {
......@@ -193,7 +299,7 @@ class checkHttpContent extends appmonitorcheck
}
// --- http body
if (isset($aParams["bodycontains"]) && $aParams["bodycontains"]) {
$sOut .= "Http body contains &quot;" . $aParams["bodycontains"] . "&quot; - ";
$sOut .= "Http body contains '$aParams[bodycontains]' - ";
if (!strstr($sHttpBody, $aParams["bodycontains"]) === false) {
$sOut .= "compare OK<br>";
} else {
......@@ -202,7 +308,7 @@ class checkHttpContent extends appmonitorcheck
}
}
if (isset($aParams["bodynotcontains"]) && $aParams["bodynotcontains"]) {
$sOut .= "Http body does not contain &quot;" . $aParams["bodynotcontains"] . "&quot; - ";
$sOut .= "Http body does not contain '$aParams[bodynotcontains]' - ";
if (strstr($sHttpBody, $aParams["bodynotcontains"]) === false) {
$sOut .= "compare OK<br>";
} else {
......@@ -211,7 +317,7 @@ class checkHttpContent extends appmonitorcheck
}
}
if (isset($aParams["bodyregex"]) && $aParams["bodyregex"]) {
$sOut .= "Http body regex test &quot;" . $aParams["bodyregex"] . "&quot; - ";
$sOut .= "Http body regex test '$aParams[bodyregex]' - ";
try {
$bRegex = preg_match($aParams["bodyregex"], $sHttpBody);
if ($bRegex) {
......@@ -229,12 +335,12 @@ class checkHttpContent extends appmonitorcheck
if (!$bError) {
return [
RESULT_OK,
'OK: http check "' . $aParams["url"] . '".<br>' . $sOut
"OK: http check '$aParams[url]'<br>$sOut"
];
} else {
return [
RESULT_ERROR,
'ERROR: http check "' . $aParams["url"] . '".<br>' . $sOut
"ERROR: http check '$aParams[url]'<br>$sOut"
];
}
......
......@@ -45,10 +45,37 @@
* 2019-06-06 <axel.hahn@iml.unibe.ch>
* 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables
* 2024-07-25 <axel.hahn@unibe.ch> float return with 2 digits behind comma
*
* 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description
*/
class checkLoadmeter extends appmonitorcheck
{
/**
* Self documentation and validation rules
* @var array
*/
protected array $_aDoc = [
'name' => 'Plugin Loadmeter',
'description' => 'Get system load and render it as a tile.',
'parameters' => [
'warning' => [
'type' => 'float',
'required' => false,
'description' => 'Warning level',
// doc
'default' => null,
'example' => '1.5',
],
'error' => [
'type' => 'float',
'required' => false,
'description' => 'Critical level when to raise an error',
'default' => null,
'example' => '2.5',
],
],
];
/**
* Get default group of this check
* @return string
......
......@@ -20,9 +20,70 @@
* 2021-10-27 <axel.hahn@iml.unibe.ch>
* 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables
* 2024-11-22 <axel.hahn@unibe.ch> detect installed mysqli function
* 2025-03-01 <axel.hahn@unibe.ch> add try catch
* 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description
*/
class checkMysqlConnect extends appmonitorcheck
{
/**
* Self documentation and validation rules
* @var array
*/
protected array $_aDoc = [
'name' => 'Plugin Mysqlconnect',
'description' => 'Verify a database connection with mysqli real connect function.',
'parameters' => [
'server' => [
'type' => 'string',
'required' => true,
'description' => 'Hostname/ ip of mysql server',
'validate' => 'hostname',
// doc
'default' => null,
'example' => 'localhost',
],
'port' => [
'type' => 'int',
'required' => false,
'description' => 'port of mysql server',
'validate' => 'portnumber',
'default' => 5,
'example' => '3',
],
'user' => [
'type' => 'string',
'required' => false,
'description' => 'Database user to connect with',
'default' => null,
'example' => 'dbuser',
],
'password' => [
'type' => 'string',
'required' => false,
'description' => 'Password of the database user to authenticate',
'default' => null,
'example' => 'mySecretDatabasePassword',
],
'db' => [
'type' => 'string',
'required' => false,
'description' => 'Database name to connect',
'default' => null,
'example' => 'wordpress',
],
'timeout' => [
'type' => 'float',
'required' => false,
'description' => 'Timeout in sec',
'default' => 5,
'example' => '3',
],
],
];
/**
* Get default group of this check
* @return string
......@@ -59,14 +120,22 @@ class checkMysqlConnect extends appmonitorcheck
return [RESULT_ERROR, 'ERROR: setting mysqli_options failed.'];
}
$db = (isset($aParams["port"]) && $aParams["port"])
? $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"], $aParams["port"])
: $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"])
;
if ($db) {
$mysqli->close();
return [RESULT_OK, "OK: Mysql database " . $aParams["db"] . " was connected"];
} else {
try{
$db = (isset($aParams["port"]) && $aParams["port"])
? $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"], $aParams["port"])
: $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"])
;
if ($db) {
$mysqli->close();
return [RESULT_OK, "OK: Mysql database " . $aParams["db"] . " was connected"];
} else {
return [
RESULT_ERROR,
"ERROR: Mysql database " . $aParams["db"] . " was not connected. Error " . mysqli_connect_errno() . ": " . mysqli_connect_error()
];
}
} catch (Exception $e) {
return [
RESULT_ERROR,
"ERROR: Mysql database " . $aParams["db"] . " was not connected. Error " . mysqli_connect_errno() . ": " . mysqli_connect_error()
......
......@@ -19,10 +19,52 @@
*
* 2021-10-27 <axel.hahn@iml.unibe.ch>
* 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables
*
* 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description
*/
class checkPdoConnect extends appmonitorcheck
{
/**
* Self documentation and validation rules
* @var array
*/
protected array $_aDoc = [
'name' => 'Plugin PdoConnect',
'description' => 'Verify a database connection with PDO connect',
'parameters' => [
'connect' => [
'type' => 'string',
'required' => true,
'description' => 'PDO conect string. See http://php.net/manual/en/pdo.drivers.php',
// doc
'default' => null,
'example' => 'mysql:host=$aDb[server];port=3306;dbname=$aDb[database]',
],
'user' => [
'type' => 'string',
'required' => false,
'description' => 'Database user to connect with',
'default' => null,
'example' => 'dbuser',
],
'password' => [
'type' => 'string',
'required' => false,
'description' => 'Password of the database user to authenticate',
'default' => null,
'example' => 'mySecretDatabasePassword',
],
'timeout' => [
'type' => 'float',
'required' => false,
'description' => 'Timeout in sec',
'default' => 5,
'example' => '3',
],
],
];
/**
* Get default group of this check
* @return string
......@@ -31,6 +73,7 @@ class checkPdoConnect extends appmonitorcheck
{
return 'database';
}
/**
* Check connection to a database using pdo
* see http://php.net/manual/en/pdo.drivers.php
......
......@@ -19,10 +19,37 @@
*
* 2022-05-06 <axel.hahn@iml.unibe.ch> first lines
* 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables
*
* 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description
*/
class checkPhpmodules extends appmonitorcheck
{
/**
* Self documentation and validation rules
* @var array
*/
protected array $_aDoc = [
'name' => 'Plugin Phpmodules',
'description' => 'Check loaded php modules',
'parameters' => [
'required' => [
'type' => 'array',
'required' => true,
'description' => 'List of php modules that are required',
// doc
'default' => [],
'example' => '["curl", "PDO"]',
],
'optional' => [
'type' => 'array',
'required' => false,
'description' => 'List of php modules that are optional. If one is missing, the status is set to warning.',
'default' => [],
'example' => '["gd"]',
],
],
];
/**
* Get default group of this check
* @return string
......