From 1e9fa284331a3ba4a409d15fdf84d221f712ec93 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 27 Jun 2013 10:14:41 +0200 Subject: [PATCH] Add the monitoring module from the incubator --- .../controllers/CommandController.php | 213 ++++++++++++ .../controllers/ListController.php | 208 +++++++++++ .../controllers/ShowController.php | 296 ++++++++++++++++ .../controllers/SummaryController.php | 70 ++++ .../views/helpers/CommandArguments.php | 19 + .../views/helpers/MonitoringState.php | 19 + .../application/views/helpers/Perfdata.php | 84 +++++ .../views/helpers/PluginOutput.php | 87 +++++ .../views/helpers/ResolveComments.php | 32 ++ .../views/helpers/_RenderServicePerfdata.php | 23 ++ .../views/scripts/list/contactgroups.phtml | 4 + .../views/scripts/list/contacts.phtml | 4 + .../views/scripts/list/hostgroups.phtml | 4 + .../views/scripts/list/hosts-compact.phtml | 59 ++++ .../views/scripts/list/hosts.phtml | 125 +++++++ .../views/scripts/list/servicegroups.phtml | 4 + .../views/scripts/list/services-compact.phtml | 73 ++++ .../views/scripts/list/services.phtml | 154 +++++++++ .../scripts/show/components/comments.phtml | 25 ++ .../scripts/show/components/contacts.phtml | 24 ++ .../views/scripts/show/header.phtml | 30 ++ .../views/scripts/show/history.phtml | 99 ++++++ .../application/views/scripts/show/host.phtml | 40 +++ .../views/scripts/show/legacy-service.phtml | 117 +++++++ .../views/scripts/show/service.phtml | 31 ++ .../views/scripts/show/services.phtml | 1 + .../views/scripts/show/ticket.phtml | 2 + .../views/scripts/summary/group.phtml | 167 +++++++++ modules/monitoring/config/backends.ini | 9 + modules/monitoring/config/instances.ini | 2 + modules/monitoring/config/menu.ini | 12 + .../monitoring/library/Monitoring/Backend.php | 84 +++++ .../Monitoring/Backend/AbstractBackend.php | 199 +++++++++++ .../library/Monitoring/Backend/Ido.php | 57 +++ .../Backend/Ido/Query/AbstractQuery.php | 326 ++++++++++++++++++ .../Backend/Ido/Query/CommentQuery.php | 50 +++ .../Backend/Ido/Query/ContactQuery.php | 72 ++++ .../Backend/Ido/Query/ContactgroupQuery.php | 106 ++++++ .../Backend/Ido/Query/CustomvarQuery.php | 40 +++ .../Backend/Ido/Query/EventHistoryQuery.php | 241 +++++++++++++ .../Backend/Ido/Query/HostgroupQuery.php | 44 +++ .../Backend/Ido/Query/ServicegroupQuery.php | 45 +++ .../Backend/Ido/Query/StatusQuery.php | 258 ++++++++++++++ .../library/Monitoring/Backend/Livestatus.php | 45 +++ .../Backend/Livestatus/Query/StatusQuery.php | 48 +++ .../library/Monitoring/Environment.php | 61 ++++ .../monitoring/library/Monitoring/Plugin.php | 9 + .../library/Monitoring/Plugin/Perfdata.php | 205 +++++++++++ .../library/Monitoring/Plugin/PerfdataSet.php | 69 ++++ .../library/Monitoring/View/CommentView.php | 23 ++ .../library/Monitoring/View/ContactView.php | 24 ++ .../Monitoring/View/ContactgroupView.php | 23 ++ .../library/Monitoring/View/CustomvarView.php | 23 ++ .../Monitoring/View/EventHistoryView.php | 34 ++ .../library/Monitoring/View/HostgroupView.php | 21 ++ .../Monitoring/View/MonitoringView.php | 135 ++++++++ .../Monitoring/View/ServicegroupView.php | 21 ++ .../library/Monitoring/View/StatusView.php | 111 ++++++ 58 files changed, 4411 insertions(+) create mode 100644 modules/monitoring/application/controllers/CommandController.php create mode 100644 modules/monitoring/application/controllers/ListController.php create mode 100644 modules/monitoring/application/controllers/ShowController.php create mode 100644 modules/monitoring/application/controllers/SummaryController.php create mode 100644 modules/monitoring/application/views/helpers/CommandArguments.php create mode 100644 modules/monitoring/application/views/helpers/MonitoringState.php create mode 100644 modules/monitoring/application/views/helpers/Perfdata.php create mode 100644 modules/monitoring/application/views/helpers/PluginOutput.php create mode 100644 modules/monitoring/application/views/helpers/ResolveComments.php create mode 100644 modules/monitoring/application/views/helpers/_RenderServicePerfdata.php create mode 100644 modules/monitoring/application/views/scripts/list/contactgroups.phtml create mode 100644 modules/monitoring/application/views/scripts/list/contacts.phtml create mode 100644 modules/monitoring/application/views/scripts/list/hostgroups.phtml create mode 100644 modules/monitoring/application/views/scripts/list/hosts-compact.phtml create mode 100644 modules/monitoring/application/views/scripts/list/hosts.phtml create mode 100644 modules/monitoring/application/views/scripts/list/servicegroups.phtml create mode 100644 modules/monitoring/application/views/scripts/list/services-compact.phtml create mode 100644 modules/monitoring/application/views/scripts/list/services.phtml create mode 100644 modules/monitoring/application/views/scripts/show/components/comments.phtml create mode 100644 modules/monitoring/application/views/scripts/show/components/contacts.phtml create mode 100644 modules/monitoring/application/views/scripts/show/header.phtml create mode 100644 modules/monitoring/application/views/scripts/show/history.phtml create mode 100644 modules/monitoring/application/views/scripts/show/host.phtml create mode 100644 modules/monitoring/application/views/scripts/show/legacy-service.phtml create mode 100644 modules/monitoring/application/views/scripts/show/service.phtml create mode 100644 modules/monitoring/application/views/scripts/show/services.phtml create mode 100644 modules/monitoring/application/views/scripts/show/ticket.phtml create mode 100644 modules/monitoring/application/views/scripts/summary/group.phtml create mode 100644 modules/monitoring/config/backends.ini create mode 100644 modules/monitoring/config/instances.ini create mode 100644 modules/monitoring/config/menu.ini create mode 100644 modules/monitoring/library/Monitoring/Backend.php create mode 100644 modules/monitoring/library/Monitoring/Backend/AbstractBackend.php create mode 100644 modules/monitoring/library/Monitoring/Backend/Ido.php create mode 100644 modules/monitoring/library/Monitoring/Backend/Ido/Query/AbstractQuery.php create mode 100644 modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php create mode 100644 modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactQuery.php create mode 100644 modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php create mode 100644 modules/monitoring/library/Monitoring/Backend/Ido/Query/CustomvarQuery.php create mode 100644 modules/monitoring/library/Monitoring/Backend/Ido/Query/EventHistoryQuery.php create mode 100644 modules/monitoring/library/Monitoring/Backend/Ido/Query/HostgroupQuery.php create mode 100644 modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicegroupQuery.php create mode 100644 modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php create mode 100644 modules/monitoring/library/Monitoring/Backend/Livestatus.php create mode 100644 modules/monitoring/library/Monitoring/Backend/Livestatus/Query/StatusQuery.php create mode 100644 modules/monitoring/library/Monitoring/Environment.php create mode 100644 modules/monitoring/library/Monitoring/Plugin.php create mode 100644 modules/monitoring/library/Monitoring/Plugin/Perfdata.php create mode 100644 modules/monitoring/library/Monitoring/Plugin/PerfdataSet.php create mode 100644 modules/monitoring/library/Monitoring/View/CommentView.php create mode 100644 modules/monitoring/library/Monitoring/View/ContactView.php create mode 100644 modules/monitoring/library/Monitoring/View/ContactgroupView.php create mode 100644 modules/monitoring/library/Monitoring/View/CustomvarView.php create mode 100644 modules/monitoring/library/Monitoring/View/EventHistoryView.php create mode 100644 modules/monitoring/library/Monitoring/View/HostgroupView.php create mode 100644 modules/monitoring/library/Monitoring/View/MonitoringView.php create mode 100644 modules/monitoring/library/Monitoring/View/ServicegroupView.php create mode 100644 modules/monitoring/library/Monitoring/View/StatusView.php diff --git a/modules/monitoring/application/controllers/CommandController.php b/modules/monitoring/application/controllers/CommandController.php new file mode 100644 index 000000000..1b04f8cd9 --- /dev/null +++ b/modules/monitoring/application/controllers/CommandController.php @@ -0,0 +1,213 @@ +_request->getPost($name, false); + if ($value === false) + throw new IcingaException\MissingParameterException("Missing parameter $name"); + return $value; + } + + public function init() + { + if (!$this->_request->isPost()) { + $this->_response->clearBody(); + $this->_response->clearHeaders(); + $this->_response->setHttpResponseCode(405); + $this->_redirect("/"); + } + if (!$this->hasValidToken()) + throw new Exception("Invalid token given", 401); + $this->_helper->viewRenderer->setNoRender(true); + $this->_helper->layout()->disableLayout(); + $targets = Icinga\Application\Config::getInstance()->getModuleConfig("instances", "monitoring"); + $instance = $this->_getParam("instance"); + if ($instance && isset($targets[$instance])) { + $this->target = new \Icinga\Protocol\Commandpipe\CommandPipe($targets[$instance]); + } else { + foreach ($targets as $target) { + $this->target = new \Icinga\Protocol\Commandpipe\CommandPipe($target); + break; + } + } + } + + private function selectCommandTargets() + { + $hostname = $this->_getParam("hosts"); + $servicename = $this->_getParam("services"); + $target = "hostlist"; + $filter = array(); + if (!$hostname && !$servicename) { + throw new IcingaException\MissingParameterException("Missing host and service definition"); + } + if ($hostname) { + $filter["hostname"] = explode(";", $hostname); + } + if ($servicename) { + $filter["servicedescription"] = explode(";", $servicename); + $target = "servicelist"; + } + + return $this->backend = Icinga\Backend::getInstance() + ->select() + ->from($target) + ->applyFilters($filter) + ->fetchAll(); + } + + public function sendReschedule() + { + $forced = (trim($this->_getParam("forced"), false) === "true"); + $time = $this->_request->getPost("time", false); + $childs = $this->_request->getPost("withChilds", false); + if ($forced) { + $this->target->scheduleForcedCheck($this->selectCommandTargets(), $time, $childs); + } else { + $this->target->scheduleCheck($this->selectCommandTargets(), $time, $childs); + } + } + + public function sendAcknowledge() + { + $author = "AUTHOR"; //@TODO: get from auth backend + $comment = $this->getMandatoryParameter("comment"); + $persistent = $this->_request->getPost("persistent", false) == "true"; + $commentObj = new \Icinga\Protocol\Commandpipe\Comment($author, $comment, $persistent); + + $notify = $this->_request->getPost("notify", false) == "true"; + $sticky = $this->_request->getPost("sticky", false) == "true"; + $expire = intval($this->_request->getPost("expire", false)); + if (!$expire) { + $expire = -1; + } + $ack = new \Icinga\Protocol\Commandpipe\Acknowledgement($commentObj, $notify, $expire, $sticky); + $this->target->acknowledge($this->selectCommandTargets(), $ack); + } + + public function sendScheduledowntime() + { + $author = "AUTHOR"; //@TODO: get from auth backend + $comment = $this->getMandatoryParameter("comment"); + $persistent = $this->_request->getPost("persistent", false) == "true"; + $commentObj = new \Icinga\Protocol\Commandpipe\Comment($author, $comment, $persistent); + + $start = intval($this->_request->getPost("start", time())); + $end = intval($this->getMandatoryParameter("end")); + $duration = $this->_request->getPost("duration", false); + if ($duration !== false) { + $duration = intval($duration); + } + $downtime = new \Icinga\Protocol\Commandpipe\Downtime($start, $end, $commentObj, $duration); + + $this->target->scheduleDowntime($this->selectCommandTargets(), $downtime); + } + + public function sendActivechecks() + { + if ($this->getMandatoryParameter("enable")) { + $this->target->enableActiveChecks($this->selectCommandTargets()); + } else { + $this->target->disableActiveChecks($this->selectCommandTargets()); + } + } + + public function sendPassivechecks() + { + if ($this->getMandatoryParameter("enable")) { + $this->target->enablePassiveChecks($this->selectCommandTargets()); + } else { + $this->target->disablePassiveChecks($this->selectCommandTargets()); + } + } + + public function sendFlappingdetection() + { + if ($this->getMandatoryParameter("enable")) { + $this->target->enableFlappingDetection($this->selectCommandTargets()); + } else { + $this->target->disableFlappingDetection($this->selectCommandTargets()); + } + } + + public function sendComment() + { + $author = "AUTHOR"; //@TODO: get from auth backend + $comment = $this->getMandatoryParameter("comment"); + $persistent = $this->_request->getPost("persistent", false) == "true"; + $commentObj = new \Icinga\Protocol\Commandpipe\Comment($author, $comment, $persistent); + $this->target->addComment($this->selectCommandTargets(), $commentObj); + } + + public function sendDeletecomment() + { + if ($this->_request->getPost("comments")) { + $comments = array(); + foreach ($this->_request->getPost("comments") as $id => $content) { + $comment = new StdClass(); + $comment->comment_id = $id; + $value = explode(";", $content, 2); + $comment->host_name = $value[0]; + if (isset($value[1])) { + $comment->service_description = $value[1]; + } + $comments[] = $comment; + } + $this->target->removeComment($comments); + } else { + $this->target->removeComment($this->selectCommandTargets()); + } + } + + public function sendRestartIcinga() + { + $this->target->restartIcinga(); + } + + public function sendDeletedowntime() + { + if ($this->_request->getPost("downtimes")) { + $downtimes = array(); + foreach ($this->_request->getPost("comments") as $id => $content) { + $downtime = new StdClass(); + $downtime->downtime_id = $id; + $value = explode(";", $content, 2); + $downtime->host_name = $value[0]; + if (isset($value[1])) { + $downtime->service_description = $value[1]; + } + $downtimes[] = $downtime; + } + $this->target->removeDowntime($downtimes); + } else { + $this->target->removeDowntime($this->selectCommandTargets()); + } + } + + + public function __call($method, $args) + { + $command = substr($method, 0, -6); + if ('Action' == substr($method, -6)) { + $command[0] = strtoupper($command[0]); + if (method_exists($this, "send$command")) { + return $this->{"send$command"}(); + } + throw new Exception("Invalid command $command", 404); + } + + throw new BadMethodCallException("Call to undefined method $method"); + + } + + +} diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php new file mode 100644 index 000000000..ac5108eb9 --- /dev/null +++ b/modules/monitoring/application/controllers/ListController.php @@ -0,0 +1,208 @@ +view->tabs = $this->getTabs() + ->activate($this->action_name) + ->enableSpecialActions(); + $this->backend = Backend::getInstance($this->_getParam('backend')); + $this->view->grapher = Hook::get('grapher'); + } + + public function hostsAction() + { + $this->view->hosts = $this->backend->select()->from('status', array( + 'host_name', + 'host_state', + 'host_acknowledged', + 'host_output', + 'host_in_downtime', + 'host_handled', + 'host_last_state_change' + )); + + if ($search = $this->_getParam('search')) { + $this->_setParam('search', null); + if (strpos($search, '=') === false) { + $this->_setParam('host_name', $search); + } else { + list($key, $val) = preg_split('~\s*=\s*~', $search, 2); + if ($this->view->hosts->isValidFilterColumn($key) || $key[0] === '_') { + $this->_setParam($key, $val); + } + } + } + $this->view->hosts->applyRequest($this->_request); +$this->view->hosts->getQuery()->group('hs.host_object_id'); + if ($this->_getParam('dump') === 'sql') { + echo '
' . htmlspecialchars(wordwrap($this->view->hosts->getQuery()->dump())) . '
'; + exit; + } + + // TODO: Get rid of "preserve" + $preserve = array(); + if ($this->_getParam('sort')) { + $preserve['sort'] = $this->view->sort = $this->_getParam('sort'); + } + if ($this->_getParam('backend')) { + $preserve['backend'] = $this->_getParam('backend'); + } + $this->view->preserve = $preserve; + + if ($this->_getParam('view') === 'compact') { + $this->_helper->viewRenderer('hosts_compact'); + } + } + + public function servicesAction() + { + $state_type = $this->_getParam('_statetype', 'soft'); + if ($state_type = 'soft') { + $state_column = 'service_state'; + $state_change_column = 'service_last_state_change'; + } else { + $state_column = 'service_hard_state'; + $state_change_column = 'service_last_hard_state_change'; + } + $this->view->services = $this->backend->select() + ->from('status', array( + 'host_name', + 'service_description', + 'service_state' => $state_column, + 'service_in_downtime', + 'service_acknowledged', + 'service_handled', + 'service_output', + 'service_last_state_change' => $state_change_column + )); + + if ($search = $this->_getParam('search')) { + $this->_setParam('search', null); + if (strpos($search, '=') === false) { + $this->_setParam('service_description', $search); + } else { + list($key, $val) = preg_split('~\s*=\s*~', $search, 2); + if ($this->view->services->isValidFilterColumn($key) || $key[0] === '_') { + $this->_setParam($key, $val); + } + } + } + + $this->view->services->applyRequest($this->_request); + if ($this->_getParam('dump') === 'sql') { + echo '
' . htmlspecialchars(wordwrap($this->view->services->getQuery()->dump())) . '
'; + exit; + } + + $preserve = array(); + if ($this->_getParam('sort')) { + $preserve['sort'] = $this->view->sort = $this->_getParam('sort'); + + } + if ($this->_getParam('backend')) { + $preserve['backend'] = $this->_getParam('backend'); + } + $this->view->preserve = $preserve; + if ($this->_getParam('view') == 'compact') { + $this->_helper->viewRenderer('services-compact'); + } + } + + public function hostgroupsAction() + { + $this->view->hostgroups = $this->backend->select() + ->from('hostgroup', array( + 'hostgroup_name', + 'hostgroup_alias', + ))->applyRequest($this->_request); + } + + public function servicegroupsAction() + { + $this->view->servicegroups = $this->backend->select() + ->from('servicegroup', array( + 'servicegroup_name', + 'servicegroup_alias', + ))->applyRequest($this->_request); + } + + public function contactgroupsAction() + { + $this->view->contactgroups = $this->backend->select() + ->from('contactgroup', array( + 'contactgroup_name', + 'contactgroup_alias', + ))->applyRequest($this->_request); + } + + public function contactsAction() + { + $this->view->contacts = $this->backend->select() + ->from('contact', array( + 'contact_name', + 'contact_alias', + 'contact_email', + 'contact_pager' + ))->applyRequest($this->_request); + } + + // TODO: Search helper playground + public function searchAction() + { + $data = array( + 'service_description', + 'service_state', + 'service_acknowledged', + 'service_handled', + 'service_output', + // '_host_satellite', + 'service_last_state_change' + ); + echo json_encode($data); + exit; + } + + protected function getTabs() + { + $tabs = $this->widget('tabs'); + $tabs->add('services', array( + 'title' => 'Services', + 'icon' => 'img/classic/service.png', + 'url' => 'monitoring/list/services', + )); + $tabs->add('hosts', array( + 'title' => 'Hosts', + 'icon' => 'img/classic/server.png', + 'url' => 'monitoring/list/hosts', + )); + $tabs->add('hostgroups', array( + 'title' => 'Hostgroups', + 'icon' => 'img/classic/servers-network.png', + 'url' => 'monitoring/list/hostgroups', + )); + $tabs->add('servicegroups', array( + 'title' => 'Servicegroups', + 'icon' => 'img/classic/servers-network.png', + 'url' => 'monitoring/list/servicegroups', + )); + $tabs->add('contacts', array( + 'title' => 'Contacts', + 'icon' => 'img/classic/servers-network.png', + 'url' => 'monitoring/list/contacts', + )); + $tabs->add('contactgroups', array( + 'title' => 'Contactgroups', + 'icon' => 'img/classic/servers-network.png', + 'url' => 'monitoring/list/contactgroups', + )); + return $tabs; + } +} diff --git a/modules/monitoring/application/controllers/ShowController.php b/modules/monitoring/application/controllers/ShowController.php new file mode 100644 index 000000000..66e7e103d --- /dev/null +++ b/modules/monitoring/application/controllers/ShowController.php @@ -0,0 +1,296 @@ +_getParam('host'); + $service = $this->_getParam('service'); + if ($host !== null) { + // TODO: $this->assertPermission('host/read', $host); + } + if ($service !== null) { + // TODO: $this->assertPermission('service/read', $service); + } + // TODO: don't allow wildcards + + $this->backend = Backend::getInstance($this->_getParam('backend')); + if ($service !== null && $service !== '*') { + $this->view->service = $this->backend->fetchService($host, $service); + } + if ($host !== null) { + $this->view->host = $this->backend->fetchHost($host); + } + $this->view->compact = $this->_getParam('view') === 'compact'; + $this->view->tabs = $this->createTabs(); + + // If ticket hook: + $params = array(); + if ($host !== null) { + $params['host'] = $this->view->host->host_name; + } + if ($service !== null) { + $params['service'] = $this->view->service->service_description; + } + if (Hook::has('ticket')) { + $params['ticket'] = '__ID__'; + $this->view->ticket_link = preg_replace( + '~__ID__~', + '\$1', + $this->view->qlink('#__ID__', + 'monitoring/show/ticket', + $params + ) + ); + // TODO: Global ticket pattern config (or per environment) + $this->view->ticket_pattern = '~#(\d{4,6})~'; + } + } + + public function serviceAction() + { + Benchmark::measure('Entered service action'); + $this->view->active = 'service'; + $this->view->tabs->activate('service')->enableSpecialActions(); + + if ($grapher = Hook::get('grapher')) { + if ($grapher->hasGraph( + $this->view->host->host_name, + $this->view->service->service_description + )) { + $this->view->preview_image = $grapher->getPreviewImage( + $this->view->host->host_name, + $this->view->service->service_description + ); + } + } + + $this->view->contacts = $this->backend->select() + ->from('contact', array( + 'contact_name', + 'contact_alias', + 'contact_email', + 'contact_pager', + )) + ->where('service_host_name', $this->view->host->host_name) + ->where('service_description', $this->view->service->service_description) + ->fetchAll(); + + $this->view->contactgroups = $this->backend->select() + ->from('contactgroup', array( + 'contactgroup_name', + 'contactgroup_alias', + )) + ->where('service_host_name', $this->view->host->host_name) + ->where('service_description', $this->view->service->service_description) + ->fetchAll(); + + $this->view->comments = $this->backend->select() + ->from('comment', array( + 'comment_timestamp', + 'comment_author', + 'comment_data', + 'comment_type', + )) + ->where('service_host_name', $this->view->host->host_name) + ->where('service_description', $this->view->service->service_description) + ->fetchAll(); + + $this->view->customvars = $this->backend->select() + ->from('customvar', array( + 'varname', + 'varvalue' + )) + ->where('varname', '-*PW*,-*PASS*') + ->where('host_name', $this->view->host->host_name) + ->where('service_description', $this->view->service->service_description) + ->where('object_type', 'service') + ->fetchPairs(); + Benchmark::measure('Service action done'); + } + + public function hostAction() + { + $this->view->active = 'host'; + $this->view->tabs->activate('host')->enableSpecialActions(); + + if ($grapher = Hook::get('grapher')) { + if ($grapher->hasGraph($this->view->host->host_name)) { + $this->view->preview_image = $grapher->getPreviewImage( + $this->view->host->host_name + ); + } + } + + $this->view->hostgroups = $this->backend->select() + ->from('hostgroup', array( + 'hostgroup_name', + 'hostgroup_alias' + )) + ->where('host_name', $this->view->host->host_name) + ->fetchPairs(); + + $this->view->contacts = $this->backend->select() + ->from('contact', array( + 'contact_name', + 'contact_alias', + 'contact_email', + 'contact_pager', + )) + ->where('host_name', $this->view->host->host_name) + ->fetchAll(); + + $this->view->contactgroups = $this->backend->select() + ->from('contactgroup', array( + 'contactgroup_name', + 'contactgroup_alias', + )) + ->where('host_name', $this->view->host->host_name) + ->fetchAll(); + + $this->view->comments = $this->backend->select() + ->from('comment', array( + 'comment_timestamp', + 'comment_author', + 'comment_data', + 'comment_type', + )) + ->where('host_name', $this->view->host->host_name) + ->fetchAll(); + + $this->view->customvars = $this->backend->select() + ->from('customvar', array( + 'varname', + 'varvalue' + )) + ->where('varname', '-*PW*,-*PASS*') + ->where('host_name', $this->view->host->host_name) + ->where('object_type', 'host') + ->fetchPairs(); + } + + public function historyAction() + { + if ($this->view->host) { + $this->view->tabs->activate('history')->enableSpecialActions(); + } + $this->view->history = $this->backend->select() + ->from('eventHistory', array( + 'object_type', + 'host_name', + 'service_description', + 'timestamp', + 'state', + 'attempt', + 'max_attempts', + 'output', + 'type' + ))->applyRequest($this->_request); + + + $this->view->preserve = $this->view->history->getAppliedFilter()->toParams(); + if ($this->_getParam('dump') === 'sql') { + echo '
' . htmlspecialchars($this->view->history->getQuery()->dump()) . '
'; + exit; + } + if ($this->_getParam('sort')) { + $this->view->preserve['sort'] = $this->_getParam('sort'); + } + } + + public function servicesAction() + { + // Ugly and slow: + $this->view->services = $this->view->action('services', 'list', 'monitoring', array( + 'host_name' => $this->view->host->host_name, + //'sort', 'service_description' + )); + } + + public function ticketAction() + { + $this->view->tabs->activate('ticket')->enableSpecialActions(); + $id = $this->_getParam('ticket'); + // Still hardcoded, TODO: get URL: + if (Hook::has('ticket')) { + $ticketModule = 'rt'; + $this->render(); + $this->_forward('ticket', 'show', $ticketModule, array( + 'id' => $id + )); + } + } + + protected function createTabs() + { + $tabs = $this->widget('tabs'); + if ( ! $this->view->host) { + return $tabs; + } + $params = array( + 'host' => $this->view->host->host_name, + ); + if ($backend = $this->_getParam('backend')) { + $params['backend'] = $backend; + } + if (isset($this->view->service)) { + $params['service'] = $this->view->service->service_description; + $hostParams = $params + array('active' => 'host'); + } else { + $hostParams = $params; + } + $tabs->add('host', array( + 'title' => 'Host', + 'icon' => 'img/classic/server.png', + 'url' => 'monitoring/show/host', + 'urlParams' => $hostParams, + )); + $tabs->add('services', array( + 'title' => 'Services', + 'icon' => 'img/classic/service.png', + 'url' => 'monitoring/show/services', + 'urlParams' => $params, + )); + if (isset($params['service'])) { + $tabs->add('service', array( + 'title' => 'Service', + 'icon' => 'img/classic/service.png', + 'url' => 'monitoring/show/service', + 'urlParams' => $params, + )); + } + $tabs->add('history', array( + 'title' => 'History', + 'icon' => 'img/classic/history.gif', + 'url' => 'monitoring/show/history', + 'urlParams' => $params, + )); + if ($this->action_name === 'ticket') { + $tabs->add('ticket', array( + 'title' => 'Ticket', + 'icon' => 'img/classic/ticket.gif', + 'url' => 'monitoring/show/ticket', + 'urlParams' => $params + array('ticket' => $this->_getParam('ticket')), + )); + } +/* + $tabs->add('contacts', array( + 'title' => 'Contacts', + 'icon' => 'img/classic/customer.png', + 'url' => 'monitoring/detail/contacts', + 'urlParams' => $params, + )); +*/ + // TODO: Inventory 'img/classic/facts.gif' + // Ticket 'img/classic/ticket.gif' + // Customer 'img/classic/customer.png' + return $tabs; + } +} diff --git a/modules/monitoring/application/controllers/SummaryController.php b/modules/monitoring/application/controllers/SummaryController.php new file mode 100644 index 000000000..fdda65a22 --- /dev/null +++ b/modules/monitoring/application/controllers/SummaryController.php @@ -0,0 +1,70 @@ +backend = Backend::getInstance($this->_getParam('backend')); + $this->view->compact = $this->_getParam('view') === 'compact'; + $this->view->tabs = $this->getTabs(); + } + + protected function getTabs() + { + $tabs = $this->widget('tabs'); + $tabs->add('hostgroup', array( + 'title' => 'Hostgroups', + 'url' => 'monitoring/summary/group', + 'urlParams' => array('by' => 'hostgroup'), + )); + $tabs->add('servicegroup', array( + 'title' => 'Servicegroups', + 'url' => 'monitoring/summary/group', + 'urlParams' => array('by' => 'servicegroup'), + )); + $tabs->activate($this->_getParam('by', 'hostgroup')); + return $tabs; + } + + public function historyAction() + { + $this->_helper->viewRenderer('history'); + } + + public function groupAction() + { + if ($this->_getParam('by') === 'servicegroup') { + $view = 'servicegroupsummary'; + } else { + $view = 'hostgroupsummary'; + } + if (! $this->backend->hasView($view)) { + $this->view->backend = $this->backend; + $this->view->view_name = $view; + $this->_helper->viewRenderer('backend-is-missing'); + return; + } + + $this->view->preserve = array( + 'problems' => $this->_getParam('problems') ? 'true' : 'false', + 'search' => $this->_getParam('search') + ); + $query = $this->backend->select()->from($view); + $query->where('problems', $this->_getParam('problems') ? 'true' : 'false'); + //$query->where('ss.current_state > 0'); + $query->where('search', $this->_getParam('search')); + + // echo '
' . $query->dump() . '
'; exit; + $this->view->summary = $query->paginate(); + + } + +} + diff --git a/modules/monitoring/application/views/helpers/CommandArguments.php b/modules/monitoring/application/views/helpers/CommandArguments.php new file mode 100644 index 000000000..c0dfc5cca --- /dev/null +++ b/modules/monitoring/application/views/helpers/CommandArguments.php @@ -0,0 +1,19 @@ +%s: %s\n"; + for ($i = 1; $i < count($parts); $i++) { + $parts[$i] = sprintf($row, '$ARG' . $i . '$', $parts[$i]); + } + array_shift($parts); + return "
\n" . implode("\n", $parts) . "
\n"; + } +} + diff --git a/modules/monitoring/application/views/helpers/MonitoringState.php b/modules/monitoring/application/views/helpers/MonitoringState.php new file mode 100644 index 000000000..0adf1135e --- /dev/null +++ b/modules/monitoring/application/views/helpers/MonitoringState.php @@ -0,0 +1,19 @@ + 'pending', null => 'pending'); + private $hoststates = array('up', 'down', 'unreachable', 99 => 'pending', null => 'pending'); + + public function monitoringState($object, $type = 'service') { + + if ($type === 'service') { + return $this->servicestates[$object->service_state]; + } else if ($type === 'host') { + return $this->hoststates[$object->host_state]; + } + } + +} + diff --git a/modules/monitoring/application/views/helpers/Perfdata.php b/modules/monitoring/application/views/helpers/Perfdata.php new file mode 100644 index 000000000..f7ae4672f --- /dev/null +++ b/modules/monitoring/application/views/helpers/Perfdata.php @@ -0,0 +1,84 @@ +getAll(); + $perfdata = preg_replace('~\'([^\']+)\'~e', "str_replace(' ', '\'', '$1')", $perfdata); + $parts = preg_split('~\s+~', $perfdata, -1, PREG_SPLIT_NO_EMPTY); + + $result = ''; + if ($compact === true) { + $compact = 5; + } + if ($compact && count($parts) > $compact) { + $parts = array_slice($parts, 0, $compact); + } + foreach ($parts as $part) { + if (strpos($part, '=') === false) continue; + list($name, $vals) = preg_split('~=~', $part, 2); + $name = str_replace("'", ' ', $name); + $parts = preg_split('~;~', $vals, 5); + while (count($parts) < 5) $parts[] = null; + list($val, $warn, $crit, $min, $max) = $parts; + + $unit = ''; + if (preg_match('~^([\d+\.]+)([^\d]+)$~', $val, $m)) { + $unit = $m[2]; + $val = $m[1]; + } else { + continue; + } + if ($unit == 'c') continue; // Counter pie graphs are not useful + if ($unit == '%') { + if (! $min ) $min = 0; + if (! $max) $max = 100; + } else { + if (! $max && $crit > 0) $max = $crit; + //return ''; + } + if (! $max) continue; + $green = 0; + $orange = 0; + $red = 0; + $gray = $max - $val; + if ($val < $warn) $green = $val; + elseif ($val < $crit) $orange = $val; + else $red = $val; + if ($compact) { + $result .= '
' + . implode(',', array($green, $orange, $red, $gray)) + . '
'; + } else { + $result .= '
' + . implode(',', array($green, $orange, $red, $gray)) + . '
' + . htmlspecialchars($name) + . '' + . htmlspecialchars($ps[$name]->getFormattedValue() /* $val*/) + //. htmlspecialchars($unit) + . ''; + } + } + if (! $compact && $result !== '') { + $result = '' . $result . '
'; + } + return $result; + } +} + diff --git a/modules/monitoring/application/views/helpers/PluginOutput.php b/modules/monitoring/application/views/helpers/PluginOutput.php new file mode 100644 index 000000000..9596bbad8 --- /dev/null +++ b/modules/monitoring/application/views/helpers/PluginOutput.php @@ -0,0 +1,87 @@ +]*>~', $output)) { + // HTML + $output = preg_replace('~getPurifier()->purify($output) + ); + } elseif (preg_match('~\\\n~', $output)) { + // Plaintext + $output = '
'
+               . preg_replace(
+              '~\\\n~', "\n", preg_replace(
+                '~\\\n\\\n~', "\n",
+                preg_replace('~\[OK\]~', '[OK]',
+                 preg_replace('~\[WARNING\]~', '[WARNING]',
+                  preg_replace('~\[CRITICAL\]~', '[CRITICAL]',
+                   preg_replace('~\@{6,}~', '@@@@@@',
+                     $this->view->escape(wordwrap($output, 72, ' ', true))
+                ))))
+              )
+            ) . '
'; + } else { + $output = preg_replace('~\@{6,}~', '@@@@@@', + $this->view->escape(wordwrap($output, 72, ' ', true)) + ); + } + $output = $this->fixLinks($output); + return $output; + } + + protected function fixLinks($html) + { + + + $ret = array(); + $dom = new DOMDocument; + $dom->loadXML('
' . $html . '
', LIBXML_NOERROR | LIBXML_NOWARNING); + $dom->preserveWhiteSpace = false; + $links = $dom->getElementsByTagName('a'); + foreach ($links as $tag) + { + $href = $tag->getAttribute('href'); + if (preg_match('~^/cgi\-bin/status\.cgi\?(.+)$~', $href, $m)) { + parse_str($m[1], $params); + if (isset($params['host'])) { + $tag->setAttribute('href', $this->view->baseUrl( + '/monitoring/detail/show?host=' . urlencode($params['host'] + ))); + } + } else { + // ignoring + } + //$ret[$tag->getAttribute('href')] = $tag->childNodes->item(0)->nodeValue; + } + return substr($dom->saveHTML(), 5, -7); + } + + protected function getPurifier() + { + if (self::$purifier === null) { + // require_once 'vendor/htmlpurifier/library/HTMLPurifier.auto.php'; + require_once 'vendor/htmlpurifier-4.5.0-lite/library/HTMLPurifier.auto.php'; + $config = HTMLPurifier_Config::createDefault(); + $config->set('Core.EscapeNonASCIICharacters', true); + $config->set('HTML.Allowed', 'p,br,b,a[href],i,table,tr,td[colspan],div[class]'); + // This avoids permission problems: + // $config->set('Core.DefinitionCache', null); + $config->set('Cache.DefinitionImpl', null); + // TODO: Use a cache directory: + // $config->set('Cache.SerializerPath', '/var/spool/whatever'); + + // $config->set('URI.Base', 'http://www.example.com'); + // $config->set('URI.MakeAbsolute', true); + // $config->set('AutoFormat.AutoParagraph', true); + self::$purifier = new HTMLPurifier($config); + } + return self::$purifier; + } +} + diff --git a/modules/monitoring/application/views/helpers/ResolveComments.php b/modules/monitoring/application/views/helpers/ResolveComments.php new file mode 100644 index 000000000..e0432aaa1 --- /dev/null +++ b/modules/monitoring/application/views/helpers/ResolveComments.php @@ -0,0 +1,32 @@ + array("self::renderDiskPie") + ); + + public function renderServicePerfdata($service) + { + if (isset(self::$RENDERMAP[$service->check_command])) { + $fn = self::$RENDERMAP[$service->check_command]; + $fn($service); + } + } + + public static function renderDiskPie($service) { + $perfdata = $service->performance_data; + if(!$perfdata) + return ""; + + } +} diff --git a/modules/monitoring/application/views/scripts/list/contactgroups.phtml b/modules/monitoring/application/views/scripts/list/contactgroups.phtml new file mode 100644 index 000000000..a4841c008 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/contactgroups.phtml @@ -0,0 +1,4 @@ +tabs ?> +
+escape(print_r($this->contactgroups->fetchAll(), 1)) ?>
+
diff --git a/modules/monitoring/application/views/scripts/list/contacts.phtml b/modules/monitoring/application/views/scripts/list/contacts.phtml new file mode 100644 index 000000000..bacc8f878 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/contacts.phtml @@ -0,0 +1,4 @@ +tabs ?> +
+escape(print_r($this->contacts->fetchAll(), 1)) ?>
+
diff --git a/modules/monitoring/application/views/scripts/list/hostgroups.phtml b/modules/monitoring/application/views/scripts/list/hostgroups.phtml new file mode 100644 index 000000000..df832cf16 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/hostgroups.phtml @@ -0,0 +1,4 @@ +tabs ?> +
+escape(print_r($this->hostgroups->fetchAll(), 1)) ?>
+
diff --git a/modules/monitoring/application/views/scripts/list/hosts-compact.phtml b/modules/monitoring/application/views/scripts/list/hosts-compact.phtml new file mode 100644 index 000000000..85ccf8282 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/hosts-compact.phtml @@ -0,0 +1,59 @@ +hosts)): ?> +- no hosts is matching this filter - + +hosts->paginate(); ?> + + + + + + + + + +host_acknowledged) { + $icons['ack.gif'] = 'Problem has been acknowledged'; +} + +if ($host->host_in_downtime) { + $icons['downtime.gif'] = 'Host is in a scheduled downtime'; +} + +$state_classes = array($this->monitoringState($host, 'host')); + +if ($host->host_acknowledged || $host->host_in_downtime) { + $state_classes[] = 'handled'; +} +if ($host->host_last_state_change > (time() - 600)) { + $state_classes[] = 'new'; +} +$state_title = strtoupper($this->monitoringState($host, 'host')) + . ' since ' + . date('Y-m-d H:i:s', $host->host_last_state_change); +?> + + + + + + +
StateHost
qlink( + $this->timeSince($host->host_last_state_change), + 'monitoring/show/history', + array('host' => $host->host_name), + array('quote' => false)) ?> $alt) + echo $this->img('img/classic/' . $icon, array( + 'class' => 'icon', + 'title' => $alt + )); +?> +qlink( + $host->host_name, + 'monitoring/show/host', + array('host' => $host->host_name), + array('class' => 'row-action') + ) ?>
diff --git a/modules/monitoring/application/views/scripts/list/hosts.phtml b/modules/monitoring/application/views/scripts/list/hosts.phtml new file mode 100644 index 000000000..7336baa8a --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/hosts.phtml @@ -0,0 +1,125 @@ +tabs ?> +hosts->limit(10); +$hosts = $this->hosts->paginate(); + +function getRowProperties(& $host, $scope) { + + $icons = array(); + if ($host->host_acknowledged) { + $icons['ack.gif'] = 'Problem has been acknowledged'; + } + + if ($host->host_in_downtime) { + $icons['downtime.gif'] = 'Host is in a scheduled downtime'; + } + + $state_classes = array($scope->monitoringState($host, 'host')); + + if ($host->host_acknowledged || $host->host_in_downtime) { + $state_classes[] = 'handled'; + } + if ($host->host_last_state_change > (time() - 600)) { + $state_classes[] = 'new'; + } + $state_title = strtoupper($scope->monitoringState($host,"host")) + . ' since ' + . date('Y-m-d H:i:s', $host->host_last_state_change); + return array($icons,$state_classes,$state_title); +} +// filter start +$fparams = $this->hosts->getAppliedFilter()->toParams(); +if ($this->preserve === null) { + $this->preserve = $fparams; +} else { + $this->preserve += $fparams; +} + +$always = array(); +if (isset($_GET['sort'])) { + $always['sort'] = $_GET['sort']; +} +if (isset($_GET['dir'])) { + $always['dir'] = $_GET['dir']; +} +?> + +
Filters
+ $v): ?> +qlink( + 'x', + 'monitoring/list/hosts', + $this->hosts->getAppliedFilter()->without($k)->toParams() + $always, + array( + 'style' => array('color' => 'red') + ) +) ?> escape("$k = $v") ?>
+ +
+ +
+Sort by formSelect( + 'sort', + $this->sort, + array('class' => 'autosubmit'), + array( + 'host_severity' => 'Severity', + 'host_last_state_change' => 'Last state change', + 'host_name' => 'Host', + ) +) ?> + +
+ + + +
+ Sorry, no host found for this search +
+ + +paginationControl($hosts, null, null, array('preserve' => $this->preserve)) ?> + + + + + + + + + + + +
qlink( + substr(strtoupper($this->monitoringState($host,"host")), 0, 4) + . ' since
' + . $this->timeSince($host->host_last_state_change), + 'monitoring/show/history', array( + 'host' => $host->host_name + ), + array('quote' => false) +) ?>
$alt) + echo $this->img('img/classic/' . $icon, array( + 'class' => 'icon', + 'title' => $alt + )); +?> +qlink( + $host->host_name, + 'monitoring/show/host', array( + 'host' => $host->host_name + ), array('class' => 'row-action') +) ?>
+ escape(substr(strip_tags($host->host_output), 0, 10000)) +?> +
diff --git a/modules/monitoring/application/views/scripts/list/servicegroups.phtml b/modules/monitoring/application/views/scripts/list/servicegroups.phtml new file mode 100644 index 000000000..926a6dd33 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/servicegroups.phtml @@ -0,0 +1,4 @@ +tabs ?> +
+escape(print_r($this->servicegroups->fetchAll(), 1)) ?>
+
diff --git a/modules/monitoring/application/views/scripts/list/services-compact.phtml b/modules/monitoring/application/views/scripts/list/services-compact.phtml new file mode 100644 index 000000000..eaf26293a --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/services-compact.phtml @@ -0,0 +1,73 @@ +services)): ?> +
+ Sorry, no services found for this search +
+ +services->paginate(); + +?> + + + + + + + + + +service_acknowledged) { + $icons['ack.gif'] = 'Problem has been acknowledged'; +} + +if (! empty($service->service_downtimes_with_info)) { + $icons['downtime.gif'] = implode("\n", + $this->resolveComments($service->service_downtimes_with_info) + ); +} + +if (! empty($service->service_comments_with_info)) { + $icons['comment.gif'] = implode("\n", + $this->resolveComments($service->service_comments_with_info) + ); +} + +$state_classes = array($this->monitoringState($service, 'service')); +if ($service->service_handled || $service->service_acknowledged || ! empty($service->service_downtimes_with_info)) { + $state_classes[] = 'handled'; +} +if ($service->service_last_state_change > (time() - 600)) { + $state_classes[] = 'new'; +} +$state_title = date('Y-m-d H:i:s', $service->service_last_state_change); + +$params = array( + 'host' => $service->host_name, + 'service' => $service->service_description +); +if (isset($this->preserve['backend'])) { + $params['backend'] = $this->preserve['backend']; +} +?> + + + + + + +
StateService
+ qlink( + $this->timeSince($service->service_last_state_change), + 'monitoring/show/history', $params, array('quote' => false)) ?> + + qlink( + $service->host_name, + 'monitoring/show/host', + $params + ); ?>: + qlink($service->service_description, 'monitoring/show/service', $params, array('class' => 'row-action')) ?> +
\ No newline at end of file diff --git a/modules/monitoring/application/views/scripts/list/services.phtml b/modules/monitoring/application/views/scripts/list/services.phtml new file mode 100644 index 000000000..c62ed3ef6 --- /dev/null +++ b/modules/monitoring/application/views/scripts/list/services.phtml @@ -0,0 +1,154 @@ +tabs ?> +services->limit(10); +$services = $this->services->paginate(); + +function getRowProperties(&$service, &$last_host, $scope) { + if ($last_host !== $service->host_name) { + $host_col = '' . $scope->qlink( + $service->host_name, + 'monitoring/show/host', + array('host' => $service->host_name) + ) . ':' + . (isset($service->host->address) ? ' ( ' . $scope->escape($service->host->address) . ')' : '') + . ''; + $last_host = $service->host_name; + } else { + $host_col = '  '; + } + $icons = array(); + if ($service->service_acknowledged) { + $icons['ack.gif'] = 'Problem has been acknowledged'; + } + + if (! empty($service->service_in_downtime)) { + $icons['downtime.gif'] = 'Service is in a scheduled downtime'; + } + + $state_classes = array($scope->monitoringState($service)); + + if ($service->service_handled) { + $state_classes[] = 'handled'; + } + if ($service->service_last_state_change > (time() - 600)) { + $state_classes[] = 'new'; + } + $state_title = strtoupper($scope->monitoringState($service)) + . ' since ' + . date('Y-m-d H:i:s', $service->service_last_state_change); + if ($scope->grapher && $scope->grapher->hasGraph($service->host_name, $service->service_description)) { + $graph = $scope->grapher->getSmallPreviewImage( + $service->host_name, + $service->service_description + ); + } else { + $graph = ''; + } + return array($host_col,$icons,$state_classes,$state_title,$graph); +} + +$fparams = $this->services->getAppliedFilter()->toParams(); +if ($this->preserve === null) { + $this->preserve = $fparams; +} else { + $this->preserve += $fparams; +} + $last_host = null; +$always = array(); +if (isset($_GET['sort'])) { + $always['sort'] = $_GET['sort']; +} +if (isset($_GET['dir'])) { + $always['dir'] = $_GET['dir']; +} +?> +
+ +
Filters
+ $v): ?> +qlink( + 'x', + 'monitoring/list/services', + $this->services->getAppliedFilter()->without($k)->toParams() + $always, + array( + 'style' => array('color' => 'red') + ) +) ?> escape("$k = $v") ?>
+ +
+ +
+Sort by formSelect( + 'sort', + $this->sort, + array( + 'class' => 'autosubmit', + ), + array( + 'severity' => 'Severity', + 'service_last_state_change' => 'Last state change', + 'host_name' => 'Host', + 'service_description' => 'Service', + ) +) ?> + +
+
+ + +
+ Sorry, no services found for this search +
+ + +paginationControl($services, null, null, array('preserve' => $this->preserve )); ?> + + + + + + + + + + +
+ qlink( + + $service->service_state == 99 ? 'PENDING' : + substr(strtoupper($this->monitoringState($service)), 0, 4) + . ' since
' + . $this->timeSince($service->service_last_state_change), + 'monitoring/show/history', array( + 'host' => $service->host_name, + 'service' => $service->service_description + ), array('quote' => false)) ?> +
+ $alt) { + echo $this->img('img/classic/' . $icon, array( + 'class' => 'icon', + 'title' => $alt + )); + } ?> + + qlink($service->service_description, 'monitoring/show/service', array( + 'host' => $service->host_name, + 'service' => $service->service_description + ), array('class' => 'row-action') + ) + ?> + +
+     + escape(substr(strip_tags($service->service_output), 0, 900)) ?> + + +
diff --git a/modules/monitoring/application/views/scripts/show/components/comments.phtml b/modules/monitoring/application/views/scripts/show/components/comments.phtml new file mode 100644 index 000000000..09c4a5f8c --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/comments.phtml @@ -0,0 +1,25 @@ + +comments)): ?> +comments as $comment) { + if ($this->ticket_pattern) { + $text = preg_replace( + $this->ticket_pattern, + $this->ticket_link, + $this->escape($comment->comment_data) + ); + } else { + $text = $this->escape($comment->comment_data); + } + $list[] = sprintf( + '[%s] %s (%s): %s', + $this->escape($comment->comment_author), + $this->format()->timeSince($comment->comment_timestamp), + $comment->comment_type, + $text + ); +} +?> +Comments:
', $list) ?>
+ diff --git a/modules/monitoring/application/views/scripts/show/components/contacts.phtml b/modules/monitoring/application/views/scripts/show/components/contacts.phtml new file mode 100644 index 000000000..9ebe38553 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/components/contacts.phtml @@ -0,0 +1,24 @@ +contacts)): ?> +contacts as $contact) { + $list[] = $this->qlink($contact->contact_alias, 'monitoring/show/contact', array( + 'contact_name' => $contact->contact_name + )); +} +?> +Contacts:
+ + +contactgroups)): ?> +contactgroups as $contactgroup) { + $list[] = $this->qlink($contactgroup->contactgroup_alias, 'monitoring/show/contactgroup', array( + 'contactgroup_name' => $contactgroup->contactgroup_name + )); +} +?> +Contactgroups:
+ + diff --git a/modules/monitoring/application/views/scripts/show/header.phtml b/modules/monitoring/application/views/scripts/show/header.phtml new file mode 100644 index 000000000..d955fdf80 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/header.phtml @@ -0,0 +1,30 @@ +service) && $this->tabs->getActiveName() !== 'host'; +?> +compact): ?> +tabs ?> + + + + + + + + + + + + + + +
escape($this->host->host_name) ?>host->host_address && $this->host->host_address !== $this->host->host_name): ?> + (escape($this->host->host_address) ?>) + + > + util()->getHostStateName($this->host->host_state); ?>
+ since timeSince($this->host->host_last_state_change) ?> +
Service: escape($this->service->service_description) ?> + util()->getServiceStateName($this->service->service_state); ?>
+ since timeSince($this->service->service_last_state_change) ?> +
Host state
diff --git a/modules/monitoring/application/views/scripts/show/history.phtml b/modules/monitoring/application/views/scripts/show/history.phtml new file mode 100644 index 000000000..1c8a2e3ad --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/history.phtml @@ -0,0 +1,99 @@ +partial('show/header.phtml', array( + 'host' => $this->host, + 'service' => $this->service, + 'tabs' => $this->tabs +)); ?>history->limit(10); +$history = $this->history->paginate(); + +?> + + +There are no matching history entries right now + +paginationControl($history, null, null, array('preserve' => $this->preserve)); ?> + + + +object_type == 'host') { + $states = array('up', 'down', 'unreachable', 'unknown', 99 => 'pending', null => 'pending'); +} else { + $states = array('ok', 'warning', 'critical', 'unknown', 99 => 'pending', null => 'pending'); +} + +$row_class = array_key_exists($event->state, $states) ? $states[$event->state] : 'invalid'; + +?> + +host)): ?> + + +service)): ?> + + + + + + +
timestamp ) ?>escape($event->host_name) ?>host)): ?> +qlink( + $event->service_description, + 'monitoring/show/service', + array( + 'host' => $this->host->host_name, + 'service' => $event->service_description + ) +) ?> + +escape($event->service_description) ?> + + 16, + 'height' => 16, +); +switch ($event->type) { + case 'notify': + echo $this->img('img/classic/mail-notification.png', $imgparams); + break; + case 'comment': + echo $this->img('img/classic/comment.gif', $imgparams); + break; + case 'ack': + echo $this->img('img/classic/ack.gif', $imgparams); + break; + case 'dt_comment': + echo $this->img('img/classic/downtime.gif', $imgparams); + break; + case 'flapping': + echo $this->img('img/classic/flapping.gif', $imgparams); + break; + case 'hard_state': + echo $this->img('img/classic/state-hard.png', $imgparams); + break; + case 'soft_state': + echo $this->img('img/classic/state-soft.png', $imgparams); + break; + case 'dt_start': + echo $this->img('img/classic/downtime-start.png', $imgparams); + echo ' Downtime start'; + break; + case 'dt_end': + echo $this->img('img/classic/downtime-end.png', $imgparams); + echo ' Downtime end'; + break; +} + ?> +attempt !== null): ?> + [ attempt ?>/max_attempts ?> ] + + ticket_pattern ? preg_replace( + $this->ticket_pattern, + $this->ticket_link, + $this->pluginOutput($event->output) + ) : $this->pluginOutput($event->output) ?> +
+ diff --git a/modules/monitoring/application/views/scripts/show/host.phtml b/modules/monitoring/application/views/scripts/show/host.phtml new file mode 100644 index 000000000..8d4dfb306 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/host.phtml @@ -0,0 +1,40 @@ +partial('show/header.phtml', array( + 'host' => $this->host, + 'service' => $this->service, + 'tabs' => $this->tabs, + 'compact' => $this->compact +)); ?> +expandable( + $this->pluginOutput($this->host->host_output), + $this->pluginOutput($this->host->host_long_output) +) ?> +expandable( + 'Command: ' . array_shift(preg_split('|!|', $this->host->host_check_command)), + $this->commandArguments($this->host->host_check_command) +) ?> +hostgroups)): ?> +hostgroups as $name => $alias) { + $list[] = $this->qlink($alias, 'monitoring/list/services', array( + 'hostgroups' => $name + )); +} +?> +Hostgroups:
+ +render('show/components/contacts.phtml') ?> +render('show/components/comments.phtml') ?> + +customvars as $name => $value): ?> +escape($name) ?>: escape($value) ?>
+ +host->host_perfdata): ?> +expandable( + 'Performance data', + $this->perfdata($this->host->host_perfdata), + array('collapsed' => false) +) ?> + +preview_image ?> + diff --git a/modules/monitoring/application/views/scripts/show/legacy-service.phtml b/modules/monitoring/application/views/scripts/show/legacy-service.phtml new file mode 100644 index 000000000..d528c3c12 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/legacy-service.phtml @@ -0,0 +1,117 @@ + + +expandable( + $this->translate('Check statistics'), + 'sdfas' +) ?> + + + +service->service_comments) && is_array($this->service->service_comments)): ?> + + diff --git a/modules/monitoring/application/views/scripts/show/service.phtml b/modules/monitoring/application/views/scripts/show/service.phtml new file mode 100644 index 000000000..9e78f98d4 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/service.phtml @@ -0,0 +1,31 @@ +partial('show/header.phtml', array( + 'host' => $this->host, + 'service' => $this->service, + 'tabs' => $this->tabs, + 'compact' => $this->compact +)); ?> +expandable( + $this->pluginOutput($this->service->service_output), + $this->pluginOutput($this->service->service_long_output) +) ?> + +render('show/components/contacts.phtml') ?> +render('show/components/comments.phtml') ?> + +customvars as $name => $value): ?> +escape($name) ?>: escape($value) ?>
+ +expandable( + 'Command: ' . array_shift(preg_split('|!|', $this->service->service_check_command)), + $this->commandArguments($this->service->service_check_command) +) ?> +service->service_perfdata): ?> +expandable( + 'Performance data:', + $this->perfdata($this->service->service_perfdata), + array('collapsed' => false) +) ?> + + +partial('show/legacy-service.phtml', array('service' => $this->service)) ?> +preview_image ?> diff --git a/modules/monitoring/application/views/scripts/show/services.phtml b/modules/monitoring/application/views/scripts/show/services.phtml new file mode 100644 index 000000000..84a566277 --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/services.phtml @@ -0,0 +1 @@ +services ?> diff --git a/modules/monitoring/application/views/scripts/show/ticket.phtml b/modules/monitoring/application/views/scripts/show/ticket.phtml new file mode 100644 index 000000000..b37eda8db --- /dev/null +++ b/modules/monitoring/application/views/scripts/show/ticket.phtml @@ -0,0 +1,2 @@ +tabs ?> + diff --git a/modules/monitoring/application/views/scripts/summary/group.phtml b/modules/monitoring/application/views/scripts/summary/group.phtml new file mode 100644 index 000000000..8c9547dc5 --- /dev/null +++ b/modules/monitoring/application/views/scripts/summary/group.phtml @@ -0,0 +1,167 @@ +compact): ?> +tabs ?> + +summary)): ?> +There are no such services right now + + +compact && $this->summary instanceof \Zend_Paginator): ?> +paginationControl($this->summary, null, null, array('preserve' => $this->preserve)); ?> + + + + + + + + + + compact): ?> + + + +summary as $row): ?> +critical > 0) { + $class_critical = 'critical'; + $html_critical = $row->critical; + if ($row->critical_ack + $row->critical_dt > 0) { + $html_critical = sprintf( + '%s (%s/%s)', + $row->critical, + $row->critical_ack, + $row->critical_dt + ); + } + if ($name_class === null) $name_class = 'critical'; +} elseif ($row->critical_dt + $row->critical_ack > 0) { + $class_critical = 'critical handled'; + $html_critical = sprintf( + '%s / %s', + $row->critical_ack, + $row->critical_dt + ); + if ($name_class === null) $name_class = 'critical handled'; +} + +if ($row->unknown > 0) { + $class_unknown = 'unknown'; + $html_unknown = $row->unknown; + if ($row->unknown_ack + $row->unknown_dt > 0) { + $html_unknown .= sprintf( + ' (%s/%s)', + $row->unknown, + $row->unknown_ack, + $row->unknown_dt + ); + } + if ($name_class === null) $name_class = 'unknown'; +} elseif ($row->unknown_dt + $row->unknown_ack > 0) { + $class_unknown = 'unknown handled'; + $html_unknown = sprintf( + '%s / %s', + $row->unknown_ack, + $row->unknown_dt + ); + if ($name_class === null) $name_class = 'unknown handled'; +} + +if ($row->warning > 0) { + $class_warning = 'warning'; + $html_warning = $row->warning; + if ($row->warning_ack + $row->warning_dt > 0) { + $html_warning .= sprintf( + ' (%s/%s)', + $row->warning, + $row->warning_ack, + $row->warning_dt + ); + } + if ($name_class === null) $name_class = 'warning'; +} elseif ($row->warning_dt + $row->warning_ack > 0) { + $class_warning = 'warning handled'; + $html_warning = sprintf( + '%s / %s', + $row->warning_ack, + $row->warning_dt + ); + if ($name_class === null) $name_class = 'warning handled'; +} + + +if ($row->ok > 0) { + $class_ok = 'ok'; + if (isset($row->hostgroup_name)) { + $html_ok = $this->qlink($row->ok, 'monitoring/list/services', array( + 'hostgroups' => $row->hostgroup_name + )); + } else { + $html_ok = $this->qlink($row->ok, 'monitoring/list/services', array( + 'servicegroups' => $row->servicegroup_name + )); + } + if ($name_class === null) $name_class = 'ok'; +} + + +if (isset($row->hostgroup_name)) { + if ($name_class === 'ok') { + $name_html = $this->qlink($row->hostgroup_name, 'monitoring/list/services', array( + 'hostgroups' => $row->hostgroup_name + )); + } else { + $name_html = $this->qlink($row->hostgroup_name, 'monitoring/list/services', array( + 'hostgroups' => $row->hostgroup_name, + 'problems' => '1', + 'sort' => 'severity' + )); + } +} else { + if ($name_class === 'ok') { + $name_html = $this->qlink($row->servicegroup_name, 'monitoring/list/services', array( + 'servicegroups' => $row->servicegroup_name + )); + } else { + $name_html = $this->qlink($row->servicegroup_name, 'monitoring/list/services', array( + 'servicegroups' => $row->servicegroup_name, + 'problems' => '1', + 'sort' => 'severity' + )); + } + +} +?> + + + + + + + compact): ?> + + + +
 CriticalUnknownWarningOK
