From 99bbf9ed839d00e01dad20b4623340540fc0d23f Mon Sep 17 00:00:00 2001 From: Frank Brendel Date: Fri, 1 Mar 2019 13:05:09 +0100 Subject: [PATCH] monit: add migration/validation for service/test type dependency --- .../OPNsense/Monit/Migrations/M1_0_8.php | 45 +++++ .../mvc/app/models/OPNsense/Monit/Monit.php | 159 +++++++++++++++++- .../mvc/app/models/OPNsense/Monit/Monit.xml | 7 + 3 files changed, 203 insertions(+), 8 deletions(-) create mode 100644 src/opnsense/mvc/app/models/OPNsense/Monit/Migrations/M1_0_8.php diff --git a/src/opnsense/mvc/app/models/OPNsense/Monit/Migrations/M1_0_8.php b/src/opnsense/mvc/app/models/OPNsense/Monit/Migrations/M1_0_8.php new file mode 100644 index 000000000..4e779dff7 --- /dev/null +++ b/src/opnsense/mvc/app/models/OPNsense/Monit/Migrations/M1_0_8.php @@ -0,0 +1,45 @@ +getNodeByReference('test')->__items as $test) { + $test->type = $model->getTestType($test->condition->getNodeData()); + } + // validation will fail because we want to change the type of tests linked to services + $model->serializeToConfig(false, true); + Config::getInstance()->save(); + } +} diff --git a/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.php b/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.php index 69610ace8..f430cf000 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.php +++ b/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.php @@ -1,7 +1,7 @@ ['Existence', 'ProcessResource', 'ProcessDiskIO', 'UID', 'GID', 'PID', 'PPID', 'Uptime', 'Connection', 'Custom'], 'file' => ['Existence', 'FileChecksum', 'Timestamp', 'FileSize', @@ -56,6 +56,96 @@ class Monit extends BaseModel 'network' => ['NetworkInterface', 'Custom'] ]; + /** + * array with condition patterns for test types + */ + private $conditionPatterns = [ + 'Existence' => [ + 'exist', 'not exist' + ], + 'SystemResource' => [ + 'loadavg (1min)', 'loadavg (5min)', 'loadavg (15min)', 'cpu usage', + 'cpu user usage', 'cpu system usage', 'cpu wait usage', 'memory usage', + 'swap usage' + ], + 'ProcessResource' => [ + 'cpu', 'total cpu', 'threads', 'children', 'memory usage', + 'total memory usage' + ], + 'ProcessDiskIO' => [ + 'disk read rate', 'disk write rate' + ], + 'FileChecksum' => [ + 'failed md5 checksum', 'changed md5 checksum', 'failed checksum expect' + ], + 'Timestamp' => [ + 'access time', 'modification time', 'change time', 'timestamp', + 'changed access time', 'changed modification time', 'changed change time', + 'changed timestamp' + ], + 'FileSize' => [ + 'size', 'changed size' + ], + 'FileContent' => [ + 'content =', 'content !=' + ], + 'FilesystemMountFlags' => [ + 'changed fsflags' + ], + 'SpaceUsage' => [ + 'space', 'space free' + ], + 'InodeUsage' => [ + 'inodes', + 'inodes free' + ], + 'DiskIO' => [ + 'read rate', + 'write rate', + 'service time' + ], + 'Permisssion' => [ + 'failed permission', + 'changed permission' + ], + 'UID' => [ + 'failed uid' + ], + 'GID' => [ + 'failed uid' + ], + 'PID' => [ + 'changed pid' + ], + 'PPID' => [ + 'changed ppid' + ], + 'Uptime' => [ + 'uptime' + ], + 'ProgramStatus' => [ + 'status', + 'changed status' + ], + 'NetworkInterface' => [ + 'failed link', + 'changed link capacity', + 'saturation', + 'upload', + 'download' + ], + 'NetworkPing' => [ + 'failed ping', + 'failed ping4', + 'failed ping6' + ], + 'Connection' => [ + 'failed host', + 'failed port', + 'failed unixsocket' + ], + 'Custom' => [] + ]; /** * validate full model using all fields and data in a single (1 deep) array @@ -77,26 +167,44 @@ class Monit extends BaseModel // test node validations switch ($node->getInternalXMLTagName()) { case 'type': + // only 'Custom' is allowed if test is linked to a service $testUuid = $parentNode->getAttribute('uuid'); - if ($node->isFieldChanged() && $this->isTestServiceRelated($testUuid)) { + if (strcmp((string)$node, 'Custom') != 0 && + $node->isFieldChanged() && + $this->isTestServiceRelated($testUuid)) { $messages->appendMessage(new \Phalcon\Validation\Message( gettext("Cannot change the type. Test is linked to a service."), $key )); } break; + case 'condition': + // only 'Custom' or the same test type (from condition) allowed if test is linked to a service + $type = $this->getTestType((string)$node); + if (strcmp($type, 'Custom') != 0 && + strcmp((string)$parentNode->type, $type) != 0 && + $this->isTestServiceRelated($parentNode->getAttribute('uuid'))) { + $messages->appendMessage(new \Phalcon\Validation\Message( + gettext("Condition would change the type of the test but it is linked to a service."), + $key + )); + } else { + // set the test tytpe according to the condition + $parentNode->type = $type; + } + break; } break; case 'service': // service node validations switch ($node->getInternalXMLTagName()) { case 'tests': - // test dependencies defined in $this->testSyntax + // test dependencies defined in $this->serviceTestMapping foreach (explode(',', (string)$parentNode->tests) as $testUUID) { $test = $this->getNodeByReference('test.' . $testUUID); if ($test != null) { - if (!empty($this->testSyntax[(string)$parentNode->type])) { - $options = $this->testSyntax[(string)$parentNode->type]; + if (!empty($this->serviceTestMapping[(string)$parentNode->type])) { + $options = $this->serviceTestMapping[(string)$parentNode->type]; if (!in_array((string)$test->type, $options)) { $validationMsg = sprintf( gettext("Test %s with type %s not allowed for this service type"), @@ -182,7 +290,6 @@ class Monit extends BaseModel return parent::serializeToConfig($validateFullModel, $disable_validation); } - /** * get configuration state * @return bool @@ -216,4 +323,40 @@ class Monit extends BaseModel } return false; } + + /** + * get test type from condition string + * @param condition string + * @return string + */ + public function getTestType($condition) + { + $condition = preg_replace('/\s\s+/', ' ', $condition); + $keyLength = 0; + $foundOperand = ''; + $foundTestType = 'Custom'; + + foreach ($this->conditionPatterns as $testType => $operandList) { + // find the operand for this condition using the longest match + foreach ($operandList as $operand) { + $operandLength = strlen($operand); + if (!strncmp($condition, $operand, $operandLength) && + $operandLength > $keyLength) { + $keyLength = $operandLength; + $foundOperand = $operand; + $foundTestType = $testType; + } + } + } + + // 'memory usage' can be ambiguous but 'percent' unit makes it clear + if (strcmp('memory usage', $foundOperand) == 0) { + if (preg_match('/^.*\spercent|%\s*$/', $condition)) { + $foundTestType = 'SystemResource'; + } else { + $foundTestType = 'ProcessResource'; + } + } + return $foundTestType; + } } diff --git a/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.xml b/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.xml index ea290ca70..c4e900fc2 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.xml @@ -186,6 +186,12 @@ Y /^([0-9a-zA-Z\._\-\$]){1,255}$/u Should be a string between 1 and 255 characters. Allowed characters are letters and numbers as well as underscore, minus, dot and the dollar sign. + + + Service name must be unique. + UniqueConstraint + + Y @@ -298,6 +304,7 @@ Program Status Network Interface Network Ping + Connection Custom