From df8d12c8a0e8abb4d3d180f56cc6129b237cb505 Mon Sep 17 00:00:00 2001 From: Ad Schellevis Date: Thu, 17 Jan 2019 15:06:55 +0100 Subject: [PATCH] Monit, major cleanup, related to https://github.com/opnsense/core/pull/3119 - remove locking in model, core framework should do that if required. (see Config->lock), deadlock issues in previous code (freezing UI) - in settings controller, expand endpoints to one per type (in stead of "one to rule them all") - in settings controller, align to base wrappers of ApiMutableModelControllerBase - move extended validations to model, to avoid validation issues when another service would like to reuse the model - align forms to standard wrappers (naming convention, simplifies code) - remove "deleteRelations", if we need something like this at some point, it should be in the model, for now it seems safe enough to ditch - ditch migration code, since it does the same as having a default value (defaults are always enforced during migrations) ** left UnitTest code for what it is now, probably needs changes too --- .../OPNsense/Monit/Api/ServiceController.php | 12 +- .../OPNsense/Monit/Api/SettingsController.php | 594 ++++++------------ .../OPNsense/Monit/forms/alerts.xml | 14 +- .../OPNsense/Monit/forms/services.xml | 28 +- .../OPNsense/Monit/forms/tests.xml | 10 +- .../OPNsense/Monit/Migrations/M1_0_8.php | 44 -- .../mvc/app/models/OPNsense/Monit/Monit.php | 205 +++--- .../mvc/app/models/OPNsense/Monit/Monit.xml | 1 + .../mvc/app/views/OPNsense/Monit/index.volt | 393 +----------- 9 files changed, 388 insertions(+), 913 deletions(-) delete mode 100644 src/opnsense/mvc/app/models/OPNsense/Monit/Migrations/M1_0_8.php diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/ServiceController.php b/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/ServiceController.php index afd0bfa88..765379433 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/ServiceController.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/ServiceController.php @@ -2,7 +2,7 @@ /** * Copyright (C) 2017-2018 EURO-LOG AG - * + * Copyright (c) 2019 Deciso B.V. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,7 +32,6 @@ namespace OPNsense\Monit\Api; use \OPNsense\Base\ApiMutableServiceControllerBase; use \OPNsense\Core\Backend; -use \OPNsense\Monit\Monit; /** * Class ServiceController @@ -45,15 +44,6 @@ class ServiceController extends ApiMutableServiceControllerBase protected static $internalServiceTemplate = 'OPNsense/Monit'; protected static $internalServiceName = 'monit'; - /** - * initialize object properties - */ - public function onConstruct() - { - // initialize the model via parent to avoid selflock by circular references - $this->getModel(); - } - /** * test monit configuration * @return array diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/SettingsController.php b/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/SettingsController.php index 6829a57e0..714dc75e3 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/SettingsController.php +++ b/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/SettingsController.php @@ -2,7 +2,7 @@ /** * Copyright (C) 2017-2019 EURO-LOG AG - * + * Copyright (c) 2019 Deciso B.V. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,163 +30,18 @@ namespace OPNsense\Monit\Api; -use \OPNsense\Base\ApiControllerBase; +use \OPNsense\Base\ApiMutableModelControllerBase; use \OPNsense\Core\Config; -use \OPNsense\Monit\Monit; use \OPNsense\Base\UIModelGrid; /** * Class SettingsController * @package OPNsense\Monit */ -class SettingsController extends ApiControllerBase +class SettingsController extends ApiMutableModelControllerBase { - - /** - * @var null|object the monit model object - */ - public $mdlMonit = null; - - /** - * @var array list with valid model node types - */ - private $nodeTypes = array('general', 'alert', 'service', 'test'); - - /** - * @var array with syntax information to build test conditions - */ - private $testSyntax = [ - 'serviceTestMapping' => [ - 'process' => ['Existence', 'Process Resource', 'Process Disk I/O', 'UID', 'GID', 'PID', 'PPID', 'Uptime', 'Connection', 'Custom'], - 'file' => ['Existence', 'File Checksum', 'Timestamp', 'File Size', 'File Content', 'Permisssion', 'UID', 'GID', 'Custom'], - 'fifo' => ['Existence', 'Timestamp', 'Permisssion', 'UID', 'GID', 'Custom'], - 'filesystem' => ['Existence', 'Filesystem Mount Flags', 'Space Usage', 'Inode Usage', 'Disk I/O', 'Permisssion', 'Custom'], - 'directory' => ['Existence', 'Timestamp', 'Permisssion', 'UID', 'GID', 'Custom'], - 'host' => ['Network Ping', 'Connection', 'Custom'], - 'system' => ['System Resource', 'Uptime', 'Custom'], - 'custom' => ['Program Status', 'Custom'], - 'network' => ['Network Interface', 'Custom'] - ], - 'operators' => ['greater', 'less', 'equal', 'notequal'], - 'units' => [ - 'timeUnits' => ['seconds', 'minutes', 'hours', 'days'], - 'amountUnits' => ['byte', 'kilobyte', 'megabyte', 'gigabyte'], - 'spaceAmountUnits' => ['byte', 'kilobyte', 'megabyte', 'gigabyte', 'percent'], - 'diskAmountUnits' => ['byte', 'kilobyte', 'megabyte', 'gigabyte', 'operations'], - 'netAmountUnits' => ['byte', 'kilobyte', 'megabyte', 'gigabyte', 'packets'], - ], - 'testConditionMapping' => [ - 'Existence' => [ - 'exist', - 'not exist' - ], - 'System Resource' => [ - 'loadavg (1min)' => ['_OPERATOR' => ['_VALUE' => null]], - 'loadavg (5min)' => ['_OPERATOR' => ['_VALUE' => null]], - 'loadavg (15min)' => ['_OPERATOR' => ['_VALUE' => null]], - 'cpu usage' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'percent']], - 'cpu user usage' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'percent']], - 'cpu system usage' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'percent']], - 'cpu wait usage' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'percent']], - 'memory usage' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'percent']], - 'swap usage' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'percent']] - ], - 'Process Resource' => [ - 'cpu' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'percent']], - 'total cpu' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'percent']], - 'threads' => ['_OPERATOR' => ['_VALUE' => null]], - 'children' => ['_OPERATOR' => ['_VALUE' => null]], - 'memory usage' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'amountUnits']], - 'total memory usage' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'percent']] - ], - 'Process Disk I/O' => [ - 'disk read rate' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'operations', '_RATE' => '/s']], - 'disk write rate' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'operations', '_RATE' => '/s']] - ], - 'File Checksum' => [ - 'failed md5 checksum', - 'changed md5 checksum', - 'failed checksum expect' => ['_VALUE' => null] - ], - 'Timestamp' => [ - 'access time' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'timeUnits']], - 'modification time' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'timeUnits']], - 'change time' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'timeUnits']], - 'timestamp' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'timeUnits']], - 'changed access time', - 'changed modification time', - 'changed change time', - 'changed timestamp' - ], - 'File Size' => [ - 'size' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'amountUnits']], - 'changed size' - ], - 'File Content' => [ - 'content =' => ['_VALUE' => null], - 'content !=' => ['_VALUE' => null] - ], - 'Filesystem Mount Flags' => [ - 'changed fsflags' - ], - 'Space Usage' => [ - 'space' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'spaceAmountUnits']], - 'space free' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'spaceAmountUnits']] - ], - 'Inode Usage' => [ - 'inodes' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'amountUnits']], - 'inodes free' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'amountUnits']] - ], - 'Disk I/O' => [ - 'read rate' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'diskAmountUnits', '_RATE' => '/s']], - 'write rate' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'diskAmountUnits', '_RATE' => '/s']], - 'service time' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'millisecond']] - ], - 'Permisssion' => [ - 'failed permission' => ['_VALUE' => null], - 'changed permission' - ], - 'UID' => [ - 'failed uid' => ['_VALUE' => null] - ], - 'GID' => [ - 'failed uid' => ['_VALUE' => null] - ], - 'PID' => [ - 'changed pid' - ], - 'PPID' => [ - 'changed ppid' - ], - 'Uptime' => [ - 'uptime' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'timeUnits']] - ], - 'Program Status' => [ - 'status' => ['_OPERATOR' => ['_VALUE' => null]], - 'changed status' - ], - 'Network Interface' => [ - 'failed link', - 'changed link capacity', - 'saturation' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'percent']], - 'upload' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'netAmountUnits', '_RATE' => '/s']], - 'download' => ['_OPERATOR' => ['_VALUE' => null, '_UNIT' => 'netAmountUnits', '_RATE' => '/s']], - ], - 'Network Ping' => [ - 'failed ping4', - 'failed ping6' - ], - 'Custom' => null - ] - ]; - - /** - * initialize object properties - */ - public function onConstruct() - { - $this->mdlMonit = new Monit(); - } + protected static $internalModelName = 'monit'; + protected static $internalModelClass = 'OPNsense\Monit\Monit'; /** * check if changes to the monit settings were made @@ -195,224 +50,216 @@ class SettingsController extends ApiControllerBase public function dirtyAction() { $result = array('status' => 'ok'); - $result['monit']['dirty'] = $this->mdlMonit->configChanged(); + $result['monit']['dirty'] = $this->getModel()->configChanged(); return $result; } /** - * query monit settings - * @param $nodeType - * @param $uuid - * @param $syntax - * @return array result - * @throws \Exception + * Retrieve alert settings or return defaults + * @param $uuid item unique id + * @return array monit alert content + * @throws \ReflectionException when not bound to model */ - public function getAction($nodeType = null, $uuid = null, $syntax = null) + public function getAlertAction($uuid = null) { - $result = array("result" => "failed"); - if ($syntax != null) { - $result['syntax'] = $this->testSyntax; - $result['result'] = 'ok'; - return $result; - } - if ($this->request->isGet() && $nodeType != null) { - $this->validateNodeType($nodeType); - if ($nodeType == 'general') { - $node = $this->mdlMonit->getNodeByReference($nodeType); - } else { - if ($uuid != null) { - $node = $this->mdlMonit->getNodeByReference($nodeType . '.' . $uuid); - } else { - $node = $this->mdlMonit->$nodeType->Add(); - } - } - if ($node != null) { - $result['monit'] = array($nodeType => $node->getNodes()); - $result['result'] = 'ok'; - } - } - return $result; + return $this->getBase("alert", "alert", $uuid); } /** - * set monit properties - * @param $nodeType - * @param $uuid - * @return array status - * @throws \Exception + * Update alert with given properties + * @param string $uuid internal id + * @return array save result + validation output + * @throws \Phalcon\Validation\Exception when field validations fail + * @throws \ReflectionException when not bound to model */ - public function setAction($nodeType = null, $uuid = null) + public function setAlertAction($uuid) { - $result = array("result" => "failed", "validations" => array()); - if ($this->request->isPost() && $this->request->hasPost("monit") && $nodeType != null) { - $this->validateNodeType($nodeType); - if ($nodeType == 'general') { - $node = $this->mdlMonit->getNodeByReference($nodeType); - } else { - if ($uuid != null) { - $node = $this->mdlMonit->getNodeByReference($nodeType . '.' . $uuid); - } else { - $node = $this->mdlMonit->$nodeType->Add(); - } - } - if ($node != null) { - $monitInfo = $this->request->getPost("monit"); - - // perform plugin specific validations - if ($nodeType == 'service') { - $tests = explode(',', $monitInfo[$nodeType]['tests']); - foreach ($tests as $testUUID) { - $test = $this->mdlMonit->getNodeByReference('test.' . $testUUID); - if ($test != null) { - $testName = $test->name->__toString(); - $testType = $test->type->getNodeData()[$test->type->__toString()]['value']; - if (array_search($testType, $this->testSyntax['serviceTestMapping'][$monitInfo[$nodeType]['type']]) === false) { - $result["validations"]['monit.service.tests'] = "Test " . $testName . ' with type ' . $testType . ' not allowed for service type ' . $monitInfo[$nodeType]['type']; - } - } - } - switch ($monitInfo[$nodeType]['type']) { - case 'process': - if (empty($monitInfo[$nodeType]['pidfile']) && empty($monitInfo[$nodeType]['match'])) { - $result["validations"]['monit.service.pidfile'] = "Please set at least one of Pidfile or Match."; - $result["validations"]['monit.service.match'] = $result["validations"]['monit.service.pidfile']; - } - break; - case 'host': - if (empty($monitInfo[$nodeType]['address'])) { - $result["validations"]['monit.service.address'] = "Address is mandatory for 'Remote Host' checks."; - } - break; - case 'network': - if (empty($monitInfo[$nodeType]['address']) && empty($monitInfo[$nodeType]['interface'])) { - $result["validations"]['monit.service.address'] = "Please set at least one of Address or Interface."; - $result["validations"]['monit.service.interface'] = $result["validations"]['monit.service.address']; - } - break; - case 'system': - break; - default: - if (empty($monitInfo[$nodeType]['path'])) { - $result["validations"]['monit.service.path'] = "Path is mandatory."; - } - } - } - - $node->setNodes($monitInfo[$nodeType]); - $valMsgs = $this->mdlMonit->performValidation(); - foreach ($valMsgs as $field => $msg) { - $fieldnm = str_replace($node->__reference, "monit." . $nodeType, $msg->getField()); - $result["validations"][$fieldnm] = $msg->getMessage(); - } - if (empty($result["validations"])) { - unset($result["validations"]); - $result['result'] = 'ok'; - $this->mdlMonit->serializeToConfig(); - Config::getInstance()->save(); - if ($this->mdlMonit->configDirty()) { - $result['status'] = 'ok'; - } - } - } - } - return $result; + return $this->setBase("alert", "alert", $uuid); } /** - * delete monit settings - * @param $nodeType - * @param $uuid - * @return array status - * @throws \Exception + * Add alert with given properties + * @return array save result + validation output + * @throws \Phalcon\Validation\Exception when field validations fail + * @throws \ReflectionException when not bound to model */ - public function delAction($nodeType = null, $uuid = null) + public function addAlertAction() { - $result = array("result" => "failed"); - if ($nodeType != null) { - $this->validateNodeType($nodeType); - if ($uuid != null) { - $node = $this->mdlMonit->getNodeByReference($nodeType . '.' . $uuid); - if ($node != null) { - if ($this->mdlMonit->$nodeType->del($uuid) == true) { - // delete relations - if ($nodeType == 'test') { - $nodeName = $this->mdlMonit->getNodeByReference($nodeType . '.' . $uuid . '.name'); - if ($nodeName != null) { - $nodeName = $nodeName->__toString(); - $this->deleteRelations('service', 'tests', $uuid, 'test', $nodeName, $this->mdlMonit); - } - } - $this->mdlMonit->serializeToConfig(); - Config::getInstance()->save(); - if ($this->mdlMonit->configDirty()) { - $result['status'] = 'ok'; - } - } - } - } - } - return $result; + return $this->addBase("alert", "alert"); } /** - * toggle monit items (enable/disable) - * @param $nodeType - * @param $uuid - * @return array result + * Delete alert by uuid + * @param string $uuid internal id + * @return array save status + * @throws \Phalcon\Validation\Exception when field validations fail + * @throws \ReflectionException when not bound to model */ - public function toggleAction($nodeType = null, $uuid = null) + public function delAlertAction($uuid) { - $result = array("result" => "failed"); - if ($this->request->isPost() && $nodeType != null) { - if ($uuid != null) { - $node = $this->mdlMonit->getNodeByReference($nodeType . '.' . $uuid); - if ($node != null) { - if ($node->enabled->__toString() == "1") { - $node->enabled = "0"; - } else { - $node->enabled = "1"; - } - $this->mdlMonit->serializeToConfig(); - Config::getInstance()->save(); - if ($this->mdlMonit->configDirty()) { - $result['status'] = 'ok'; - } - } else { - $result['result'] = "not found"; - } - } else { - $result['result'] = "uuid not given"; - } - } - return $result; + return $this->delBase("alert", $uuid); } /** - * search monit settings - * @param $nodeType - * @return array result - * @throws \Exception + * Search monit alerts + * @return array list of found alerts + * @throws \ReflectionException when not bound to model */ - public function searchAction($nodeType = null) + public function searchAlertAction() { - $this->sessionClose(); - if ($this->request->isPost() && $nodeType != null) { - $this->validateNodeType($nodeType); - $grid = new UIModelGrid($this->mdlMonit->$nodeType); - $fields = array(); - switch ($nodeType) { - case 'alert': - $fields = array("enabled", "recipient", "noton", "events", "description"); - break; - case 'service': - $fields = array("enabled", "name", "type"); - break; - case 'test': - $fields = array("name", "type", "condition", "action"); - break; - } - return $grid->fetchBindRequest($this->request, $fields); - } + return $this->searchBase( + "alert", + array("enabled", "recipient", "noton", "events", "description"), + "description" + ); + } + + /** + * Toggle alert defined by uuid (enable/disable) + * @param $uuid alert internal id + * @param $enabled desired state enabled(1)/disabled(1), leave empty for toggle + * @return array save result + * @throws \Phalcon\Validation\Exception when field validations fail + * @throws \ReflectionException when not bound to model + */ + public function toggleAlertAction($uuid, $enabled = null) + { + return $this->toggleBase("alert", $uuid, $enabled); + } + + /** + * Retrieve service settings or return defaults + * @param $uuid item unique id + * @return array monit service content + * @throws \ReflectionException when not bound to model + */ + public function getServiceAction($uuid = null) + { + return $this->getBase("service", "service", $uuid); + } + + /** + * Update service with given properties + * @param string $uuid internal id + * @return array save result + validation output + * @throws \Phalcon\Validation\Exception when field validations fail + * @throws \ReflectionException when not bound to model + */ + public function setServiceAction($uuid) + { + return $this->setBase("service", "service", $uuid); + } + + /** + * Add service with given properties + * @return array save result + validation output + * @throws \Phalcon\Validation\Exception when field validations fail + * @throws \ReflectionException when not bound to model + */ + public function addServiceAction() + { + return $this->addBase("service", "service"); + } + + /** + * Delete service by uuid + * @param string $uuid internal id + * @return array save status + * @throws \Phalcon\Validation\Exception when field validations fail + * @throws \ReflectionException when not bound to model + */ + public function delServiceAction($uuid) + { + return $this->delBase("service", $uuid); + } + + /** + * Search monit services + * @return array list of found services + * @throws \ReflectionException when not bound to model + */ + public function searchServiceAction() + { + return $this->searchBase("service", array("enabled", "name", "type"), "name"); + } + + /** + * Toggle service defined by uuid (enable/disable) + * @param $uuid service internal id + * @param $enabled desired state enabled(1)/disabled(1), leave empty for toggle + * @return array save result + * @throws \Phalcon\Validation\Exception when field validations fail + * @throws \ReflectionException when not bound to model + */ + public function toggleServiceAction($uuid, $enabled = null) + { + return $this->toggleBase("service", $uuid, $enabled); + } + + /** + * Retrieve test settings or return defaults + * @param $uuid item unique id + * @return array monit test content + * @throws \ReflectionException when not bound to model + */ + public function getTestAction($uuid = null) + { + return $this->getBase("test", "test", $uuid); + } + + /** + * Update test with given properties + * @param string $uuid internal id + * @return array save result + validation output + * @throws \Phalcon\Validation\Exception when field validations fail + * @throws \ReflectionException when not bound to model + */ + public function setTestAction($uuid) + { + return $this->setBase("test", "test", $uuid); + } + + /** + * Add test with given properties + * @return array save result + validation output + * @throws \Phalcon\Validation\Exception when field validations fail + * @throws \ReflectionException when not bound to model + */ + public function addTestAction() + { + return $this->addBase("test", "test"); + } + + /** + * Delete test by uuid + * @param string $uuid internal id + * @return array save status + * @throws \Phalcon\Validation\Exception when field validations fail + * @throws \ReflectionException when not bound to model + */ + public function delTestAction($uuid) + { + return $this->delBase("test", $uuid); + } + + /** + * Search monit services + * @return array list of found services + * @throws \ReflectionException when not bound to model + */ + public function searchTestAction() + { + return $this->searchBase("test", array("name", "condition", "action"), "name"); + } + + /** + * Retrieve general settings + * @return array monit general settings content + * @throws \ReflectionException when not bound to model + */ + public function getGeneralAction() + { + return ['monit' => $this->getModel()->general->getNodes(), 'result' => 'ok']; } /** @@ -427,7 +274,7 @@ class SettingsController extends ApiControllerBase $cfg = Config::getInstance(); $cfgObj = $cfg->object(); - $node = $this->mdlMonit->getNodeByReference('general'); + $node = $this->getModel()->getNodeByReference('general'); $generalSettings = array(); // inherit SMTP settings from System->Settings->Notifications @@ -448,72 +295,19 @@ class SettingsController extends ApiControllerBase // apply them $node->setNodes($generalSettings); - $valMsgs = $this->mdlMonit->performValidation(); + $valMsgs = $this->getModel()->performValidation(); foreach ($valMsgs as $field => $msg) { $fieldnm = str_replace($node->__reference, "monit.general.", $msg->getField()); $result["validations"][$fieldnm] = $msg->getMessage(); } if (empty($result["validations"])) { unset($result["validations"]); - $this->mdlMonit->serializeToConfig(); + $this->getModel()->serializeToConfig(); Config::getInstance()->save(); - if ($this->mdlMonit->configDirty()) { - $result['status'] = 'ok'; - $result['result'] = 'OK'; - } + $result['status'] = 'ok'; + $result['result'] = 'OK'; } } return $result; } - - /** - * validate nodeType - * @param $nodeType - * @throws \Exception - */ - private function validateNodeType($nodeType = null) - { - if (array_search($nodeType, $this->nodeTypes) === false) { - throw new \Exception('unknown nodeType: ' . $nodeType); - } - } - - /** - * delete relations - * @param string|null $nodeType - * @param string|null $nodeField - * @param string|null $relUuid - * @param string|null $relNodeType - * @param string|null $relNodeName - * @throws \Exception - */ - private function deleteRelations( - $nodeType = null, - $nodeField = null, - $relUuid = null, - $relNodeType = null, - $relNodeName = null - ) { - $nodes = $this->mdlMonit->$nodeType->getNodes(); - // get nodes with relations - foreach ($nodes as $nodeUuid => $node) { - // get relation uuids - foreach ($node[$nodeField] as $fieldUuid => $field) { - // remove uuid from field - if ($fieldUuid == $relUuid) { - $refField = $nodeType . '.' . $nodeUuid . '.' . $nodeField; - $relNode = $this->mdlMonit->getNodeByReference($refField); - $nodeRels = str_replace($relUuid, '', $relNode->__toString()); - $nodeRels = str_replace(',,', ',', $nodeRels); - $nodeRels = rtrim($nodeRels, ','); - $nodeRels = ltrim($nodeRels, ','); - $this->mdlMonit->setNodeByReference($refField, $nodeRels); - if ($relNode->isEmptyAndRequired()) { - $nodeName = $this->mdlMonit->getNodeByReference($nodeType . '.' . $nodeUuid . '.name')->__toString(); - throw new \Exception("Cannot delete $relNodeType '$relNodeName' from $nodeType '$nodeName'"); - } - } - } - } - } } diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/alerts.xml b/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/alerts.xml index 867465487..2b7a64ff1 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/alerts.xml +++ b/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/alerts.xml @@ -1,43 +1,43 @@
- monit.alert.enabled + alert.enabled checkbox - monit.alert.recipient + alert.recipient text - monit.alert.noton + alert.noton checkbox - monit.alert.events + alert.events select_multiple 28 - monit.alert.format + alert.format textbox Subject: $SERVICE on $HOST failed]]> - monit.alert.reminder + alert.reminder text - monit.alert.description + alert.description text diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/services.xml b/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/services.xml index 30c65eaf9..1975364d1 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/services.xml +++ b/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/services.xml @@ -1,85 +1,85 @@ - monit.service.enabled + service.enabled checkbox - monit.service.name + service.name text - monit.service.type + service.type dropdown - monit.service.pidfile + service.pidfile text - monit.service.match + service.match text monit procmatch <PATTERN>]]> - monit.service.path + service.path text - monit.service.timeout + service.timeout text true - monit.service.address + service.address text - monit.service.interface + service.interface dropdown - monit.service.start + service.start text - monit.service.stop + service.stop text - monit.service.tests + service.tests select_multiple - monit.service.depends + service.depends select_multiple - monit.service.description + service.description text diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/tests.xml b/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/tests.xml index a328a99d6..8aed9bcb8 100644 --- a/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/tests.xml +++ b/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/tests.xml @@ -1,30 +1,30 @@ - monit.test.name + test.name text - monit.test.type + test.type dropdown - monit.test.condition + test.condition text cpu is greater than 50%
failed host 127.0.0.1 port 22 protocol ssh
]]>
- monit.test.action + test.action dropdown alert or restart the service or execute a program.]]> - monit.test.path + test.path text 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 deleted file mode 100644 index ea84ef767..000000000 --- a/src/opnsense/mvc/app/models/OPNsense/Monit/Migrations/M1_0_8.php +++ /dev/null @@ -1,44 +0,0 @@ -getNodeByReference('test')->__items as $test) { - $test->type = 'Custom'; - } - } -} - diff --git a/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.php b/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.php index d74a0f49c..9ca5a5176 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.php +++ b/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.php @@ -1,32 +1,32 @@ ['Existence', 'Process Resource', 'Process Disk I/O', + 'UID', 'GID', 'PID', 'PPID', 'Uptime', 'Connection', 'Custom'], + 'file' => ['Existence', 'File Checksum', 'Timestamp', 'File Size', + 'File Content', 'Permisssion', 'UID', 'GID', 'Custom'], + 'fifo' => ['Existence', 'Timestamp', 'Permisssion', 'UID', 'GID', 'Custom'], + 'filesystem' => ['Existence', 'Filesystem Mount Flags', + 'Space Usage', 'Inode Usage', 'Disk I/O', 'Permisssion', 'Custom'], + 'directory' => ['Existence', 'Timestamp', 'Permisssion', 'UID', 'GID', 'Custom'], + 'host' => ['Network Ping', 'Connection', 'Custom'], + 'system' => ['System Resource', 'Uptime', 'Custom'], + 'custom' => ['Program Status', 'Custom'], + 'network' => ['Network Interface', 'Custom'] + ]; + /** - * lock the model to avoid issues with concurrent access - * @throws \Exception + * validate full model using all fields and data in a single (1 deep) array + * @param bool $validateFullModel validate full model or only changed fields + * @return \Phalcon\Validation\Message\Group */ - public function __construct($lock = true) - { - if ($lock == true) { - $this->requestLock(); - $this->internalLockHandle = fopen("/tmp/monit.lock", "w+"); - if (is_resource($this->internalLockHandle) == false || flock($this->internalLockHandle, LOCK_EX) == false) { - throw new \Exception("Cannot lock monit model"); + public function performValidation($validateFullModel = false) { + // standard model validations + $messages = parent::performValidation($validateFullModel); + $all_nodes = $this->getFlatNodes(); + foreach ($all_nodes as $key => $node) { + if ($validateFullModel || $node->isFieldChanged()) { + // the item container may have different validations attached. + $parentNode = $node->getParentNode(); + // perform plugin specific validations + switch ($parentNode->getInternalXMLTagName()) { + case 'service': + // service type node validations + switch ($node->getInternalXMLTagName()) { + case 'tests': + // test dependencies defined in $this->testSyntax + 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 (!in_array((string)$test->type, $options)) { + $validationMsg = sprintf( + gettext("Test %s with type %s not allowed for this service type"), + (string)$test->name, + $test->type->getNodeData()[(string)$test->type]['value'] + ); + $messages->appendMessage( + new \Phalcon\Validation\Message($validationMsg, $key) + ); + } + } + } + } + break; + case 'pidfile': + if (empty((string)$node) && (string)$parentNode->type == 'process' + && empty((string)$parentNode->match)) { + $messages->appendMessage(new \Phalcon\Validation\Message( + gettext("Please set at least one of Pidfile or Match."), $key + )); + } + break; + case 'match': + if (empty((string)$node) && (string)$parentNode->type == 'process' + && empty((string)$parentNode->pidfile)) { + $messages->appendMessage(new \Phalcon\Validation\Message( + gettext("Please set at least one of Pidfile or Match."), $key + )); + } + break; + case 'address': + if (empty((string)$node) && (string)$parentNode->type == 'host') { + $messages->appendMessage(new \Phalcon\Validation\Message( + gettext("Address is mandatory for 'Remote Host' checks."), $key + )); + } elseif (empty((string)$node) && (string)$parentNode->type == 'network' + && empty((string)$parentNode->interface) ) { + $messages->appendMessage(new \Phalcon\Validation\Message( + gettext("Please set at least one of Address or Interface."), $key + )); + } + break; + case 'interface': + if (empty((string)$node) && (string)$parentNode->type == 'network' + && empty((string)$parentNode->address) ) { + $messages->appendMessage(new \Phalcon\Validation\Message( + gettext("Please set at least one of Address or Interface."), $key + )); + } + break; + case 'path': + if (empty((string)$node) && in_array((string)$parentNode->type, + ['file', 'fifo', 'filesystem', 'directory'])) { + $messages->appendMessage(new \Phalcon\Validation\Message( + gettext("Path is mandatory."), $key + )); + } + break; + } + break; + default: + break; + } } } - parent::__construct(); + return $messages; } /** - * release lock after usage + * mark configuration as changed when data is pushed back to the config */ - public function __destruct() + public function serializeToConfig($validateFullModel = false, $disable_validation = false) { - $this->releaseLock(); + @touch("/tmp/monit.dirty"); + return parent::performValidation($validateFullModel, $disable_validation); } - /** - * lock the model - */ - public function requestLock() - { - $this->internalLockHandle = fopen("/tmp/monit.lock", "w+"); - if (is_resource($this->internalLockHandle) == false || flock($this->internalLockHandle, LOCK_EX) == false) { - throw new \Exception("Cannot lock monit model"); - } - } - /** - * release lock - */ - public function releaseLock() - { - if (is_resource($this->internalLockHandle)) { - flock($this->internalLockHandle, LOCK_UN); - fclose($this->internalLockHandle); - } - } /** * get configuration state @@ -97,15 +169,6 @@ class Monit extends BaseModel return file_exists("/tmp/monit.dirty"); } - /** - * mark configuration as changed - * @return bool - */ - public function configDirty() - { - return @touch("/tmp/monit.dirty"); - } - /** * mark configuration as consistent with the running config * @return bool diff --git a/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.xml b/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.xml index 6c912c387..f65e27ee6 100644 --- a/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.xml +++ b/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.xml @@ -277,6 +277,7 @@ Y + Custom Existence System Resource diff --git a/src/opnsense/mvc/app/views/OPNsense/Monit/index.volt b/src/opnsense/mvc/app/views/OPNsense/Monit/index.volt index a02bd957b..8c9e12131 100644 --- a/src/opnsense/mvc/app/views/OPNsense/Monit/index.volt +++ b/src/opnsense/mvc/app/views/OPNsense/Monit/index.volt @@ -1,6 +1,7 @@ {# -Copyright © 2017-2019 by EURO-LOG AG +Copyright (c) 2017-2018 by EURO-LOG AG +Copyright (c) 2019 Deciso B.V. All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -28,7 +29,6 @@ POSSIBILITY OF SUCH DAMAGE.