From f2ed6f5d8784cf07790b8d4eb020288b4564c306 Mon Sep 17 00:00:00 2001 From: fbrendel Date: Thu, 2 Mar 2017 09:24:29 +0100 Subject: [PATCH] new plugin Monit (#81) * new plugin Monit * fix missing events in monitrc template --- sysutils/monit/Makefile | 7 + .../monit/src/etc/inc/plugins.inc.d/monit.inc | 48 ++ .../OPNsense/Monit/Api/ServiceController.php | 172 +++++++ .../OPNsense/Monit/Api/SettingsController.php | 463 ++++++++++++++++++ .../OPNsense/Monit/Api/StatusController.php | 134 +++++ .../OPNsense/Monit/IndexController.php | 52 ++ .../OPNsense/Monit/ItemController.php | 40 ++ .../OPNsense/Monit/StatusController.php | 47 ++ .../OPNsense/Monit/forms/alerts.xml | 48 ++ .../OPNsense/Monit/forms/general.xml | 26 + .../OPNsense/Monit/forms/services.xml | 75 +++ .../OPNsense/Monit/forms/tests.xml | 26 + .../mvc/app/models/OPNsense/Monit/ACL/ACL.xml | 10 + .../app/models/OPNsense/Monit/Menu/Menu.xml | 8 + .../mvc/app/models/OPNsense/Monit/Monit.php | 41 ++ .../mvc/app/models/OPNsense/Monit/Monit.xml | 200 ++++++++ .../mvc/app/views/OPNsense/Monit/index.volt | 323 ++++++++++++ .../mvc/app/views/OPNsense/Monit/status.volt | 45 ++ .../scripts/OPNsense/Monit/+POST_INSTALL.post | 1 + .../scripts/OPNsense/Monit/post-install.php | 121 +++++ .../opnsense/scripts/OPNsense/Monit/setup.sh | 5 + .../service/conf/actions.d/actions_monit.conf | 29 ++ .../service/templates/OPNsense/Monit/+TARGETS | 2 + .../service/templates/OPNsense/Monit/monitrc | 79 +++ .../templates/OPNsense/Monit/rc.conf.d | 6 + 25 files changed, 2008 insertions(+) create mode 100644 sysutils/monit/Makefile create mode 100644 sysutils/monit/src/etc/inc/plugins.inc.d/monit.inc create mode 100644 sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/ServiceController.php create mode 100644 sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/SettingsController.php create mode 100644 sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/StatusController.php create mode 100644 sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/IndexController.php create mode 100644 sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/ItemController.php create mode 100644 sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/StatusController.php create mode 100644 sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/alerts.xml create mode 100644 sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/general.xml create mode 100644 sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/services.xml create mode 100644 sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/tests.xml create mode 100644 sysutils/monit/src/opnsense/mvc/app/models/OPNsense/Monit/ACL/ACL.xml create mode 100644 sysutils/monit/src/opnsense/mvc/app/models/OPNsense/Monit/Menu/Menu.xml create mode 100644 sysutils/monit/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.php create mode 100644 sysutils/monit/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.xml create mode 100644 sysutils/monit/src/opnsense/mvc/app/views/OPNsense/Monit/index.volt create mode 100644 sysutils/monit/src/opnsense/mvc/app/views/OPNsense/Monit/status.volt create mode 100755 sysutils/monit/src/opnsense/scripts/OPNsense/Monit/+POST_INSTALL.post create mode 100644 sysutils/monit/src/opnsense/scripts/OPNsense/Monit/post-install.php create mode 100755 sysutils/monit/src/opnsense/scripts/OPNsense/Monit/setup.sh create mode 100644 sysutils/monit/src/opnsense/service/conf/actions.d/actions_monit.conf create mode 100644 sysutils/monit/src/opnsense/service/templates/OPNsense/Monit/+TARGETS create mode 100644 sysutils/monit/src/opnsense/service/templates/OPNsense/Monit/monitrc create mode 100644 sysutils/monit/src/opnsense/service/templates/OPNsense/Monit/rc.conf.d diff --git a/sysutils/monit/Makefile b/sysutils/monit/Makefile new file mode 100644 index 000000000..11462e3dd --- /dev/null +++ b/sysutils/monit/Makefile @@ -0,0 +1,7 @@ +PLUGIN_NAME= monit +PLUGIN_VERSION= 0.1 +PLUGIN_COMMENT= Proactive system monitoring +PLUGIN_MAINTAINER= frank.brendel@eurolog.com +PLUGIN_DEPENDS= monit + +.include "../../Mk/plugins.mk" diff --git a/sysutils/monit/src/etc/inc/plugins.inc.d/monit.inc b/sysutils/monit/src/etc/inc/plugins.inc.d/monit.inc new file mode 100644 index 000000000..fd864f5bf --- /dev/null +++ b/sysutils/monit/src/etc/inc/plugins.inc.d/monit.inc @@ -0,0 +1,48 @@ + gettext('Monit System Monitoring'), + 'configd' => array( + 'restart' => array('monit restart'), + 'start' => array('monit start'), + 'stop' => array('monit stop'), + ), + 'name' => 'monit', + ); + } + return $services; +} diff --git a/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/ServiceController.php b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/ServiceController.php new file mode 100644 index 000000000..6d441feb0 --- /dev/null +++ b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/ServiceController.php @@ -0,0 +1,172 @@ +request->isPost()) { + $this->sessionClose(); + } + $result['function'] = "configtest"; + + $result['template'] = $this->callBackend('template'); + if ($result['template'] != 'OK') + { + $result['result'] = "Template error: " . $result['template']; + return $result; + } + + $result['result'] = $this->callBackend('configtest'); + return $result; + } + + /** + * reload monit with new configuration + * @return array + */ + public function reloadAction() + { + if ($this->request->isPost()) { + $this->sessionClose(); + } + $result['function'] = "reload"; + + $result['template'] = $this->callBackend('template'); + if ($result['template'] != 'OK') + { + $result['result'] = "Template error: " . $result['template']; + return $result; + } + + $status = $this->callBackend('status'); + if (substr($status, 0, 16) != 'monit is running') { + $result['result'] = "Monit is not running"; + return $result; + } + + $result['result'] = $this->callBackend('reload'); + return $result; + } + + /** + * get status of monit process + * @return array + */ + public function statusAction() + { + if ($this->request->isPost()) { + $this->sessionClose(); + } + $result['function'] = "status"; + $result['result'] = "failed"; + $result['status'] = 'stopped'; + $status = $this->callBackend('status'); + if (substr($status, 0, 16) == 'monit is running') { + $result['result'] = "ok"; + $result['status'] = 'running'; + } else { + $result['error'] = $status; + } + return $result; + } + + /** + * start monit service + * @return array + */ + public function startAction() + { + $result = array("result" => "failed", "function" => "start"); + if ($this->request->isPost()) { + $this->sessionClose(); + $result['result'] = $this->callBackend('start'); + } + return $result; + } + + /** + * stop monit service + * @return array + */ + public function stopAction() + { + $result = array("result" => "failed", "function" => "stop"); + if ($this->request->isPost()) { + $this->sessionClose(); + $result['result'] = $this->callBackend('stop'); + } + return $result; + } + + /** + * restart monit service + * @return array + */ + public function restartAction() + { + $result = array("result" => "failed", "function" => "restart"); + if ($this->request->isPost()) { + $this->sessionClose(); + $result['result'] = $this->callBackend('restart'); + } + return $result; + } + + /** + * call backend functions + * @param action + * @return string + */ + protected function callBackend($action) + { + $backend = new Backend(); + if ($action == 'template') { + return trim($backend->configdRun('template reload OPNsense/Monit')); + } else { + return trim($backend->configdRun('monit ' . $action)); + } + } +} diff --git a/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/SettingsController.php b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/SettingsController.php new file mode 100644 index 000000000..78856ae3c --- /dev/null +++ b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/SettingsController.php @@ -0,0 +1,463 @@ +get('general'); + } + + /** + * update monit general settings with given properties + * @return array + */ + public function setGeneralAction() + { + return $this->set('general'); + } + + //// ALERT SETTINGS //// + + /** + * search alert + * @return array + */ + public function searchAlertAction() + { + $fields = array("enabled", "recipient", "noton", "events", "description"); + return $this->search('alert', $fields); + } + + /** + * retrieve monit alert settings or return defaults + * @param $uuid item unique id + * @return array + */ + public function getAlertAction($uuid = null) + { + return $this->get('alert', $uuid); + } + + /** + * set monit alert parameter + * @param $uuid item unique id + * @return array + */ + public function setAlertAction($uuid = null) + { + if ($uuid != null) { + return $this->set('alert', $uuid); + } + return array("result" => "failed"); + } + + /** + * add monit alert parameter + * @return array + */ + public function addAlertAction() + { + return $this->set('alert', null, true); + } + + /** + * delete monit alert parameter + * @param $uuid item unique id + * @return array + */ + public function delAlertAction($uuid = null) + { + if ($uuid != null) { + return $this->del('alert', $uuid); + } + return array("result" => "failed"); + } + + /** + * toggle monit alert by uuid (enable/disable) + * @param $uuid item unique id + * @return array + */ + public function toggleAlertAction($uuid) + { + if ($uuid != null) { + return $this->toggle('alert', $uuid); + } + return array("result" => "failed"); + } + + //// SERVICE SETTINGS //// + + /** + * search service + * @return array + */ + public function searchServiceAction() + { + $fields = array("enabled", "name", "type", "description"); + return $this->search('service', $fields); + } + + /** + * retrieve monit service settings or return defaults + * @param $uuid item unique id + * @return array + */ + public function getServiceAction($uuid = null) + { + return $this->get('service', $uuid); + } + + /** + * set monit service parameter + * @param $uuid item unique id + * @return array + */ + public function setServiceAction($uuid = null) + { + if ($uuid != null) { + return $this->set('service', $uuid); + } + return array("result" => "failed"); + } + + /** + * add monit service parameter + * @return array + */ + public function addServiceAction() + { + return $this->set('service', null, true); + } + + /** + * delete monit service parameter + * @param $uuid item unique id + * @return array + */ + public function delServiceAction($uuid = null) + { + if ($uuid != null) { + return $this->del('service', $uuid); + } + return array("result" => "failed"); + } + + /** + * toggle monit service by uuid (enable/disable) + * @param $uuid item unique id + * @return array + */ + public function toggleServiceAction($uuid) + { + if ($uuid != null) { + return $this->toggle('service', $uuid); + } + return array("result" => "failed"); + } + + //// SERVICE TEST SETTINGS //// + + /** + * search test + * @return array + */ + public function searchTestAction() + { + $fields = array("name", "condition", "action"); + return $this->search('test', $fields); + } + + /** + * retrieve monit service test settings or return defaults + * @param $uuid item unique id + * @return array + */ + public function getTestAction($uuid = null) + { + return $this->get('test', $uuid); + } + + /** + * set monit service test parameter + * @param $uuid item unique id + * @return array + */ + public function setTestAction($uuid = null) + { + if ($uuid != null) { + return $this->set('test', $uuid); + } + return array("result" => "failed"); + } + + /** + * add monit service test parameter + * @return array + */ + public function addTestAction() + { + return $this->set('test', null, true); + } + + /** + * delete monit service test parameter + * @param $uuid item unique id + * @return array + */ + public function delTestAction($uuid = null) + { + if ($uuid != null) { + return $this->del('test', $uuid); + } + return array("result" => "failed"); + } + + //// ABSTRACT FUNCTIONS //// + + /** + * retrieve monit settings + * @param $nodeType + * @param $uuid + * @return result array + */ + private function get($nodeType = null, $uuid = null) + { + $result = array("result" => "failed"); + if($this->request->isGet() && $nodeType != null) { + $mdlMonit = new Monit(); + if ($uuid != null) { + $node = $mdlMonit->getNodeByReference($nodeType . '.' . $uuid); + } else { + if ($nodeType == 'general') { + $node = $mdlMonit->getNodeByReference($nodeType); + } else { + $node = $mdlMonit->$nodeType->Add(); + } + } + if ($node != null) { + $result[$nodeType] = $node->getNodes(); + $result["result"] = "ok"; + } + } + return $result; + } + + /** + * set monit properties + * @param $nodeType + * @param $uuid + * @parm $action set or add node + * @return result array + */ + private function set($nodeType = null, $uuid = null, $add = false) + { + $result = array("result" => "failed"); + if ($this->request->isPost() && $this->request->hasPost("monit") && $nodeType != null) { + $mdlMonit = new Monit(); + if($add == false) { // set node + if ($uuid != null) { + $node = $mdlMonit->getNodeByReference($nodeType . '.' . $uuid); + } else { + if ($nodeType == 'general') { + $node = $mdlMonit->getNodeByReference($nodeType); + } else { + $node = $mdlMonit->$nodeType->Add(); + } + } + } else { + $node = $mdlMonit->$nodeType->Add(); + } + if ($node != null) { + $monitInfo = $this->request->getPost("monit"); + + // perform plugin specific validations + if ($nodeType == 'service') { + 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 = $mdlMonit->performValidation(); + foreach ($valMsgs as $field => $msg) { + $fieldnm = str_replace($node->__reference, "monit." . $nodeType, $msg->getField()); + $result["validations"][$fieldnm] = $msg->getMessage(); + } + if ($valMsgs->count() == 0) { + $mdlMonit->serializeToConfig(); + Config::getInstance()->save(); + $result["result"] = "saved"; + } + } + } + return $result; + } + + /** + * delete monit properties + * @param $nodeType + * @param $uuid + * @return result array + */ + private function del($nodeType = null, $uuid = null) + { + $result = array("result" => "failed"); + if ($this->request->isPost() && $nodeType != null) { + $mdlMonit = new Monit(); + if ($uuid != null) { + $node = $mdlMonit->getNodeByReference($nodeType . '.' . $uuid); + if ($node != null) { + if ($mdlMonit->$nodeType->del($uuid) == true) { + // remove test from services + if ($nodeType == 'test') { + // get a list of all services + $services = $mdlMonit->service->getNodes(); + foreach ($services as $serviceUuid => $service) { + foreach ($service['tests'] as $testUuid => $test) { + // service has a reference to a test + if ($testUuid == $uuid) { + // get service model and remove $uuid from tests + $ref = 'service.' . $serviceUuid . '.tests'; + $tstNode = $mdlMonit->getNodeByReference($ref); + $svcTests = str_replace($uuid, '', $tstNode->__toString()); + $svcTests = str_replace(',,', ',', $svcTests); + $svcTests = rtrim($svcTests, ','); + $svcTests = ltrim($svcTests, ','); + $mdlMonit->setNodeByReference($ref, $svcTests); + } + } + } + } + $mdlMonit->serializeToConfig(); + Config::getInstance()->save(); + $svcMonit = new ServiceController(); + $result = $svcMonit->reloadAction(); + } + } else { + $result['result'] = "not found"; + } + } else { + $result['result'] = "uuid not given"; + } + } + return $result; + } + + /** + * toggle monit items (enable/disable) + * @param $nodeType + * @param $uuid + * @return result array + */ + private function toggle($nodeType = null, $uuid = null) + { + $result = array("result" => "failed"); + if ($this->request->isPost() && $nodeType != null) { + $mdlMonit = new Monit(); + if ($uuid != null) { + $node = $mdlMonit->getNodeByReference($nodeType . '.' . $uuid); + if ($node != null) { + if ($node->enabled->__toString() == "1") { + $node->enabled = "0"; + } else { + $node->enabled = "1"; + } + $mdlMonit->serializeToConfig(); + Config::getInstance()->save(); + $svcMonit = new ServiceController(); + $result= $svcMonit->reloadAction(); + } else { + $result['result'] = "not found"; + } + } else { + $result['result'] = "uuid not given"; + } + } + return $result; + } + + /** + * search monit settings + * @param $nodeType + * @param requested field list + * @return array + */ + private function search($nodeType = null, &$fields = null) + { + $this->sessionClose(); + if($nodeType != null) { + $mdlMonit = new Monit(); + $grid = new UIModelGrid($mdlMonit->$nodeType); + return $grid->fetchBindRequest($this->request, $fields); + } + } +} diff --git a/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/StatusController.php b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/StatusController.php new file mode 100644 index 000000000..525950a30 --- /dev/null +++ b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/Api/StatusController.php @@ -0,0 +1,134 @@ + "failed", "function" => "getStatus"); + + // connect monit httpd socket defined in monitrc by 'set httpd ...' + if (file_exists("/var/run/monit.sock") && filetype("/var/run/monit.sock") == "socket" ) { + // throws an exception therefore no error handling + $socket = stream_socket_client("unix:///var/run/monit.sock", $errno, $errstr); + + // get monit status page + $request = "GET /_status?format=text HTTP/1.0\r\n"; + $request .= "\r\n"; + $count = fwrite($socket, $request); + $result['count'] = $count; + $result['status'] = ''; + $result['orig'] = ''; + $result['httpstatus'] = preg_replace( "/\r|\n/", "", fgets($socket)); + $ignorelines = 1; + if ($result['httpstatus'] == 'HTTP/1.0 200 OK') { + while (!feof($socket)) { + $line = fgets($socket); + $result['orig'] .= $line; + + // ignore lines (mostly HTTP headers) until a line starts with 'Monit' e.g. 'Monit 5.20.0 uptime: 2d 23h 2m' + if (substr($line, 0, 5) == 'Monit') { + $ignorelines = 0; + } + if ($ignorelines) { + continue; + } + $result['status'] .= $line; + } + $result['result'] = "ok"; + + } + fclose($socket); + + // response contains shell color escape codes; convert them to CSS + $result['status'] = '
' . $this->bashColorToCSS($result['status']) . '
'; + } else { + $result['status'] = '
+Either the file /var/run/monit.sock does not exists or it is not a unix socket.
+Please check if the Monit service is running.
+
+If you have started Monit recently, wait for StartDelay seconds and refresh this page.
'; + } + return $result; + } + + /** + * convert bash color escape codes to CSS + * @param $string + * @return string + */ + private function bashColorToCSS($string) { + $colors = [ + '/\x1b\[0;30m(.*?)\x1b\[0m/s' => '$1', + + '/\x1b\[0;30m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[0;31m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[0;32m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[0;33m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[0;34m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[0;35m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[0;36m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[0;37m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[0;39m(.*?)\x1b\[0m/s' => '$1', + + '/\x1b\[1;30m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[1;31m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[1;32m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[1;33m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[1;34m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[1;35m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[1;36m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[1;37m(.*?)\x1b\[0m/s' => '$1', + + '/\x1b\[0;90m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[0;91m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[0;92m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[0;93m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[0;94m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[0;95m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[0;96m(.*?)\x1b\[0m/s' => '$1', + '/\x1b\[0;97m(.*?)\x1b\[0m/s' => '$1' + ]; + return preg_replace(array_keys($colors), $colors, $string); + } +} diff --git a/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/IndexController.php b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/IndexController.php new file mode 100644 index 000000000..45c54d2b4 --- /dev/null +++ b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/IndexController.php @@ -0,0 +1,52 @@ +view->title = gettext('Monit System Monitoring - Settings'); + $this->view->formGeneralSettings = $this->getForm("general"); + $this->view->formDialogEditAlert = $this->getForm("alerts"); + $this->view->formDialogEditService = $this->getForm("services"); + $this->view->formDialogEditTest = $this->getForm("tests"); + $this->view->pick('OPNsense/Monit/index'); + } +} diff --git a/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/ItemController.php b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/ItemController.php new file mode 100644 index 000000000..f2e397071 --- /dev/null +++ b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/ItemController.php @@ -0,0 +1,40 @@ +view->title = gettext('Monit System Monitoring - Status'); + $this->view->pick('OPNsense/Monit/status'); + } +} diff --git a/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/alerts.xml b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/alerts.xml new file mode 100644 index 000000000..30bee0535 --- /dev/null +++ b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/alerts.xml @@ -0,0 +1,48 @@ +
+ + monit.alert.enabled + + checkbox + + + + monit.alert.recipient + + text + + + + + monit.alert.noton + + checkbox + + + + monit.alert.events + + select_multiple + 28 + + + + + monit.alert.format + + text + + Subject: $SERVICE on $HOST failed]]> + + + monit.alert.reminder + + text + + Send a reminder after some cycles + + + monit.alert.description + + text + +
diff --git a/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/general.xml b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/general.xml new file mode 100644 index 000000000..9c4c95825 --- /dev/null +++ b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/general.xml @@ -0,0 +1,26 @@ +
+ + monit.general.enabled + + checkbox + Enable or disable monit. + + + monit.general.interval + + text + Polling interval in seconds + + + monit.general.startdelay + + text + On system boot wait before Monit starts checking services. + + + monit.general.mailserver + + text + Comma separated list of SMTP servers for alert delivery. + +
diff --git a/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/services.xml b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/services.xml new file mode 100644 index 000000000..6fa73bbd5 --- /dev/null +++ b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/services.xml @@ -0,0 +1,75 @@ +
+ + monit.service.enabled + + checkbox + + + + monit.service.name + + text + + + + monit.service.type + + dropdown + + + + monit.service.pidfile + + text + + + + monit.service.match + + text + monit procmatch <PATTERN>]]> + + + monit.service.path + + text + + + + monit.service.address + + text + + + + monit.service.interface + + dropdown + + + + monit.service.start + + text + + + + monit.service.stop + + text + + + + monit.service.tests + + select_multiple + + + + + monit.service.description + + text + +
+ diff --git a/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/tests.xml b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/tests.xml new file mode 100644 index 000000000..5d96a0a21 --- /dev/null +++ b/sysutils/monit/src/opnsense/mvc/app/controllers/OPNsense/Monit/forms/tests.xml @@ -0,0 +1,26 @@ +
+ + monit.test.name + + text + + + + monit.test.condition + + text + cpu is greater than 50%
failed host 127.0.0.1 port 22 protocol ssh
]]>
+
+ + monit.test.action + + dropdown + alert or restart the service or execute a program.]]> + + + monit.test.path + + text + + +
diff --git a/sysutils/monit/src/opnsense/mvc/app/models/OPNsense/Monit/ACL/ACL.xml b/sysutils/monit/src/opnsense/mvc/app/models/OPNsense/Monit/ACL/ACL.xml new file mode 100644 index 000000000..72937387e --- /dev/null +++ b/sysutils/monit/src/opnsense/mvc/app/models/OPNsense/Monit/ACL/ACL.xml @@ -0,0 +1,10 @@ + + + WebCfg - Services: Monit System Monitoring page + Allow access to the 'Services: Monit System Monitoring' page. + + ui/monit/* + api/monit/* + + + diff --git a/sysutils/monit/src/opnsense/mvc/app/models/OPNsense/Monit/Menu/Menu.xml b/sysutils/monit/src/opnsense/mvc/app/models/OPNsense/Monit/Menu/Menu.xml new file mode 100644 index 000000000..841575d23 --- /dev/null +++ b/sysutils/monit/src/opnsense/mvc/app/models/OPNsense/Monit/Menu/Menu.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/sysutils/monit/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.php b/sysutils/monit/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.php new file mode 100644 index 000000000..a386f7c00 --- /dev/null +++ b/sysutils/monit/src/opnsense/mvc/app/models/OPNsense/Monit/Monit.php @@ -0,0 +1,41 @@ + + //OPNsense/monit + 1.0.0 + Monit settings + + + + 0 + Y + + + 120 + Y + 0 + 86400 + Polling Interval needs to be an integer value between 0 and 86400 + + + 120 + Y + 0 + 86400 + Start Delay needs to be an integer value between 0 and 86400 + + + Y + 127.0.0.1 + Y + /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-4]|2[0-5][0-9]|[01]?[0-9][0-9]?)$/ + Mail Server must be a valid IPv4 address + + + + + 0 + Y + + + root@localhost.local + Y + Please enter a valid email address. + + + Y + 0 + + + N + + Action done + Checksum failed + Download bytes exceeded + Upload bytes exceeded + Connection failed + Content failed + Data access error + Execution failed + Filesystem flags failed + GID failed + Ping failed + Monit instance changed + Invalid type + Does not exist + Download packets exceeded + Upload packets exceeded + Permission failed + PID failed + PPID failed + Resource limit matched + Saturation exceeded + Size failed + Speed failed + Status failed + Timeout + Timestamp failed + UID failed + Uptime failed + + + + N + /^([ 0-9a-zA-Z.,_\x{00A0}-\x{FFFF}]){1,255}$/u + Message format should be a string between 1 and 255 characters. + + + 10 + N + 0 + 86400 + Reminder needs to be an integer value between 0 and 86400 + + + N + /^([\t\n\v\f\r 0-9a-zA-Z.,_\x{00A0}-\x{FFFF}]){1,255}$/u + Enter a description. + + + + + 0 + Y + + + 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 and dot. + + + Y + + Process + File + Fifo + Filesystem + Directory + Remote Host + System + Custom + Network + + + + N + /^(\/[^\/ ]*)+\/?$/ + Should be a valid absolute path to the PID file of the process. + + + N + + + N + /^(\/[^\/ ]*)+\/?$/ + Should be a valid absolute file or folder path. + +
+ N + /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-4]|2[0-5][0-9]|[01]?[0-9][0-9]?)$/ + Address must be a valid IPv4 address +
+ + N + N + + /^(?!0).*$/ + /^((?!dhcp).)*$/ + + + + N + /^(\/[^\/ ]*)+\/? .*$/ + Should be a valid absolute path to the executable with its arguments. + + + N + /^(\/[^\/ ]*)+\/? .*$/ + Should be a valid absolute path to the executable with its arguments. + + + + + + Related item not found + Y + N + +
+ + + Y + /^([0-9a-zA-Z._ ]){1,255}$/u + Should be a string between 1 and 255 characters. + + + Y + /^([\t\n\v\f\r 0-9a-zA-Z.:\-,_()%\x{00A0}-\x{FFFF}]){1,255}$/u + Should be a string between 1 and 255 characters. + + + Y + + Alert + Restart + Start + Stop + Execute + Unmonitor + + + + N + /^(\/[^\/ ]*)+?$/ + Should be a valid absolute file path. + + +
+ diff --git a/sysutils/monit/src/opnsense/mvc/app/views/OPNsense/Monit/index.volt b/sysutils/monit/src/opnsense/mvc/app/views/OPNsense/Monit/index.volt new file mode 100644 index 000000000..c67c865cd --- /dev/null +++ b/sysutils/monit/src/opnsense/mvc/app/views/OPNsense/Monit/index.volt @@ -0,0 +1,323 @@ +{# + +Copyright © 2017 by EURO-LOG AG +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +#} + + + + + +
+
+ + {{ partial("layout_partials/base_form",['fields':formGeneralSettings,'id':'frm_GeneralSettings','apply_btn_id':'btn_ApplyGeneralSettings'])}} +
+
+ + + + + + + + + + + + + + + + + + + + +
{{ lang._('Enabled') }}{{ lang._('Recipient') }}{{ lang._('Events') }}{{ lang._('Description') }}{{ lang._('ID') }}{{ lang._('Edit') }} | {{ lang._('Delete') }}
+ + +
+
+
+ + + + + + + + + + + + + + + + + +
{{ lang._('Enabled') }}{{ lang._('Name') }}{{ lang._('ID') }}{{ lang._('Edit') }} | {{ lang._('Delete') }}
+ + +
+
+
+ + + + + + + + + + + + + + + + + + +
{{ lang._('Name') }}{{ lang._('Condition') }}{{ lang._('Action') }}{{ lang._('ID') }}{{ lang._('Edit') }} | {{ lang._('Delete') }}
+ + +
+
+
+
+ + +
+
+
+
+{# include dialogs #} +{{ partial("layout_partials/base_dialog",['fields':formDialogEditAlert,'id':'DialogEditAlert','label':'Edit Alert  NOTE: For a detailed description see monit(1) section "ALERT MESSAGES".'])}} +{{ partial("layout_partials/base_dialog",['fields':formDialogEditService,'id':'DialogEditService','label':'Edit Service'])}} +{{ partial("layout_partials/base_dialog",['fields':formDialogEditTest,'id':'DialogEditTest','label':'Edit Test  NOTE: For a detailed description see monit(1) section "SERVICE TESTS".'])}} diff --git a/sysutils/monit/src/opnsense/mvc/app/views/OPNsense/Monit/status.volt b/sysutils/monit/src/opnsense/mvc/app/views/OPNsense/Monit/status.volt new file mode 100644 index 000000000..5ba2dd30d --- /dev/null +++ b/sysutils/monit/src/opnsense/mvc/app/views/OPNsense/Monit/status.volt @@ -0,0 +1,45 @@ +{# + +Copyright © 2017 by EURO-LOG AG +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +#} + + +
+Loading... +
diff --git a/sysutils/monit/src/opnsense/scripts/OPNsense/Monit/+POST_INSTALL.post b/sysutils/monit/src/opnsense/scripts/OPNsense/Monit/+POST_INSTALL.post new file mode 100755 index 000000000..77a81f2b2 --- /dev/null +++ b/sysutils/monit/src/opnsense/scripts/OPNsense/Monit/+POST_INSTALL.post @@ -0,0 +1 @@ +/usr/local/bin/php /usr/local/opnsense/scripts/OPNsense/Monit/post-install.php diff --git a/sysutils/monit/src/opnsense/scripts/OPNsense/Monit/post-install.php b/sysutils/monit/src/opnsense/scripts/OPNsense/Monit/post-install.php new file mode 100644 index 000000000..c57a19bdc --- /dev/null +++ b/sysutils/monit/src/opnsense/scripts/OPNsense/Monit/post-install.php @@ -0,0 +1,121 @@ +getNodes(); +// test if Monit is already configured +if(count($nodes['service']) != 0 || count($nodes['test']) != 0) { + exit; +} + +$cfg = Config::getInstance(); +$cfgObj = $cfg->object(); +$shellObj = new OPNsense\Core\Shell; + +// get number of cpus and calculate load average limits +$nCPU = array(); +$shellObj->exec('/sbin/sysctl -n kern.smp.cpus', false, $nCPU); +$LoadAvg1 = $nCPU[0] * 2; +$LoadAvg5 = $nCPU[0] + ($nCPU[0] / 2); +$LoadAvg15 = $nCPU[0]; + +// get FQDN +$hostName = $cfgObj->system->hostname; +$domainName = $cfgObj->system->domain; + +// define some tests +$defaultTests = array( + array("name" => "Ping", "condition" => "failed ping", "action" => "alert"), + array("name" => "NetworkLink", "condition" => "failed link", "action" => "alert"), + array("name" => "NetworkSaturation", "condition" => "saturation is greater than 75%", "action" => "alert"), + array("name" => "MemoryUsage", "condition" => "memory usage is greater than 75%", "action" => "alert"), + array("name" => "CPUUsage", "condition" => "cpu usage is greater than 75%", "action" => "alert"), + array("name" => "LoadAvg1", "condition" => "loadavg (1min) is greater than $LoadAvg1", "action" => "alert"), + array("name" => "LoadAvg5", "condition" => "loadavg (5min) is greater than $LoadAvg5", "action" => "alert"), + array("name" => "LoadAvg15", "condition" => "loadavg (15min) is greater than $LoadAvg15", "action" => "alert"), + array("name" => "SpaceUsage", "condition" => "space usage is greater than 75%", "action" => "alert") +); + +// define system service +$systemService = array( + "enabled" => 1, + "name" => $hostName . "." . $domainName, + "type" => "system", + "tests" => "" +); + +// define root filesystem service +$rootFsService = array( + "enabled" => 1, + "name" => "RootFs", + "type" => "filesystem", + "path" => "/", + "tests" => "" +); + +foreach ($defaultTests as $defaultTest) { + $testNode = $mdlMonit->test->Add(); + $testNode->setNodes($defaultTest); + if ($defaultTest['name'] == "MemoryUsage" || + $defaultTest['name'] == "CPUUsage" || + $defaultTest['name'] == "LoadAvg1" || + $defaultTest['name'] == "LoadAvg5" ) { + $systemService['tests'] .= $testNode->getAttributes()['uuid'] . ","; + } + if ($defaultTest['name'] == "SpaceUsage") { + $rootFsService['tests'] .= $testNode->getAttributes()['uuid'] . ","; + } +} + +// remove last comma from tests csv +$systemService['tests'] = substr($systemService['tests'], 0, -1); +$rootFsService['tests'] = substr($rootFsService['tests'], 0, -1); + +// add an alert with default settings +$mdlMonit->alert->Add(); + +// add system service +$serviceNode = $mdlMonit->service->Add(); +$serviceNode->setNodes($systemService); + +// add root filesystem service +$rootFsNode = $mdlMonit->service->Add(); +$rootFsNode->setNodes($rootFsService); + +// ignore validations because ModelRelationField does not work +$mdlMonit->serializeToConfig(false, true); +$cfg->save(); diff --git a/sysutils/monit/src/opnsense/scripts/OPNsense/Monit/setup.sh b/sysutils/monit/src/opnsense/scripts/OPNsense/Monit/setup.sh new file mode 100755 index 000000000..cf200fd2e --- /dev/null +++ b/sysutils/monit/src/opnsense/scripts/OPNsense/Monit/setup.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# change permissions of the monit configuration file +chmod 600 /usr/local/etc/monitrc || exit 1 +exit 0 diff --git a/sysutils/monit/src/opnsense/service/conf/actions.d/actions_monit.conf b/sysutils/monit/src/opnsense/service/conf/actions.d/actions_monit.conf new file mode 100644 index 000000000..1bf973bbc --- /dev/null +++ b/sysutils/monit/src/opnsense/service/conf/actions.d/actions_monit.conf @@ -0,0 +1,29 @@ +[start] +command:/usr/local/opnsense/scripts/OPNsense/Monit/setup.sh; /usr/local/etc/rc.d/monit start +type:script +message:starting monit + +[stop] +command:/usr/local/etc/rc.d/monit stop +type:script +message:stopping monit + +[status] +command:/usr/local/etc/rc.d/monit status || exit 0 +type:script_output +message:get monit status + +[restart] +command:/usr/local/opnsense/scripts/OPNsense/Monit/setup.sh; /usr/local/etc/rc.d/monit restart +type:script +message:restarting monit + +[reload] +command:/usr/local/opnsense/scripts/OPNsense/Monit/setup.sh; /usr/local/etc/rc.d/monit reload +type:script +message:reload monit configuration + +[configtest] +command:/usr/local/opnsense/scripts/OPNsense/Monit/setup.sh; /usr/local/bin/monit -t 2>&1 || exit 0 +type:script_output +message:testing monit configuration diff --git a/sysutils/monit/src/opnsense/service/templates/OPNsense/Monit/+TARGETS b/sysutils/monit/src/opnsense/service/templates/OPNsense/Monit/+TARGETS new file mode 100644 index 000000000..d5be9309b --- /dev/null +++ b/sysutils/monit/src/opnsense/service/templates/OPNsense/Monit/+TARGETS @@ -0,0 +1,2 @@ +monitrc:/usr/local/etc/monitrc +rc.conf.d:/etc/rc.conf.d/monit diff --git a/sysutils/monit/src/opnsense/service/templates/OPNsense/Monit/monitrc b/sysutils/monit/src/opnsense/service/templates/OPNsense/Monit/monitrc new file mode 100644 index 000000000..099ad4c8e --- /dev/null +++ b/sysutils/monit/src/opnsense/service/templates/OPNsense/Monit/monitrc @@ -0,0 +1,79 @@ +# DO NOT EDIT THIS FILE -- OPNsense auto-generated file + +{% from 'OPNsense/Macros/interface.macro' import physical_interface %} +{% if helpers.exists('OPNsense.monit.general.enabled') and OPNsense.monit.general.enabled|default("0") == "1" %} +set httpd unixsocket /var/run/monit.sock + allow localhost + +set daemon {{ OPNsense.monit.general.interval }} with start delay {{ OPNsense.monit.general.startdelay }} + +set logfile syslog facility log_daemon + +set mailserver {{ OPNsense.monit.general.mailserver }} + +{% if helpers.exists('OPNsense.monit.alert') %} +{% for alert in helpers.toList('OPNsense.monit.alert') %} +{% if alert.enabled|default('0') == '1' %} +{% set noton = 'not on' if alert.noton|default('0') == '1' %} +{% set mailformat = "mail-format { " ~ alert.format ~ " }" if alert.format|default('') != '' %} +{% set reminder = "reminder on " ~ alert.reminder ~ " cycles" if alert.reminder|default('0') != '0' %} +{% set events = "{ " ~ alert.events ~ " }" if alert.events|default('') != '' %} +set alert {{ alert.recipient }} {{ noton }} {{ events }} {{ mailformat }} {{ reminder }} +{% endif %} +{% endfor %} +{% endif %} + +{% if helpers.exists('OPNsense.monit.service') %} +{% for service in helpers.toList('OPNsense.monit.service') %} +{% if service.enabled|default('0') == '1' %} +{% set pidfile = '' %} +{% set match = '' %} +{% set path = '' %} +{% set address = '' %} +{% set interface = '' %} +{% if service.type == 'process' %} +{% if service.pidfile|default('') != '' %} +{% set pidfile = "pidfile " ~ service.pidfile %} +{% elif service.match|default('') != '' %} +{% set match = "matching " ~ service.match if service.match|default('') != '' %} +{% endif %} +check {{ service.type }} {{ service.name }} {{ pidfile }} {{ match }} +{% elif service.type == 'host' %} +{% set address = "address " ~ service.address %} +check {{ service.type }} {{ service.name }} {{ address }} +{% elif service.type == 'network' %} +{% if service.address|default('') != '' %} +{% set address = "address " ~ service.address %} +{% elif service.interface|default('') != '' %} +{% set interface = "interface " ~ physical_interface(service.interface) %} +{% endif %} +check {{ service.type }} {{ service.name }} {{ address }} {{ interface }} +{% elif service.type == 'system' %} +check {{ service.type }} {{ service.name }} +{% else %} +{% set path = "with path " ~ service.path %} +check {{ service.type }} {{ service.name }} {{ path }} +{% endif %} +{% if service.start|default('') != '' %} + start program = "{{ service.start }}" +{% endif %} +{% if service.start|default('') != '' %} + stop program = "{{ service.stop }}" +{% endif %} +{% if service.tests is defined %} +{% for test in service.tests.split(",") %} +{% set test = helpers.getUUID(test) %} +{% if test.condition is defined and test.action is defined %} +{% set epath = '' %} +{% if test.action == 'exec' and test.path|default('') != '' %} +{% set epath = test.path %} +{% endif %} + if {{ test.condition }} then {{ test.action }} {{ epath }} +{% endif %} +{% endfor %} +{% endif %} +{% endif %} + +{% endfor %} +{% endif %} +{% endif %} diff --git a/sysutils/monit/src/opnsense/service/templates/OPNsense/Monit/rc.conf.d b/sysutils/monit/src/opnsense/service/templates/OPNsense/Monit/rc.conf.d new file mode 100644 index 000000000..3ca37dd5a --- /dev/null +++ b/sysutils/monit/src/opnsense/service/templates/OPNsense/Monit/rc.conf.d @@ -0,0 +1,6 @@ +# DO NOT EDIT THIS FILE -- OPNsense auto-generated file +{% if helpers.exists('OPNsense.monit.general.enabled') and OPNsense.monit.general.enabled|default("0") == "1" %} +monit_enable="YES" +{% else %} +monit_enable="NO" +{% endif %}