diff --git a/application/controllers/HostController.php b/application/controllers/HostController.php index 24de45ab..a01c8d3a 100644 --- a/application/controllers/HostController.php +++ b/application/controllers/HostController.php @@ -2,6 +2,7 @@ namespace Icinga\Module\Director\Controllers; +use dipl\Html\Html; use Exception; use Icinga\Module\Director\CustomVariable\CustomVariableDictionary; use Icinga\Module\Director\Db\AppliedServiceSetLoader; @@ -19,6 +20,7 @@ use Icinga\Module\Director\Web\Table\IcingaHostAppliedForServiceTable; use Icinga\Module\Director\Web\Table\IcingaHostAppliedServicesTable; use Icinga\Module\Director\Web\Table\IcingaHostServiceTable; use Icinga\Module\Director\Web\Table\IcingaServiceSetServiceTable; +use Icinga\Module\Director\Web\Widget\HostServiceRedirector; use Icinga\Web\Url; use dipl\Html\Link; @@ -29,6 +31,10 @@ class HostController extends ObjectController $this->assertPermission('director/hosts'); } + /** + * @return HostgroupRestriction + * @throws \Icinga\Exception\ConfigurationError + */ protected function getHostgroupRestriction() { return new HostgroupRestriction($this->db(), $this->Auth()); @@ -40,6 +46,12 @@ class HostController extends ObjectController $this->addOptionalMonitoringLink(); } + /** + * @throws \Icinga\Exception\ConfigurationError + * @throws \Icinga\Exception\Http\HttpNotFoundException + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\ProgrammingError + */ public function serviceAction() { $host = $this->getHostObject(); @@ -53,6 +65,12 @@ class HostController extends ObjectController ); } + /** + * @throws \Icinga\Exception\ConfigurationError + * @throws \Icinga\Exception\Http\HttpNotFoundException + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\ProgrammingError + */ public function servicesetAction() { $host = $this->getHostObject(); @@ -66,6 +84,11 @@ class HostController extends ObjectController ); } + /** + * @throws \Icinga\Exception\Http\HttpNotFoundException + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\ProgrammingError + */ protected function addServicesHeader() { $host = $this->getHostObject(); @@ -85,6 +108,40 @@ class HostController extends ObjectController )); } + /** + * @throws \Icinga\Exception\ConfigurationError + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\ProgrammingError + */ + public function findserviceAction() + { + $host = $this->getHostObject(); + $redirector = new HostServiceRedirector($host); + $this->redirectNow( + $redirector->getRedirectionUrl($this->params->get('service')) + ); + } + + /** + * @throws \Icinga\Exception\IcingaException + */ + public function invalidserviceAction() + { + $this->content()->add( + Html::tag('p', ['class' => 'error'], sprintf( + $this->translate('No such service: %s'), + $this->params->get('service') + )) + ); + + $this->servicesAction(); + } + + /** + * @throws \Icinga\Exception\ConfigurationError + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\ProgrammingError + */ public function servicesAction() { $this->addServicesHeader(); @@ -149,6 +206,12 @@ class HostController extends ObjectController } } + /** + * @param IcingaHost $host + * @param IcingaHost|null $affectedHost + * @throws \Icinga\Exception\ConfigurationError + * @throws \Icinga\Exception\IcingaException + */ protected function addHostServiceSetTables(IcingaHost $host, IcingaHost $affectedHost = null) { $db = $this->db(); @@ -183,6 +246,12 @@ class HostController extends ObjectController } } + /** + * @throws \Icinga\Exception\ConfigurationError + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\NotFoundError + * @throws \Icinga\Exception\ProgrammingError + */ public function appliedserviceAction() { $db = $this->db(); @@ -216,6 +285,12 @@ class HostController extends ObjectController $this->commonForServices(); } + /** + * @throws \Icinga\Exception\ConfigurationError + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\NotFoundError + * @throws \Icinga\Exception\ProgrammingError + */ public function inheritedserviceAction() { $db = $this->db(); @@ -252,6 +327,12 @@ class HostController extends ObjectController $this->commonForServices(); } + /** + * @throws \Icinga\Exception\ConfigurationError + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\NotFoundError + * @throws \Icinga\Exception\ProgrammingError + */ public function removesetAction() { // TODO: clean this up, use POST @@ -276,6 +357,13 @@ class HostController extends ObjectController ); } + /** + * @throws \Icinga\Exception\ConfigurationError + * @throws \Icinga\Exception\Http\HttpNotFoundException + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\NotFoundError + * @throws \Icinga\Exception\ProgrammingError + */ public function servicesetserviceAction() { $db = $this->db(); @@ -314,6 +402,11 @@ class HostController extends ObjectController $this->commonForServices(); } + /** + * @throws \Icinga\Exception\Http\HttpNotFoundException + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\ProgrammingError + */ protected function commonForServices() { $host = $this->object; @@ -326,6 +419,12 @@ class HostController extends ObjectController $this->tabs()->activate('services'); } + /** + * @throws \Icinga\Exception\Http\HttpNotFoundException + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\NotFoundError + * @throws \Icinga\Exception\ProgrammingError + */ public function agentAction() { $selfService = new SelfService($this->getHostObject(), $this->api()); @@ -365,6 +464,10 @@ class HostController extends ObjectController } } + /** + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\ProgrammingError + */ protected function addOptionalInspectLink() { if (! $this->hasPermission('director/inspect')) { diff --git a/library/Director/ProvidedHook/Monitoring/ServiceActions.php b/library/Director/ProvidedHook/Monitoring/ServiceActions.php index 5127b4cb..6b594d56 100644 --- a/library/Director/ProvidedHook/Monitoring/ServiceActions.php +++ b/library/Director/ProvidedHook/Monitoring/ServiceActions.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Director\ProvidedHook\Monitoring; use Exception; use Icinga\Application\Config; use Icinga\Module\Director\Db; +use Icinga\Module\Director\Objects\IcingaHost; use Icinga\Module\Director\Util; use Icinga\Module\Monitoring\Hook\ServiceActionsHook; use Icinga\Module\Monitoring\Object\Service; @@ -17,31 +18,41 @@ class ServiceActions extends ServiceActionsHook try { return $this->getThem($service); } catch (Exception $e) { - return array(); + return []; } } + /** + * @param Service $service + * @return array + * @throws \Icinga\Exception\ProgrammingError + */ protected function getThem(Service $service) { - $actions = array(); + $actions = []; $db = $this->db(); if (! $db) { - return array(); + return []; } + $hostname = $service->host_name; if (Util::hasPermission('director/inspect')) { - $actions['Inspect'] = Url::fromPath( - 'director/inspect/object', - array( - 'type' => 'service', - 'plural' => 'services', - 'name' => sprintf( - '%s!%s', - $service->host_name, - $service->service_description - ) + $actions['Inspect'] = Url::fromPath('director/inspect/object', [ + 'type' => 'service', + 'plural' => 'services', + 'name' => sprintf( + '%s!%s', + $hostname, + $service->service_description ) - ); + ]); + } + + if (IcingaHost::exists($hostname, $db)) { + $actions['Modify'] = Url::fromPath('director/host/findservice', [ + 'name' => $hostname, + 'service' => $service->service_description + ]); } return $actions; diff --git a/library/Director/Web/Widget/HostServiceRedirector.php b/library/Director/Web/Widget/HostServiceRedirector.php new file mode 100644 index 00000000..8ca8ee07 --- /dev/null +++ b/library/Director/Web/Widget/HostServiceRedirector.php @@ -0,0 +1,210 @@ +host = $host; + $this->db = $host->getConnection(); + } + + /** + * @param $serviceName + * @return Url + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\ProgrammingError + */ + public function getRedirectionUrl($serviceName) + { + if ($url = $this->getSingleServiceUrl($serviceName)) { + return $url; + } elseif ($url = $this->getParentServiceUrl($serviceName)) { + return $url; + } elseif ($url = $this->getAppliedServiceUrl($serviceName)) { + return $url; + } elseif ($url = $this->getServiceSetServiceUrl($serviceName)) { + return $url; + } + + return Url::fromPath('director/host/invalidservice', [ + 'name' => $this->host->getObjectName(), + 'service' => $serviceName, + ]); + } + + /** + * @return IcingaHost[] + * @throws \Icinga\Exception\ProgrammingError + */ + protected function getParents() + { + if ($this->parents === null) { + $this->parents = IcingaTemplateRepository::instanceByObject( + $this->host + )->getTemplatesFor($this->host, true); + } + + return $this->parents; + } + + /** + * @param $serviceName + * @return Url|null + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\ProgrammingError + */ + protected function getSingleServiceUrl($serviceName) + { + if (IcingaService::exists([ + 'host_id' => $this->host->get('id'), + 'object_name' => $serviceName + ], $this->db)) { + return Url::fromPath('director/service/edit', [ + 'name' => $serviceName, + 'host' => $this->host->getObjectName() + ]); + } + + return null; + } + + /** + * @param $serviceName + * @return Url|null + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\ProgrammingError + */ + protected function getParentServiceUrl($serviceName) + { + foreach ($this->getParents() as $parent) { + if (IcingaService::exists([ + 'host_id' => $parent->get('id'), + 'object_name' => $serviceName + ], $this->db)) { + return Url::fromPath('director/host/inheritedservice', [ + 'name' => $this->host->getObjectName(), + 'service' => $serviceName, + 'inheritedFrom' => $parent->getObjectName() + ]); + } + } + + return null; + } + + /** + * @param $serviceName + * @return Url|null + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\ProgrammingError + */ + protected function getServiceSetServiceUrl($serviceName) + { + $ids = [$this->host->get('id')]; + + foreach ($this->getParents() as $parent) { + $ids[] = $parent->get('id'); + } + + $db = $this->db->getDbAdapter(); + $query = $db->select() + ->from( + ['s' => 'icinga_service'], + ['service_set_name' => 'ss.object_name',] + )->join( + ['ss' => 'icinga_service_set'], + 's.service_set_id = ss.id', + [] + )->join( + ['hsi' => 'icinga_service_set_inheritance'], + 'hsi.parent_service_set_id = ss.id', + [] + )->join( + ['hs' => 'icinga_service_set'], + 'hs.id = hsi.service_set_id', + [] + )->where('hs.host_id IN (?)', $ids) + ->where('s.object_name = ?', $serviceName); + + if ($row = $db->fetchRow($query)) { + return Url::fromPath('director/host/servicesetservice', [ + 'name' => $this->host->getObjectName(), + 'service' => $serviceName, + 'set' => $row->service_set_name + ]); + } + + return null; + } + + /** + * @param $serviceName + * @return Url|null + * @throws \Icinga\Exception\ProgrammingError + */ + protected function getAppliedServiceUrl($serviceName) + { + $matcher = $this->getHostApplyMatcher(); + foreach ($this->fetchAllApplyRulesForService($serviceName) as $rule) { + if ($matcher->matchesFilter($rule->filter)) { + return Url::fromPath('director/host/appliedservice', [ + 'name' => $this->host->getObjectName(), + 'service_id' => $rule->id, + ]); + } + } + + return null; + } + + protected function getHostApplyMatcher() + { + if ($this->applyMatcher === null) { + $this->applyMatcher = HostApplyMatches::prepare($this->host); + } + + return $this->applyMatcher; + } + + protected function fetchAllApplyRulesForService($serviceName) + { + $db = $this->db->getDbAdapter(); + $query = $db->select()->from( + ['s' => 'icinga_service'], + [ + 'id' => 's.id', + 'name' => 's.object_name', + 'assign_filter' => 's.assign_filter', + ] + )->where('object_name = ?', $serviceName) + ->where('object_type = ? AND assign_filter IS NOT NULL', 'apply'); + + $allRules = $db->fetchAll($query); + foreach ($allRules as $rule) { + $rule->filter = Filter::fromQueryString($rule->assign_filter); + } + + return $allRules; + } +}