+ +compact): ?>more diff --git a/modules/monitoring/config/backends.ini b/modules/monitoring/config/backends.ini new file mode 100644 index 000000000..3fabbbf5d --- /dev/null +++ b/modules/monitoring/config/backends.ini @@ -0,0 +1,9 @@ + +[localdb] +type = ido +host = localhost +user = "icinga-idoutils" +pass = "***" +db = "icinga" + + diff --git a/modules/monitoring/config/instances.ini b/modules/monitoring/config/instances.ini new file mode 100644 index 000000000..18213f142 --- /dev/null +++ b/modules/monitoring/config/instances.ini @@ -0,0 +1,2 @@ +[icinga] +path=/usr/local/icinga/var/rw/icinga.cmd diff --git a/modules/monitoring/config/menu.ini b/modules/monitoring/config/menu.ini new file mode 100644 index 000000000..bd902fd2e --- /dev/null +++ b/modules/monitoring/config/menu.ini @@ -0,0 +1,12 @@ +[menu] +Issues.title = "Issues" ; Extended version +Issues.route = "/monitoring/list/services?problems=1&sort=severity" ; Explicit route +Issues.key = "issues" ; When this key is set in the controller, the item is active + +Changes.title = "Recent Changes" +Changes.route = "/monitoring/list/services?sort=service_last_state_change" +_1 = 1 ;Spacer after this section + +Hosts = "/monitoring/list/hosts" +Services = "/monitoring/list/services" +Summaries = "/monitoring/summary/group/by/hostgroup" diff --git a/modules/monitoring/library/Monitoring/Backend.php b/modules/monitoring/library/Monitoring/Backend.php new file mode 100644 index 000000000..937ec48cc --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend.php @@ -0,0 +1,84 @@ +backends; + $backends = $config->backends; + foreach ($backends as $name => $config) { + // TODO: Check if access to this backend is allowed + self::$backendConfigs[$name] = $config; + } + } + return self::$backendConfigs; + } + + public function getBackend($name = null) + { + if (! array_key_exists($name, self::$instances)) { + if ($name === null) { + $name = self::getDefaultName(); + } else { + if (! self::exists($name)) { + throw new Exception(sprintf( + 'There is no such backend: "%s"', + $name + )); + } + } + + $config = self::$backendConfigs[$name]; + $type = $config->type; + $type[0] = strtoupper($type[0]); + $class = '\\Icinga\\Monitoring\\Backend\\' . $type; + self::$instances[$name] = new $class($config); + } + return self::$instances[$name]; + } + + public static function getInstance($name = null) + { + if (array_key_exists($name, self::$instances)) { + return self::$instances[$name]; + } else { + if ($name === null) { + // TODO: Remove this, will be chosen by Environment + $name = Session::getInstance()->backend; + } + return self::getBackend($name); + } + } +} diff --git a/modules/monitoring/library/Monitoring/Backend/AbstractBackend.php b/modules/monitoring/library/Monitoring/Backend/AbstractBackend.php new file mode 100644 index 000000000..0dcdb278a --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/AbstractBackend.php @@ -0,0 +1,199 @@ +config = $config; + $this->init(); + } + + protected function init() + { + } + + /** + * Dummy function for fluent code + * + * return self + */ + public function select() + { + return $this; + } + + /** + * Create a Query object instance for given virtual table and desired fields + * + * Leave fields empty to get all available properties + * + * @param string Virtual table name + * @param array Fields + * return self + */ + public function from($virtual_table, $fields = array()) + { + $classname = $this->tableToClassName($virtual_table); + if (! class_exists($classname)) { + throw new ProgrammingError( + sprintf( + 'Asking for invalid virtual table %s', + $classname + ) + ); + } + $query = new $classname($this, $fields); + return $query; + } + + public function hasView($virtual_table) + { + // TODO: This is no longer enough, have to check for Query right now + return class_exists($this->tableToClassName($virtual_table)); + } + + protected function tableToClassName($virtual_table) + { + return 'Icinga\\Monitoring\\View\\' + // . $this->getName() + // . '\\' + . ucfirst($virtual_table) + . 'View'; + } + + public function getName() + { + return preg_replace('~^.+\\\(.+?)$~', '$1', get_class($this)); + } + + public function __toString() + { + return $this->getName(); + } + + + + // UGLY temporary host fetch + public function fetchHost($host) + { + $select = $this->select() + ->from('status', array( + 'host_name', + 'host_address', + 'host_state', + 'host_handled', + 'host_in_downtime', + 'host_acknowledged', + 'host_check_command', + 'host_last_state_change', + 'host_alias', + 'host_output', + 'host_long_output', + 'host_perfdata', + )) + ->where('host_name', $host); + return $select->fetchRow(); + $object = \Icinga\Objects\Host::fromBackend( + $this->select() + ->from('status', array( + 'host_name', + 'host_address', + 'host_state', + 'host_handled', + 'host_in_downtime', + 'host_acknowledged', + 'host_check_command', + 'host_last_state_change', + 'host_alias', + 'host_output', + 'host_long_output', + 'host_perfdata', + )) + ->where('host_name', $host) + ->fetchRow() + ); + // $object->customvars = $this->fetchCustomvars($host); + return $object; + } + + // UGLY temporary service fetch + public function fetchService($host, $service) + { +Benchmark::measure('Preparing service select'); + $select = $this->select() + ->from('status', array( + + 'host_name', + 'host_state', + 'host_check_command', + 'host_last_state_change', + + 'service_description', + 'service_state', + 'service_acknowledged', + 'service_handled', + 'service_output', + 'service_long_output', + 'service_perfdata', + // '_host_satellite', + 'service_check_command', + 'service_last_state_change', + 'service_last_check', + 'service_next_check', + 'service_check_execution_time', + 'service_check_latency', + // 'service_ + )) + ->where('host_name', $host) + ->where('service_description', $service); + // Benchmark::measure((string) $select->getQuery()); +Benchmark::measure('Prepared service select'); + + return $select->fetchRow(); + $object = \Icinga\Objects\Service::fromBackend( + $this->select() + ->from('status', array( + + 'host_name', + 'host_state', + 'host_check_command', + 'host_last_state_change', + + 'service_description', + 'service_state', + 'service_acknowledged', + 'service_handled', + 'service_output', + 'service_long_output', + 'service_perfdata', + // '_host_satellite', + 'service_check_command', + 'service_last_state_change', + 'service_last_check', + 'service_next_check', + 'service_check_execution_time', + 'service_check_latency', + // 'service_ + )) + ->where('host_name', $host) + ->where('service_description', $service) + ->fetchRow() + ); + // $object->customvars = $this->fetchCustomvars($host, $service); + return $object; + } + + +} diff --git a/modules/monitoring/library/Monitoring/Backend/Ido.php b/modules/monitoring/library/Monitoring/Backend/Ido.php new file mode 100644 index 000000000..b456d1957 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/Ido.php @@ -0,0 +1,57 @@ + + * CREATE INDEX web2_index ON icinga_scheduleddowntime (object_id, is_in_effect); + * CREATE INDEX web2_index ON icinga_comments (object_id); + * CREATE INDEX web2_index ON icinga_objects (object_id, is_active); -- (not sure yet) + * + * + * Other possible (history-related) indexes, still subject to tests: + * CREATE INDEX web2_index ON icinga_statehistory (object_id, state_time DESC); + * CREATE INDEX web2_index ON icinga_notifications (object_id, instance_id, start_time DESC); + * CREATE INDEX web2_index ON icinga_downtimehistory (object_id, actual_start_time, actual_end_time); + * + * These should be unique: + * CREATE INDEX web2_index ON icinga_host_contacts (host_id, contact_object_id); + * CREATE INDEX web2_index ON icinga_service_contacts (service_id, contact_object_id); + * + * ...and we should drop a lot's of useless and/or redundant index definitions + */ +class Ido extends AbstractBackend +{ + protected $db; + protected $prefix = 'icinga_'; + + protected function init() + { + $this->db = new Connection($this->config); + if ($this->db->getDbType() === 'oracle') { + $this->prefix = ''; + } + } + + public function getConnection() + { + return $this->db; + } + + /** + * Get our IDO table prefix + * + * return string + */ + public function getPrefix() + { + return $this->prefix; + } +} diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/AbstractQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/AbstractQuery.php new file mode 100644 index 000000000..f27fb1afd --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/AbstractQuery.php @@ -0,0 +1,326 @@ +applyDbSpecificWorkarounds + $this->prefix = $this->ds->getPrefix(); + + if ($this->ds->getConnection()->getDbType() === 'oracle') { + $this->object_id = $this->hostgroup_id = $this->servicegroup_id = 'id'; + foreach ($this->columnMap as $table => & $columns) { + foreach ($column as $key => & $value) { + $value = preg_replace('/UNIX_TIMESTAMP/', 'localts2unixts', $value); + } + } + } + + $this->prepareAliasIndexes(); + $this->joinBaseTables(); + } + + protected function isCustomVar($alias) + { + return $this->allowCustomVars && $alias[0] === '_'; + } + + protected function joinCustomvar($customvar) + { + // TODO: This is not generic enough yet + list($type, $name) = $this->customvarNameToTypeName($customvar); + $alias = ($type === 'host' ? 'hcv_' : 'scv_') . strtolower($name); + + $this->customVars[$customvar] = $alias; + + // TODO: extend if we allow queries with only hosts / only services + // ($leftcol s.host_object_id vs h.host_object_id + $leftcol = 's.' . $type . '_object_id'; + $joinOn = $leftcol + . ' = ' + . $alias + . '.object_id' + . ' AND ' + . $alias + . '.varname = ' + . $this->db->quote(strtoupper($name)); + + $this->baseQuery->joinLeft( + array($alias => $this->prefix . 'customvariablestatus'), + $joinOn, + array() + ); + + return $this; + } + + protected function prepareAliasIndexes() + { + foreach ($this->columnMap as $tbl => & $cols) { + foreach ($cols as $alias => $col) { + $this->idxAliasTable[$alias] = $tbl; + $this->idxAliasColumn[$alias] = preg_replace('~\n\s*~', ' ', $col); + } + } + } + + protected function getDefaultColumns() + { + return $this->columnMap['hostgroups']; + } + + protected function beforeCreatingCountQuery() + { + $this->applyAllFilters(); + } + + protected function beforeCreatingSelectQuery() + { + $this->setRealColumns(); + Benchmark::measure(sprintf('%s is going to SELECT', get_class($this))); + } + + protected function applyAllFilters() + { + $filters = array(); + // TODO: Handle $special in a more generic way + $special = array('hostgroups', 'servicegroups'); + foreach ($this->filters as $f) { + $alias = $f[0]; + $value = $f[1]; + $this->requireColumn($alias); + + if ($alias === 'hostgroups') { + $col = 'hg.alias'; + } elseif ($alias === 'servicegroups') { + $col = 'sg.alias'; + } elseif ($this->isCustomvar($alias)) { + $col = $this->getCustomvarColumnName($alias); + } elseif ($this->hasAliasName($alias)) { + $col = $this->aliasToColumnName($alias); + } else { + throw new ProgrammingError( + 'If you finished here, code has been messed up' + ); + } + $this->baseQuery->where($this->prepareFilterStringForColumn($col, $value)); + } + } + + public function order($col, $dir = null) + { + $this->requireColumn($col); + if ($this->isCustomvar($col)) { + // TODO: Doesn't work right now. Does it? + $col = $this->getCustomvarColumnName($col); + } elseif ($this->hasAliasName($col)) { + $col = $this->aliasToColumnName($col); + } else { + die('SHIT'); + } + $this->order_columns[] = array($col, $dir); + return $this; + return parent::order($col, $dir); + } + + public function setRealColumns() + { + $columns = $this->columns; + $this->columns = array(); + if (empty($columns)) { + $colums = $this->getDefaultColumns(); + } + + foreach ($columns as $alias => $col) { + $this->requireColumn($col); + if ($this->isCustomvar($col)) { + $name = $this->getCustomvarColumnName($col); + } else { + $name = $this->aliasToColumnName($col); + } + if (is_int($alias)) { + $alias = $col; + } + + $this->columns[$alias] = preg_replace('|\n|', ' ' , $name); + } + return $this; + } + + protected function requireColumn($alias) + { + if ($this->hasAliasName($alias)) { + $this->requireVirtualTable($this->aliasToTableName($alias)); + } elseif ($this->isCustomVar($alias)) { + $this->requireCustomvar($alias); + } else { + throw new ProgrammingError(sprintf('Got invalid column: %s', $alias)); + } + return $this; + } + + protected function hasAliasName($alias) + { + return array_key_exists($alias, $this->idxAliasColumn); + } + + protected function aliasToColumnName($alias) + { + return $this->idxAliasColumn[$alias]; + } + + protected function aliasToTableName($alias) + { + return $this->idxAliasTable[$alias]; + } + + protected function hasJoinedVirtualTable($name) + { + return array_key_exists($name, $this->joinedVirtualTables); + } + + protected function requireVirtualTable($name) + { + if ($this->hasJoinedVirtualTable($name)) { + return $this; + } + return $this->joinVirtualTable($name); + } + + protected function joinVirtualTable($table) + { + $func = 'join' . ucfirst($table); + if (method_exists($this, $func)) { + $this->$func(); + } else { + throw new ProgrammingError(sprintf( + 'Cannot join "%s", no such table found', + $table + )); + } + $this->joinedVirtualTables[$table] = true; + return $this; + } + + protected function requireCustomvar($customvar) + { + if (! $this->hasCustomvar($customvar)) { + $this->joinCustomvar($customvar); + } + return $this; + } + + protected function hasCustomvar($customvar) + { + return array_key_exists($customvar, $this->customVars); + } + + protected function getCustomvarColumnName($customvar) + { + return $this->customVars[$customvar] . '.varvalue'; + } + + protected function customvarNameToTypeName($customvar) + { + // TODO: Improve this: + if (! preg_match('~^_(host|service)_([a-zA-Z0-9_]+)$~', $customvar, $m)) { + throw new ProgrammingError( + sprintf( + 'Got invalid custom var: "%s"', + $customvar + ) + ); + } + return array($m[1], $m[2]); + } + + protected function prepareFilterStringForColumn($column, $value) + { + $filter = ''; + $filters = array(); + + $or = array(); + $and = array(); + + if (! is_array($value) && strpos($value, ',') !== false) { + $value = preg_split('~,~', $value, -1, PREG_SPLIT_NO_EMPTY); + } + if (! is_array($value)) { + $value = array($value); + } + + // Go through all given values + foreach ($value as $val) { + // Value starting with minus: negation + if ($val[0] === '-') { + $val = substr($val, 1); + if (strpos($val, '*') === false) { + $and[] = $this->db->quoteInto($column . ' != ?', $val); + } else { + $and[] = $this->db->quoteInto( + $column . ' NOT LIKE ?', + str_replace('*', '%', $val) + ); + } + } elseif ($val[0] === '+') { // Value starting with +: enforces AND + // TODO: depends on correct URL handling, not given in all + // ZF versions + $val = substr($val, 1); + if (strpos($val, '*') === false) { + $and[] = $this->db->quoteInto($column . ' = ?', $val); + } else { + $and[] = $this->db->quoteInto( + $column . ' LIKE ?', + str_replace('*', '%', $val) + ); + } + } else { // All others ar ORs: + if (strpos($val, '*') === false) { + $or[] = $this->db->quoteInto($column . ' = ?', $val); + } else { + $or[] = $this->db->quoteInto( + $column . ' LIKE ?', + str_replace('*', '%', $val) + ); + } + } + } + + if (! empty($or)) { + $filters[] = implode(' OR ', $or); + } + + if (! empty($and)) { + $filters[] = implode(' AND ', $and); + } + + if (! empty($filters)) { + $filter = '(' . implode(') AND (', $filters) . ')'; + } + + return $filter; + } +} diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php new file mode 100644 index 000000000..5759a1c58 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/CommentQuery.php @@ -0,0 +1,50 @@ + array( + 'comment_data' => 'cm.comment_data', + 'comment_author' => 'cm.author_name', + 'comment_timestamp' => 'UNIX_TIMESTAMP(cm.entry_time)', + 'comment_type' => "CASE cm.entry_type WHEN 1 THEN 'comment' WHEN 2 THEN 'downtime' WHEN 3 THEN 'flapping' WHEN 4 THEN 'ack' END", + ), + 'hosts' => array( + 'host_name' => 'ho.name1', + ), + 'services' => array( + 'service_host_name' => 'so.name1', + 'service_description' => 'so.name2', + ) + ); + + protected function joinBaseTables() + { + $this->baseQuery = $this->db->select()->from( + array('cm' => $this->prefix . 'comments'), + array() + ); + + $this->joinedVirtualTables = array('comments' => true); + } + + protected function joinHosts() + { + $this->baseQuery->join( + array('ho' => $this->prefix . 'objects'), + 'cm.object_id = ho.' . $this->object_id . ' AND ho.is_active = 1 AND ho.objecttype_id = 1', + array() + ); + } + + protected function joinServices() + { + $this->baseQuery->join( + array('so' => $this->prefix . 'objects'), + 'cm.object_id = so.' . $this->object_id . ' AND so.is_active = 1 AND so.objecttype_id = 2', + array() + ); + } +} diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactQuery.php new file mode 100644 index 000000000..94e17e0f3 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactQuery.php @@ -0,0 +1,72 @@ + array( + 'contact_name' => 'co.name1', + 'contact_alias' => 'c.alias', + 'contact_email' => 'c.email_address', + 'contact_pager' => 'c.pager_address', + ), + 'hosts' => array( + 'host_name' => 'ho.name1', + ), + 'services' => array( + 'service_host_name' => 'so.name1', + 'service_description' => 'so.name2', + ) + ); + + protected function joinBaseTables() + { + $this->baseQuery = $this->db->select()->from( + array('c' => $this->prefix . 'contacts'), + array() + )->join( + array('co' => $this->prefix . 'objects'), + 'c.contact_object_id = co.' . $this->object_id + . ' AND co.is_active = 1', + array() + ); + + $this->joinedVirtualTables = array('contacts' => true); + } + + protected function joinHosts() + { + $this->baseQuery->join( + array('hc' => $this->prefix . 'host_contacts'), + 'hc.contact_object_id = c.contact_object_id', + array() + )->join( + array('h' => $this->prefix . 'hosts'), + 'hc.host_id = h.host_id', + array() + )->join( + array('ho' => $this->prefix . 'objects'), + 'h.host_object_id = ho.' . $this->object_id . ' AND ho.is_active = 1', + array() + ); + } + + protected function joinServices() + { + $this->baseQuery->join( + array('sc' => $this->prefix . 'service_contacts'), + 'sc.contact_object_id = c.contact_object_id', + array() + )->join( + array('s' => $this->prefix . 'services'), + 'sc.service_id = s.service_id', + array() + )->join( + array('so' => $this->prefix . 'objects'), + 's.service_object_id = so.' . $this->object_id . ' AND so.is_active = 1', + array() + ); + } + +} diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php new file mode 100644 index 000000000..b7f1ac0fc --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ContactgroupQuery.php @@ -0,0 +1,106 @@ + array( + 'contactgroup_name' => 'cgo.name1', + 'contactgroup_alias' => 'cg.alias', + ), + 'contacts' => array( + 'contact_name' => 'co.name1', + ), + 'hosts' => array( + 'host_name' => 'ho.name1', + ), + 'services' => array( + 'service_host_name' => 'so.name1', + 'service_description' => 'so.name2', + ) + ); + + protected function joinBaseTables() + { + $this->baseQuery = $this->db->select()->from( + array('cg' => $this->prefix . 'contactgroups'), + array() + )->join( + array('cgo' => $this->prefix . 'objects'), + 'cg.contactgroup_object_id = cgo.' . $this->object_id + . ' AND cgo.is_active = 1', + array() + ); + + $this->joinedVirtualTables = array('contactgroups' => true); + } + + protected function joinContacts() + { + $this->baseQuery->join( + array('cgm' => $this->prefix . 'contactgroup_members'), + 'cgm.contactgroup_id = cg.contactgroup_id', + array() + )->join( + array('co' => $this->prefix . 'objects'), + 'cgm.contact_object_id = co.object_id AND co.is_active = 1', + array() + ); + } + + protected function joinHosts() + { + $this->baseQuery->join( + array('hcg' => $this->prefix . 'host_contactgroups'), + 'hcg.contactgroup_object_id = cg.contactgroup_object_id', + array() + )->join( + array('h' => $this->prefix . 'hosts'), + 'hcg.host_id = h.host_id', + array() + )->join( + array('ho' => $this->prefix . 'objects'), + 'h.host_object_id = ho.' . $this->object_id . ' AND ho.is_active = 1', + array() + ); + } + + protected function joinServices() + { + $scgSub = $this->db->select()->distinct() + ->from($this->prefix . 'service_contactgroups', array( + 'contactgroup_object_id', 'service_id' + )); + + /* + This subselect is a workaround for a fucking stupid bug. Other tables + may be affected too. We absolutely need uniqueness here. + + mysql> SELECT * FROM icinga_service_contactgroups WHERE + contactgroup_object_id = 143 AND service_id = 2079564; + +-------------------------+-------------+------------+------------------------+ + | service_contactgroup_id | instance_id | service_id | contactgroup_object_id | + +-------------------------+-------------+------------+------------------------+ + | 4904240 | 1 | 2079564 | 143 | + | 4904244 | 1 | 2079564 | 143 | + +-------------------------+-------------+------------+------------------------+ + */ + + $this->baseQuery->join( + // array('scg' => $this->prefix . 'service_contactgroups'), + array('scg' => $scgSub), + 'scg.contactgroup_object_id = cg.contactgroup_object_id', + array() + )->join( + array('s' => $this->prefix . 'services'), + 'scg.service_id = s.service_id', + array() + )->join( + array('so' => $this->prefix . 'objects'), + 's.service_object_id = so.' . $this->object_id . ' AND so.is_active = 1', + array() + ); + } + +} diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/CustomvarQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/CustomvarQuery.php new file mode 100644 index 000000000..a6175373f --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/CustomvarQuery.php @@ -0,0 +1,40 @@ + array( + 'varname' => 'cvs.varname', + 'varvalue' => 'cvs.varvalue', + ), + 'objects' => array( + 'host_name' => 'cvo.name1', + 'service_description' => 'cvo.name2', + 'contact_name' => 'cvo.name1', + 'object_type' => "CASE cvo.objecttype_id WHEN 1 THEN 'host' WHEN 2 THEN 'service' WHEN 10 THEN 'contact' ELSE 'invalid' END" +// 'object_type' => "CASE cvo.objecttype_id WHEN 1 THEN 'host' WHEN 2 THEN 'service' WHEN 3 THEN 'hostgroup' WHEN 4 THEN 'servicegroup' WHEN 5 THEN 'hostescalation' WHEN 6 THEN 'serviceescalation' WHEN 7 THEN 'hostdependency' WHEN 8 THEN 'servicedependency' WHEN 9 THEN 'timeperiod' WHEN 10 THEN 'contact' WHEN 11 THEN 'contactgroup' WHEN 12 THEN 'command' ELSE 'other' END" + ), + ); + + protected function joinBaseTables() + { + $this->baseQuery = $this->db->select()->from( + array('cvs' => $this->prefix . 'customvariablestatus'), + array() + )->join( + array('cvo' => $this->prefix . 'objects'), + 'cvs.object_id = cvo.' . $this->object_id + . ' AND cvo.is_active = 1', + array() + ); + + $this->joinedVirtualTables = array( + 'customvars' => true, + 'objects' => true + ); + } +} diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/EventHistoryQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/EventHistoryQuery.php new file mode 100644 index 000000000..9c4554e99 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/EventHistoryQuery.php @@ -0,0 +1,241 @@ + array( + 'host' => 'eho.name1', + 'service' => 'eho.name2', + 'host_name' => 'eho.name1', + 'service_description' => 'eho.name2', + 'object_type' => "CASE WHEN eho.objecttype_id = 1 THEN 'host' ELSE 'service' END", + 'timestamp' => 'UNIX_TIMESTAMP(eh.state_time)', + 'raw_timestamp' => 'eh.state_time', + 'state' => 'eh.state', +// 'last_state' => 'eh.last_state', +// 'last_hard_state' => 'eh.last_hard_state', + 'attempt' => 'eh.attempt', + 'max_attempts' => 'eh.max_attempts', + 'output' => 'eh.output', // we do not want long_output + 'problems' => 'CASE WHEN eh.state = 0 OR eh.state IS NULL THEN 0 ELSE 1 END', + 'type' => 'eh.type', + ) + ); + + protected function getDefaultColumns() + { + return $this->columnMap['eventhistory']; + } + + protected function joinBaseTables() + { + $start = date('Y-m-d H:i:s', time() - 3600 * 24 * 30); + $end = date('Y-m-d H:i:s'); + $start = null; + $end = null; + + $history = $this->db->select()->from( + $this->prefix . 'statehistory', + array( + 'state_time' => 'state_time', + 'object_id' => 'object_id', + 'type' => "(CASE WHEN state_type = 1 THEN 'hard_state' ELSE 'soft_state' END)", + 'state' => 'state', + 'state_type' => 'state_type', + 'output' => 'output', + 'attempt' => 'current_check_attempt', + 'max_attempts' => 'max_check_attempts', + ) + ); + if ($start !== null) { + $history->where('state_time >= ?', $start); + } + // ->where('state_type = 1') ?? + if ($end !== null) { + $history->where('state_time <= ?', $end); + } + + $dt_start = $this->db->select()->from( + $this->prefix . 'downtimehistory', + array( + 'state_time' => 'actual_start_time', + 'object_id' => 'object_id', + 'type' => "('dt_start')", + 'state' => '(NULL)', + 'state_type' => '(NULL)', + 'output' => "CONCAT('[', author_name, '] ', comment_data)", + 'attempt' => '(NULL)', + 'max_attempts' => '(NULL)', + ) + ); + if ($start !== null) { + $dt_start->where('actual_start_time >= ?', $start); + } + if ($end !== null) { + $dt_start->where('actual_start_time <= ?', $end); + } + + // TODO: check was_cancelled + $dt_end = $this->db->select()->from( + $this->prefix . 'downtimehistory', + array( + 'state_time' => 'actual_end_time', + 'object_id' => 'object_id', + 'type' => "('dt_end')", + 'state' => '(NULL)', + 'state_type' => '(NULL)', + 'output' => "CONCAT('[', author_name, '] ', comment_data)", + 'attempt' => '(NULL)', + 'max_attempts' => '(NULL)', + ) + ); + if ($start !== null) { + $dt_end->where('actual_end_time >= ?', $start); + } + if ($end !== null) { + $dt_end->where('actual_end_time <= ?', $end); + } + + $comments = $this->db->select()->from( + $this->prefix . 'commenthistory', + array( + 'state_time' => 'comment_time', + 'object_id' => 'object_id', + 'type' => "(CASE entry_type WHEN 1 THEN 'comment' WHEN 2 THEN 'dt_comment' WHEN 3 THEN 'flapping' WHEN 4 THEN 'ack' END)", + 'state' => '(NULL)', + 'state_type' => '(NULL)', + 'output' => "CONCAT('[', author_name, '] ', comment_data)", + 'attempt' => '(NULL)', + 'max_attempts' => '(NULL)', + ) + ); + + if ($start !== null) { + $comments->where('comment_time >= ?', $start); + } + if ($end !== null) { + $comments->where('comment_time <= ?', $end); + } + + $cndetails = $this->db->select()->from( + array('cn' => $this->prefix . 'contactnotifications'), + array( + 'cnt' => 'COUNT(*)', + 'contacts' => 'GROUP_CONCAT(c.alias)', + 'notification_id' => 'notification_id' + ) + )->join( + array('c' => $this->prefix . 'contacts'), + 'cn.contact_object_id = c.contact_object_id', + array() + )->group('notification_id'); + + $notifications = $this->db->select()->from( + array('n' => $this->prefix . 'notifications'), + array( + 'state_time' => 'start_time', + 'object_id' => 'object_id', + 'type' => "('notify')", + 'state' => 'state', + 'state_type' => '(NULL)', + 'output' => "CONCAT('[', cndetails.contacts, '] ', n.output)", + 'attempt' => '(NULL)', + 'max_attempts' => '(NULL)', + ) + )->join( + array('cndetails' => $cndetails), + 'cndetails.notification_id = n.notification_id', + array() + ); + + if ($start !== null) { + $notifications->where('start_time >= ?', $start); + } + if ($end !== null) { + $notifications->where('start_time <= ?', $end); + } + + $this->subQueries = array( + $history, + $dt_start, + $dt_end, + $comments, + $notifications + ); + $sub = $this->db->select()->union($this->subQueries, \Zend_Db_Select::SQL_UNION_ALL); + + $this->baseQuery = $this->db->select()->from( + array('eho' => $this->prefix . 'objects'), + array() + )->join( + array('eh' => $sub), + 'eho.' . $this->object_id + . ' = eh.' . $this->object_id + . ' AND eho.is_active = 1', + array() + ); + + $this->joinedVirtualTables = array('eventhistory' => true); + } + + // TODO: This duplicates code from AbstractQuery + protected function applyAllFilters() + { + $filters = array(); + + $host = null; + $service = null; + + foreach ($this->filters as $f) { + $alias = $f[0]; + $value = $f[1]; + $this->requireColumn($alias); + + if ($this->hasAliasName($alias)) { + $col = $this->aliasToColumnName($alias); + } else { + throw new ProgrammingError( + 'If you finished here, code has been messed up' + ); + } + + if (in_array($alias, array('host', 'host_name'))) { + $host = $value; + continue; + } + if (in_array($alias, array('service', 'service_description'))) { + $service = $value; + continue; + } + + $this->baseQuery->where($this->prepareFilterStringForColumn($col, $value)); + } + + $objectQuery = $this->db->select()->from( + $this->prefix . 'objects', + $this->object_id + )->where('is_active = 1'); + + if ($service === '*') { + $objectQuery->where('name1 = ?', $host) + ->where('objecttype_id IN (1, 2)'); + } elseif ($service) { + $objectQuery->where('name1 = ?', $host) + ->where('name2 = ?', $service) + ->where('objecttype_id = 2'); + } else { + $objectQuery->where('name1 = ?', $host) + ->where('objecttype_id = 1'); + } + $objectId = $this->db->fetchCol($objectQuery); + foreach ($this->subQueries as $query) { + $query->where('object_id IN (?)', $objectId); + } + } +} \ No newline at end of file diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostgroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostgroupQuery.php new file mode 100644 index 000000000..e1a08f043 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HostgroupQuery.php @@ -0,0 +1,44 @@ + array( + 'hostgroup_name' => 'hgo.name1', + 'hostgroup_alias' => 'hg.alias', + ), + 'hosts' => array( + 'host_name' => 'ho.name1' + ) + ); + + protected function joinBaseTables() + { + $this->baseQuery = $this->db->select()->from( + array('hg' => $this->prefix . 'hostgroups'), + array() + )->join( + array('hgo' => $this->prefix . 'objects'), + 'hg.hostgroup_object_id = hgo.' . $this->object_id + . ' AND hgo.is_active = 1', + array() + ); + + $this->joinedVirtualTables = array('hostgroups' => true); + } + + protected function joinHosts() + { + $this->baseQuery->join( + array('hgm' => $this->prefix . 'hostgroup_members'), + 'hgm.hostgroup_id = hg.hostgroup_id', + array() + )->join( + array('ho' => $this->prefix . 'objects'), + 'hgm.host_object_id = ho.object_id AND ho.is_active = 1', + array() + ); + } +} diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicegroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicegroupQuery.php new file mode 100644 index 000000000..d54fa8753 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicegroupQuery.php @@ -0,0 +1,45 @@ + array( + 'servicegroup_name' => 'sgo.name1', + 'servicegroup_alias' => 'sg.alias', + ), + 'services' => array( + 'host_name' => 'so.name1', + 'service_description' => 'so.name2' + ) + ); + + protected function joinBaseTables() + { + $this->baseQuery = $this->db->select()->from( + array('sg' => $this->prefix . 'servicegroups'), + array() + )->join( + array('sgo' => $this->prefix . 'objects'), + 'sg.servicegroup_object_id = sgo.' . $this->object_id + . ' AND sgo.is_active = 1', + array() + ); + + $this->joinedVirtualTables = array('servicegroups' => true); + } + + protected function joinServices() + { + $this->baseQuery->join( + array('sgm' => $this->prefix . 'servicegroup_members'), + 'sgm.servicegroup_id = sg.servicegroup_id', + array() + )->join( + array('so' => $this->prefix . 'objects'), + 'sgm.service_object_id = so.object_id AND so.is_active = 1', + array() + ); + } +} diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php new file mode 100644 index 000000000..4d2ee946e --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php @@ -0,0 +1,258 @@ + array( + 'host' => 'ho.name1', + 'host_name' => 'ho.name1', + 'host_display_name' => 'h.display_name', + 'host_alias' => 'h.alias', + 'host_address' => 'h.address', + 'host_ipv4' => 'INET_ATON(h.address)', + 'host_icon_image' => 'h.icon_image', + ), + 'hoststatus' => array( + 'host_state' => 'CASE WHEN hs.has_been_checked = 0 OR hs.has_been_checked IS NULL THEN 99 ELSE hs.current_state END', + 'host_output' => 'hs.output', + 'host_long_output' => 'hs.long_output', + 'host_perfdata' => 'hs.perfdata', + 'host_acknowledged' => 'hs.problem_has_been_acknowledged', + 'host_in_downtime' => 'CASE WHEN (hs.scheduled_downtime_depth = 0) THEN 0 ELSE 1 END', + 'host_handled' => 'CASE WHEN (hs.problem_has_been_acknowledged + hs.scheduled_downtime_depth) > 0 THEN 1 ELSE 0 END', + 'host_does_active_checks' => 'hs.active_checks_enabled', + 'host_accepts_passive_checks' => 'hs.passive_checks_enabled', + 'host_last_state_change' => 'UNIX_TIMESTAMP(hs.last_state_change)', + 'host_check_command' => 'hs.check_command', + 'host_problems' => 'CASE WHEN hs.current_state = 0 THEN 0 ELSE 1 END', + 'host_severity' => 'CASE WHEN hs.current_state = 0 + THEN + CASE WHEN hs.has_been_checked = 0 OR hs.has_been_checked IS NULL + THEN 16 + ELSE 0 + END + + + CASE WHEN hs.problem_has_been_acknowledged = 1 + THEN 2 + ELSE + CASE WHEN hs.scheduled_downtime_depth > 0 + THEN 1 + ELSE 4 + END + END + ELSE + CASE WHEN hs.has_been_checked = 0 OR hs.has_been_checked IS NULL THEN 16 + WHEN hs.current_state = 1 THEN 32 + WHEN hs.current_state = 2 THEN 64 + ELSE 256 + END + + + CASE WHEN hs.problem_has_been_acknowledged = 1 + THEN 2 + ELSE + CASE WHEN hs.scheduled_downtime_depth > 0 + THEN 1 + ELSE 4 + END + END + END', + ), + 'hostgroups' => array( + 'hostgroups' => 'hgo.name1', + ), + 'services' => array( + 'service_host_name' => 'so.name1', + 'service' => 'so.name2', + 'service_description' => 'so.name2', + 'service_display_name' => 's.display_name', + 'service_icon_image' => 's.icon_image', + ), + 'servicestatus' => array( + 'current_state' => 'CASE WHEN ss.has_been_checked = 0 OR ss.has_been_checked IS NULL THEN 99 ELSE ss.current_state END', + 'service_state' => 'CASE WHEN ss.has_been_checked = 0 OR ss.has_been_checked IS NULL THEN 99 ELSE ss.current_state END', + 'service_hard_state' => 'CASE WHEN ss.has_been_checked = 0 OR ss.has_been_checked IS NULL THEN 99 ELSE CASE WHEN ss.state_type = 1 THEN ss.current_state ELSE ss.last_hard_state END END', + 'service_state_type' => 'ss.state_type', + 'service_output' => 'ss.output', + 'service_long_output' => 'ss.long_output', + 'service_perfdata' => 'ss.perfdata', + 'service_acknowledged' => 'ss.problem_has_been_acknowledged', + 'service_in_downtime' => 'CASE WHEN (ss.scheduled_downtime_depth = 0) THEN 0 ELSE 1 END', + 'service_handled' => 'CASE WHEN (ss.problem_has_been_acknowledged + ss.scheduled_downtime_depth + COALESCE(hs.current_state, 0)) > 0 THEN 1 ELSE 0 END', + 'service_does_active_checks' => 'ss.active_checks_enabled', + 'service_accepts_passive_checks' => 'ss.passive_checks_enabled', + 'service_last_state_change' => 'UNIX_TIMESTAMP(ss.last_state_change)', + 'service_last_hard_state_change' => 'UNIX_TIMESTAMP(ss.last_hard_state_change)', + 'service_check_command' => 'ss.check_command', + 'service_last_check' => 'UNIX_TIMESTAMP(ss.last_check)', + 'service_next_check' => 'CASE WHEN ss.should_be_scheduled THEN UNIX_TIMESTAMP(ss.next_check) ELSE NULL END', + 'service_check_execution_time' => 'ss.execution_time', + 'service_check_latency' => 'ss.latency', + ), + 'status' => array( + 'problems' => 'CASE WHEN ss.current_state = 0 THEN 0 ELSE 1 END', + 'handled' => 'CASE WHEN ss.problem_has_been_acknowledged = 1 OR ss.scheduled_downtime_depth > 0 THEN 1 ELSE 0 END', + 'severity' => 'CASE WHEN ss.current_state = 0 + THEN + CASE WHEN ss.has_been_checked = 0 OR ss.has_been_checked IS NULL + THEN 16 + ELSE 0 + END + + + CASE WHEN ss.problem_has_been_acknowledged = 1 + THEN 2 + ELSE + CASE WHEN ss.scheduled_downtime_depth > 0 + THEN 1 + ELSE 4 + END + END + ELSE + CASE WHEN ss.has_been_checked = 0 OR ss.has_been_checked IS NULL THEN 16 + WHEN ss.current_state = 1 THEN 32 + WHEN ss.current_state = 2 THEN 128 + WHEN ss.current_state = 3 THEN 64 + ELSE 256 + END + + + CASE WHEN ss.problem_has_been_acknowledged = 1 + THEN 2 + ELSE + CASE WHEN ss.scheduled_downtime_depth > 0 + THEN 1 + ELSE 4 + END + END + END', + ) + ); +public function group($col) +{ + $this->baseQuery->group($col); +} + protected function getDefaultColumns() + { + return $this->columnMap['hosts']; + /* + + $this->columnMap['services'] + + $this->columnMap['hoststatus'] + + $this->columnMap['servicestatus'] + ;*/ + } + + protected function joinBaseTables() + { + // TODO: Shall we always add hostobject? + $this->baseQuery = $this->db->select()->from( + array('ho' => $this->prefix . 'objects'), + array() + )->join( + array('hs' => $this->prefix . 'hoststatus'), + 'ho.object_id = hs.host_object_id AND ho.is_active = 1', + array() + )->join( + array('h' => $this->prefix . 'hosts'), + 'hs.host_object_id = h.host_object_id', + array() + ); + $this->joinedVirtualTables = array( + 'hosts' => true, + 'hoststatus' => true, + ); + } + + protected function joinStatus() + { + $this->requireVirtualTable('services'); + } + + protected function joinServiceStatus() + { + $this->requireVirtualTable('services'); + } + + protected function joinServices() + { + $this->baseQuery->join( + array('s' => $this->prefix . 'services'), + 's.host_object_id = h.host_object_id', + array() + )->join( + array('so' => $this->prefix . 'objects'), + "so.$this->object_id = s.service_object_id AND so.is_active = 1", + array() + )->joinLeft( + array('ss' => $this->prefix . 'servicestatus'), + "so.$this->object_id = ss.service_object_id", + array() + ); + } + + // TODO: Test this one, doesn't seem to work right now + protected function joinHostgroups() + { + if ($this->hasJoinedVirtualTable('services')) { + return $this->joinServiceHostgroups(); + } else { + return $this->joinHostHostgroups(); + } + } + + protected function joinHostHostgroups() + { + $this->baseQuery->join( + array('hgm' => $this->prefix . 'hostgroup_members'), + 'hgm.host_object_id = h.host_object_id', + array() + )->join( + array('hg' => $this->prefix . 'hostgroups'), + "hgm.hostgroup_id = hg.$this->hostgroup_id", + array() + ); + + return $this; + } + + protected function joinServiceHostgroups() + { + $this->baseQuery->join( + array('hgm' => $this->prefix . 'hostgroup_members'), + 'hgm.host_object_id = s.host_object_id', + array() + )->join( + array('hg' => $this->prefix . 'hostgroups'), + 'hgm.hostgroup_id = hg.' . $this->hostgroup_id, + array() + )->join( + array('hgo' => $this->prefix . 'objects'), + 'hgo.' . $this->object_id. ' = hg.hostgroup_object_id' + . ' AND hgo.is_active = 1', + array() + ); + + return $this; + } + + protected function joinServicegroups() + { + $this->baseQuery->join( + array('sgm' => $this->prefix . 'servicegroup_members'), + 'sgm.service_object_id = s.service_object_id', + array() + )->join( + array('sg' => $this->prefix . 'servicegroups'), + 'sgm.servicegroup_id = sg.' . $this->servicegroup_id, + array() + )->join( + array('hgo' => $this->prefix . 'objects'), + 'hgo.' . $this->object_id. ' = hg.' . $this->hostgroup_id + . ' AND hgo.is_active = 1', + array() + ); + + return $this; + } +} diff --git a/modules/monitoring/library/Monitoring/Backend/Livestatus.php b/modules/monitoring/library/Monitoring/Backend/Livestatus.php new file mode 100644 index 000000000..0cc038291 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/Livestatus.php @@ -0,0 +1,45 @@ + + * @author Icinga-Web Team + * @package Icinga\Application + * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License + */ +class Livestatus extends AbstractBackend +{ + protected $connection; + + /** + * Backend initialization starts here + * + * return void + */ + protected function init() + { + $this->connection = new Connection($this->config->socket); + } + + /** + * Get our Livestatus connection + * + * return \Icinga\Protocol\Livestatus\Connection + */ + public function getConnection() + { + return $this->connection; + } +} diff --git a/modules/monitoring/library/Monitoring/Backend/Livestatus/Query/StatusQuery.php b/modules/monitoring/library/Monitoring/Backend/Livestatus/Query/StatusQuery.php new file mode 100644 index 000000000..cc1be7363 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/Livestatus/Query/StatusQuery.php @@ -0,0 +1,48 @@ + 'host_address', // TODO + 'host_icon_image', + + // Host state + 'host_state', + 'host_output' => 'host_plugin_output', + 'host_perfdata' => 'host_perf_data', + 'host_acknowledged', + 'host_does_active_checks' => 'host_active_checks_enabled', + 'host_accepts_passive_checks' => 'host_accept_passive_checks', + 'host_last_state_change', + + // Service config + 'service_description' => 'description', + 'service_display_name' => 'display_name', + + // Service state + 'service_state' => 'state', + 'service_output' => 'plugin_output', + 'service_perfdata' => 'perf_data', + 'service_acknowledged' => 'acknowledged', + 'service_does_active_checks' => 'active_checks_enabled', + 'service_accepts_passive_checks' => 'accept_passive_checks', + 'service_last_state_change' => 'last_state_change', + + // Service comments + 'comments_with_info', + 'downtimes_with_info', + ); + + protected function createQuery() + { + return $this->connection->getConnection()->select()->from('services', $this->available_columns); + } +} diff --git a/modules/monitoring/library/Monitoring/Environment.php b/modules/monitoring/library/Monitoring/Environment.php new file mode 100644 index 000000000..bf198db4f --- /dev/null +++ b/modules/monitoring/library/Monitoring/Environment.php @@ -0,0 +1,61 @@ + array( + 'backend' => null, + 'grapher' => null, + 'configBackend' => null, + 'commandPipe' => null, + ) + ); + + public static function defaultName() + { + // TODO: Check session + reset(self::$envs); + return key(self::$envs); + } + + protected static function config($env, $what) + { + return self::$config[self::getName($env)][$what]; + } + + protected static function getName($env) + { + return $env === null ? self::defaultName() : $env; + } + + public static function backend($env = null) + { + return Backend::getInstance(self::config($env, 'backend')); + } + + public static function grapher($env = null) + { + return Hook::get('grapher', null, self::config($env, 'grapher')); + } + + public static function configBackend($env = null) + { + return Hook::get( + 'configBackend', + null, + self::config($env, 'configBackend') + ); + } + + public static function commandPipe($env = null) + { + return CommandPipe::getInstance(self::config($env, 'commandPipe')); + } +} + diff --git a/modules/monitoring/library/Monitoring/Plugin.php b/modules/monitoring/library/Monitoring/Plugin.php new file mode 100644 index 000000000..80475f5d9 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Plugin.php @@ -0,0 +1,9 @@ +unit) { + case self::BYTES: + return $this->formatBytes() . ' von ' . $this->formatBytes($this->max); + break; + case self::SECONDS: + return $this->formatSeconds(); + break; + case self::PERCENT: + return number_format($this->val, 2, ',', '.') . '%'; + break; + default: + return $this->val; + } + } + + public function getValue() + { + return $this->val; + } + + protected function formatBytes($val = null) + { + $steps = array( + 1 => 'Byte', + 1024 => 'KByte', + 1024 * 1024 => 'MByte', + 1024 * 1024 * 1024 => 'GByte', + 1024 * 1024 * 1024 * 1024 => 'TByte' + ); + return $this->formatSpecial($steps, 1, $val); + } + + protected function formatSeconds() + { + $steps = array( + 1 => 'us', + 1000 => 'ms', + 10000000 => 's', + ); + return $this->formatSpecial($steps, 1000000, $this->val); + } + + protected function formatSpecial($steps, $multi = 1, $val = null) + { + if ($val === null) { + $val = abs($this->val); + } else { + $val = abs($val); + } + // TODO: Check this, prefix fails if $val is given + if ($this->val < 0) { + $prefix = '-'; + } else { + $prefix = ''; + } + $val *= $multi; + $step = 1; + foreach (array_keys($steps) as $key) { + if ($key > $val * 1) { + break; + } + $step = $key; + } + return $prefix + . number_format($val / $step, 1, ',', '.') + . ' ' + . $steps[$step]; + } + + protected function __construct(& $perfdata) + { + $this->byte_map = array( + 'b' => 1, + 'kb' => 1024, + 'mb' => 1024 * 1024, + 'gb' => 1024 * 1024 * 1024, + 'tb' => 1024 * 1024 * 1024 * 1024 + ); + + // UGLY, fixes floats using comma: + $perfdata = preg_replace('~\,~', '.', $perfdata); + + $parts = preg_split('~;~', $perfdata, 5); + while (count($parts) < 5) { + $parts[] = null; + } + list( + $this->val, + $this->warn, + $this->crit, + $this->min, + $this->max + ) = $parts; + // TODO: check numbers! + + $unit = null; + if (! preg_match('~^(\-?[\d+\.]+(?:E\-?\d+)?)([^\d]+)?$~', $this->val, $m)) { + throw new \Exception('Got invalid perfdata: ' . $perfdata); + } + $this->val = $m[1]; + if (isset($m[2])) { + $unit = strtolower($m[2]); + } + if ($unit === 'c') { + $this->unit = self::COUNTER; + } + + if ($unit === '%') { + if (! is_numeric($this->min)) { + $this->min = 0; + } + if (! is_numeric($this->max)) { + $this->max = 100; + } + $this->unit = self::PERCENT; + } else { + if (! is_numeric($this->max) && $this->crit > 0) { + $this->max = $this->crit; + } + } + + + if (array_key_exists($unit, $this->byte_map)) { + $this->unit = self::BYTES; + $this->val = $this->val * $this->byte_map[$unit]; + $this->min = $this->min * $this->byte_map[$unit]; + $this->max = $this->max * $this->byte_map[$unit]; + } + if ($unit === 's') { + $this->unit = self::SECONDS; + } + if ($unit === 'ms') { + $this->unit = self::SECONDS; + $this->val = $this->val / 1000; + } + if ($unit === '%') { + if (! is_numeric($this->min)) { + $this->min = 0; + } + if (! is_numeric($this->max)) { + $this->max = 100; + } + } else { + if (! is_numeric($this->max) && $this->crit > 0) { + $this->max = $this->crit; + } + } + } + + public function isCounter() + { + return $this->unit === self::COUNTER; + } + + public static function fromString(& $perfdata) + { + $pdat = new Perfdata($perfdata); + return $pdat; + } + + protected function normalizeNumber($num) + { + return $num; + // Bullshit, still TODO + /* + $dot = strpos($num, '.'); + $comma = strpos($num, ','); + + if ($dot === false) { + // No dot... + if ($comma === false) { + // ...and no comma, it's an integer: + return (int) $num; + } else { + // just a comma + } + } else { + if ($comma === false) { + } + */ + } +} diff --git a/modules/monitoring/library/Monitoring/Plugin/PerfdataSet.php b/modules/monitoring/library/Monitoring/Plugin/PerfdataSet.php new file mode 100644 index 000000000..e43a7c56c --- /dev/null +++ b/modules/monitoring/library/Monitoring/Plugin/PerfdataSet.php @@ -0,0 +1,69 @@ +ptr = & $perfdata; + $this->len = strlen($this->ptr); + while ($this->pos < $this->len) { + $label = $this->readLabel(); + $perf = $this->readUntil(' '); + if (empty($perf)) continue; + $this->perfdata[$label] = Perfdata::fromString($perf); + } + } + + public static function fromString(& $perfdata) + { + $pset = new PerfdataSet($perfdata); + return $pset; + } + + public function getAll() + { + return $this->perfdata; + } + + protected function readLabel() + { + $this->skipSpaces(); + if (in_array($this->ptr[$this->pos], array('"', "'"))) { + $this->pos++; + $label = $this->readUntil($this->ptr[$this->pos - 1]); + $this->pos++; // Skip ' or " + $skip = $this->readUntil('='); + $this->pos++; + } else { + $label = $this->readUntil('='); + $this->pos++; + } + $this->skipSpaces(); + return trim($label); + } + + protected function readUntil($stop_char) + { + $start = $this->pos; + while ($this->pos < $this->len && $this->ptr[$this->pos] !== $stop_char) { + $this->pos++; + } + return substr($this->ptr, $start, $this->pos - $start); + } + + protected function skipSpaces() + { + while ($this->pos < $this->len && $this->ptr[$this->pos] === ' ') { + $this->pos++; + } + } +} + diff --git a/modules/monitoring/library/Monitoring/View/CommentView.php b/modules/monitoring/library/Monitoring/View/CommentView.php new file mode 100644 index 000000000..166c73950 --- /dev/null +++ b/modules/monitoring/library/Monitoring/View/CommentView.php @@ -0,0 +1,23 @@ + array( + 'default_dir' => self::SORT_DESC + ) + ); +} diff --git a/modules/monitoring/library/Monitoring/View/ContactView.php b/modules/monitoring/library/Monitoring/View/ContactView.php new file mode 100644 index 000000000..11b015b63 --- /dev/null +++ b/modules/monitoring/library/Monitoring/View/ContactView.php @@ -0,0 +1,24 @@ + array( + 'default_dir' => self::SORT_ASC + ) + ); +} diff --git a/modules/monitoring/library/Monitoring/View/ContactgroupView.php b/modules/monitoring/library/Monitoring/View/ContactgroupView.php new file mode 100644 index 000000000..0f4b0c36c --- /dev/null +++ b/modules/monitoring/library/Monitoring/View/ContactgroupView.php @@ -0,0 +1,23 @@ + array( + 'default_dir' => self::SORT_ASC + ) + ); +} diff --git a/modules/monitoring/library/Monitoring/View/CustomvarView.php b/modules/monitoring/library/Monitoring/View/CustomvarView.php new file mode 100644 index 000000000..37e45ebf0 --- /dev/null +++ b/modules/monitoring/library/Monitoring/View/CustomvarView.php @@ -0,0 +1,23 @@ + array( + 'varname' => self::SORT_ASC, + 'varvalue' => self::SORT_ASC, + ) + ); +} diff --git a/modules/monitoring/library/Monitoring/View/EventHistoryView.php b/modules/monitoring/library/Monitoring/View/EventHistoryView.php new file mode 100644 index 000000000..68c38e87d --- /dev/null +++ b/modules/monitoring/library/Monitoring/View/EventHistoryView.php @@ -0,0 +1,34 @@ + array( + 'default_dir' => self::SORT_DESC + ), + 'timestamp' => array( + 'default_dir' => self::SORT_DESC + ), + ); +} diff --git a/modules/monitoring/library/Monitoring/View/HostgroupView.php b/modules/monitoring/library/Monitoring/View/HostgroupView.php new file mode 100644 index 000000000..61e204b49 --- /dev/null +++ b/modules/monitoring/library/Monitoring/View/HostgroupView.php @@ -0,0 +1,21 @@ + array( + 'default_dir' => self::SORT_ASC + ) + ); +} diff --git a/modules/monitoring/library/Monitoring/View/MonitoringView.php b/modules/monitoring/library/Monitoring/View/MonitoringView.php new file mode 100644 index 000000000..6987c0e82 --- /dev/null +++ b/modules/monitoring/library/Monitoring/View/MonitoringView.php @@ -0,0 +1,135 @@ +availableColumns); + } + + public function getAvailableColumns() + { + return $this->availableColumns; + } + + public function applyRequest($request) + { + return $this->applyRequestFilters($request) + ->applyRequestSorting($request); + } + + protected function applyRequestSorting($request) + { + return $this->order( + $request->getParam('sort', $this->availableColumns[0]), + $request->getParam('dir') + ); + } + + protected function applyRequestFilters($request) + { + foreach ($request->getParams() as $key => $value) { + if ($this->isValidFilterColumn($key)) { + $this->where($key, $value); + } + } + return $this; + } + + // TODO: applyAuthFilters(Auth $auth = null) + + public function applyFilters($filters) + { + foreach ($filters as $col => $filter) { + $this->where($col, $filter); + } + return $this; + } + + public function getAppliedFilter() + { + return new Filter($this->filters); + } + + protected function getDefaultSortDir($col) + { + if (isset($this->sortDefaults[$col]['default_dir'])) { + return $this->sortDefaults[$col]['default_dir']; + } + return self::SORT_ASC; + } + + public function getQuery() + { + + if ($this->query === null) { + $class = substr(array_pop(preg_split('|\\\|', get_class($this))), 0, -4) . 'Query'; + $class = '\\' . get_class($this->ds) . '\\Query\\' . $class; + + $query = new $class($this->ds, $this->columns); + foreach ($this->filters as $f) { + $query->where($f[0], $f[1]); + } + foreach ($this->order_columns as $col) { + if (isset($this->sortDefaults[$col[0]]['columns'])) { + foreach ($this->sortDefaults[$col[0]]['columns'] as $c) { + $query->order($c, $col[1]); + } + } else { + $query->order($col[0], $col[1]); + } + } + $this->query = $query; + } + if ($this->hasLimit()) { + $this->query->limit($this->getLimit(), $this->getOffset()); + } + return $this->query; + } + + public function count() + { + return $this->getQuery()->count(); + } + + public function fetchAll() + { + return $this->getQuery()->fetchAll(); + } + + public function fetchRow() + { + return $this->getQuery()->fetchRow(); + } + + public function fetchColumn() + { + return $this->getQuery()->fetchColumn(); + } + + public function fetchPairs() + { + return $this->getQuery()->fetchPairs(); + } + + public function isValidFilterColumn($column) + { + if (in_array($column, $this->specialFilters)) { + return true; + } + return in_array($column, $this->availableColumns); + } +} diff --git a/modules/monitoring/library/Monitoring/View/ServicegroupView.php b/modules/monitoring/library/Monitoring/View/ServicegroupView.php new file mode 100644 index 000000000..3401af68c --- /dev/null +++ b/modules/monitoring/library/Monitoring/View/ServicegroupView.php @@ -0,0 +1,21 @@ + array( + 'default_dir' => self::SORT_ASC + ) + ); +} diff --git a/modules/monitoring/library/Monitoring/View/StatusView.php b/modules/monitoring/library/Monitoring/View/StatusView.php new file mode 100644 index 000000000..35b00d315 --- /dev/null +++ b/modules/monitoring/library/Monitoring/View/StatusView.php @@ -0,0 +1,111 @@ + array( + 'columns' => array( + 'host_name', + 'service_description' + ), + 'default_dir' => self::SORT_ASC + ), + 'host_address' => array( + 'columns' => array( + 'host_ipv4', + 'service_description' + ), + 'default_dir' => self::SORT_ASC + ), + 'host_last_state_change' => array( + 'default_dir' => self::SORT_DESC + ), + 'service_last_state_change' => array( + 'default_dir' => self::SORT_DESC + ), + 'severity' => array( + 'columns' => array( + 'severity', + 'service_last_state_change', + ), + 'default_dir' => self::SORT_DESC + ), + 'host_severity' => array( + 'columns' => array( + 'host_severity', + 'host_last_state_change', + ), + 'default_dir' => self::SORT_DESC + ) + ); + + public function isValidFilterColumn($column) + { + if ($column[0] === '_' + && preg_match('~^_(?:host|service)_~', $column) + ) { + return true; + } + return parent::isValidFilterColumn($column); + } +}