From 8073128d0e07a5409aa20db2a1d80ae79a45f2d0 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 25 May 2018 17:03:33 +0200 Subject: [PATCH] IcingaServiceForm: add blacklist/delete button... ...and a lot of related boilerplate to get the "blacklist" feature working --- application/forms/IcingaServiceForm.php | 204 ++++++++++++++++++ library/Director/Db/Cache/PrefetchCache.php | 12 ++ library/Director/Objects/IcingaService.php | 2 +- .../Director/Web/Form/DirectorObjectForm.php | 7 +- .../Table/IcingaHostAppliedServicesTable.php | 18 +- .../Table/IcingaServiceSetServiceTable.php | 35 ++- public/css/module.less | 2 +- 7 files changed, 271 insertions(+), 9 deletions(-) diff --git a/application/forms/IcingaServiceForm.php b/application/forms/IcingaServiceForm.php index 6358345e..c1e68563 100644 --- a/application/forms/IcingaServiceForm.php +++ b/application/forms/IcingaServiceForm.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Director\Forms; use Icinga\Data\Filter\Filter; +use Icinga\Exception\IcingaException; use Icinga\Exception\ProgrammingError; use Icinga\Module\Director\Data\PropertiesFilter\ArrayCustomVariablesFilter; use Icinga\Module\Director\Exception\NestingError; @@ -43,6 +44,11 @@ class IcingaServiceForm extends DirectorObjectForm return $this; } + /** + * @throws IcingaException + * @throws ProgrammingError + * @throws \Zend_Form_Exception + */ public function setup() { if ($this->providesOverrides()) { @@ -79,6 +85,27 @@ class IcingaServiceForm extends DirectorObjectForm || ($this->object && $this->object->usesVarOverrides()); } + /** + * @throws IcingaException + * @throws ProgrammingError + * @throws \Zend_Form_Exception + */ + protected function addFields() + { + if ($this->providesOverrides() && $this->hasBeenBlacklisted()) { + $this->onAddedFields(); + + return; + } else { + parent::addFields(); + } + } + + /** + * @throws IcingaException + * @throws ProgrammingError + * @throws \Zend_Form_Exception + */ protected function onAddedFields() { if (! $this->providesOverrides()) { @@ -106,8 +133,140 @@ class IcingaServiceForm extends DirectorObjectForm $this->setSubmitLabel(false); } + + if ($this->hasBeenBlacklisted()) { + $this->addDeleteButton($this->translate('Restore')); + } else { + $this->addDeleteButton($this->translate('Blacklist')); + } + + if (! $this->hasSubmitButton()) { + $this->addDisplayGroup([$this->deleteButtonName], 'buttons', [ + 'decorators' => [ + 'FormElements', + ['HtmlTag', ['tag' => 'dl']], + 'DtDdWrapper', + ], + 'order' => 1000, + ]); + } } + /** + * @param IcingaService $service + * @return IcingaService + * @throws ProgrammingError + */ + protected function getFirstParent(IcingaService $service) + { + $objects = $service->imports()->getObjects(); + if (empty($objects)) { + throw new ProgrammingError('Something went wrong, got no parent'); + } + reset($objects); + return current($objects); + } + + /** + * @return bool + * @throws IcingaException + * @throws ProgrammingError + */ + protected function hasBeenBlacklisted() + { + if (! $this->providesOverrides() || $this->object === null) { + return false; + } + + $host = $this->host; + $service = $this->getFirstParent($this->object); + $db = $this->db->getDbAdapter(); + if ($this->providesOverrides()) { + return 1 === (int) $db->fetchOne( + $db->select()->from('icinga_host_service_blacklist', 'COUNT(*)') + ->where('host_id = ?', $host->get('id')) + ->where('service_id = ?', $service->get('id')) + ); + } else { + return false; + } + } + + /** + * @param $object + * @throws IcingaException + * @throws ProgrammingError + * @throws \Zend_Db_Adapter_Exception + */ + protected function deleteObject($object) + { + /** @var IcingaService $object */ + if ($this->providesOverrides()) { + if ($this->hasBeenBlacklisted()) { + $this->removeFromBlacklist(); + } else { + $this->blacklist(); + } + } else { + parent::deleteObject($object); + } + } + + /** + * @throws IcingaException + * @throws ProgrammingError + * @throws \Zend_Db_Adapter_Exception + */ + protected function blacklist() + { + $host = $this->host; + $service = $this->getFirstParent($this->object); + + $db = $this->db->getDbAdapter(); + $host->unsetOverriddenServiceVars($service)->store(); + + if ($db->insert('icinga_host_service_blacklist', [ + 'host_id' => $host->get('id'), + 'service_id' => $service->get('id') + ])) { + $msg = sprintf( + $this->translate('%s has been blacklisted on %s'), + $service->getObjectName(), + $host->getObjectName() + ); + $this->redirectOnSuccess($msg); + } + } + + /** + * @throws IcingaException + * @throws ProgrammingError + */ + protected function removeFromBlacklist() + { + $host = $this->host; + $service = $this->getFirstParent($this->object); + + $db = $this->db->getDbAdapter(); + $where = implode(' AND ', [ + $db->quoteInto('host_id = ?', $host->get('id')), + $db->quoteInto('service_id = ?', $service->get('id')), + ]); + if ($db->delete('icinga_host_service_blacklist', $where)) { + $msg = sprintf( + $this->translate('%s has been removed from blacklist %s'), + $service->getObjectName(), + $host->getObjectName() + ); + $this->redirectOnSuccess($msg); + } + } + + /** + * @param IcingaService $service + * @return $this + * @throws ProgrammingError + */ public function createApplyRuleFor(IcingaService $service) { $this->apply = $service; @@ -118,6 +277,9 @@ class IcingaServiceForm extends DirectorObjectForm return $this; } + /** + * @throws \Zend_Form_Exception + */ protected function setupServiceElements() { if ($this->object) { @@ -144,6 +306,10 @@ class IcingaServiceForm extends DirectorObjectForm ->setButtons(); } + /** + * @throws IcingaException + * @throws ProgrammingError + */ protected function addOverrideHint() { if ($this->object && $this->object->usesVarOverrides()) { @@ -204,6 +370,10 @@ class IcingaServiceForm extends DirectorObjectForm $this->addHtmlHint($hint, ['name' => 'inheritance_hint']); } + /** + * @throws IcingaException + * @throws ProgrammingError + */ protected function setupOnHostForSet() { $msg = $this->translate( @@ -248,6 +418,9 @@ class IcingaServiceForm extends DirectorObjectForm return $this; } + /** + * @throws \Zend_Form_Exception + */ protected function setupHostRelatedElements() { $this->addHidden('host_id', $this->host->id); @@ -291,6 +464,9 @@ class IcingaServiceForm extends DirectorObjectForm return $this; } + /** + * @throws \Zend_Form_Exception + */ protected function setupSetRelatedElements() { $this->addHidden('service_set_id', $this->set->id); @@ -333,6 +509,10 @@ class IcingaServiceForm extends DirectorObjectForm return $this; } + /** + * @return $this + * @throws \Zend_Form_Exception + */ protected function addNameElement() { $this->addElement('text', 'object_name', array( @@ -350,6 +530,10 @@ class IcingaServiceForm extends DirectorObjectForm return $this; } + /** + * @return $this + * @throws \Zend_Form_Exception + */ protected function addHostObjectElement() { if ($this->isObject()) { @@ -366,6 +550,10 @@ class IcingaServiceForm extends DirectorObjectForm return $this; } + /** + * @return $this + * @throws \Zend_Form_Exception + */ protected function addApplyForElement() { if ($this->object->isApplyRule()) { @@ -392,6 +580,10 @@ class IcingaServiceForm extends DirectorObjectForm return $this; } + /** + * @return $this + * @throws \Zend_Form_Exception + */ protected function addGroupsElement() { $groups = $this->enumServicegroups(); @@ -415,6 +607,10 @@ class IcingaServiceForm extends DirectorObjectForm return $this; } + /** + * @return $this + * @throws \Zend_Form_Exception + */ protected function addAgentAndZoneElements() { if (!$this->isTemplate()) { @@ -470,6 +666,10 @@ class IcingaServiceForm extends DirectorObjectForm return $db->fetchPairs($select); } + /** + * @throws IcingaException + * @throws ProgrammingError + */ protected function succeedForOverrides() { $vars = array(); @@ -502,6 +702,10 @@ class IcingaServiceForm extends DirectorObjectForm $this->redirectOnSuccess($msg); } + /** + * @throws IcingaException + * @throws ProgrammingError + */ public function onSuccess() { if ($this->providesOverrides()) { diff --git a/library/Director/Db/Cache/PrefetchCache.php b/library/Director/Db/Cache/PrefetchCache.php index 09c93f74..29ac990d 100644 --- a/library/Director/Db/Cache/PrefetchCache.php +++ b/library/Director/Db/Cache/PrefetchCache.php @@ -6,6 +6,7 @@ use Icinga\Exception\ProgrammingError; use Icinga\Module\Director\CustomVariable\CustomVariable; use Icinga\Module\Director\Db; use Icinga\Module\Director\Objects\IcingaObject; +use Icinga\Module\Director\Resolver\HostServiceBlacklist; use Icinga\Module\Director\Resolver\TemplateTree; /** @@ -31,6 +32,8 @@ class PrefetchCache protected $templateTrees = array(); + protected $hostServiceBlacklist; + public static function initialize(Db $db) { self::$instance = new static($db); @@ -101,6 +104,15 @@ class PrefetchCache } } + public function hostServiceBlacklist() + { + if ($this->hostServiceBlacklist === null) { + $this->hostServiceBlacklist = new HostServiceBlacklist($this->db); + } + + return $this->hostServiceBlacklist; + } + /** * @param IcingaObject $object * @return CustomVariableCache diff --git a/library/Director/Objects/IcingaService.php b/library/Director/Objects/IcingaService.php index dd8e40ba..f807a099 100644 --- a/library/Director/Objects/IcingaService.php +++ b/library/Director/Objects/IcingaService.php @@ -412,7 +412,7 @@ class IcingaService extends IcingaObject $blacklist = $this->getBlacklistedHostnames(); if (! empty($blacklist)) { $output .= sprintf( - " ignore where host.name IN %s\n", + " ignore where host.name in %s\n", c::renderArray($blacklist) ); } diff --git a/library/Director/Web/Form/DirectorObjectForm.php b/library/Director/Web/Form/DirectorObjectForm.php index 272eb5a9..1d585156 100644 --- a/library/Director/Web/Form/DirectorObjectForm.php +++ b/library/Director/Web/Form/DirectorObjectForm.php @@ -781,14 +781,15 @@ abstract class DirectorObjectForm extends DirectorForm } catch (Exception $e) { $this->addUniqueException($e); } + + if ($this->shouldBeDeleted()) { + $this->deleteObject($this->object()); + } } protected function handlePost() { $object = $this->object(); - if ($this->shouldBeDeleted()) { - $this->deleteObject($object); - } $post = $this->getRequest()->getPost(); $this->populate($post); diff --git a/library/Director/Web/Table/IcingaHostAppliedServicesTable.php b/library/Director/Web/Table/IcingaHostAppliedServicesTable.php index fa2fb767..fc7bf64d 100644 --- a/library/Director/Web/Table/IcingaHostAppliedServicesTable.php +++ b/library/Director/Web/Table/IcingaHostAppliedServicesTable.php @@ -52,6 +52,12 @@ class IcingaHostAppliedServicesTable extends SimpleQueryBasedTable public function renderRow($row) { + if ($row->blacklisted === 'y') { + $attributes = ['class' => 'strike-links']; + } else { + $attributes = null; + } + return $this::row([ Link::create( sprintf( @@ -65,7 +71,7 @@ class IcingaHostAppliedServicesTable extends SimpleQueryBasedTable 'service_id' => $row->id, ] ) - ]); + ], $attributes); } public function prepareQuery() @@ -82,7 +88,8 @@ class IcingaHostAppliedServicesTable extends SimpleQueryBasedTable return $ds->select()->columns([ 'id' => 'id', 'name' => 'name', - 'filter' => 'filter', + 'filter' => 'filter', + 'blacklisted' => 'blacklisted', 'assign_filter' => 'assign_filter', ]); } @@ -108,8 +115,13 @@ class IcingaHostAppliedServicesTable extends SimpleQueryBasedTable 'id' => 's.id', 'name' => 's.object_name', 'assign_filter' => 's.assign_filter', + 'blacklisted' => "CASE WHEN hsb.service_id IS NULL THEN 'n' ELSE 'y' END", ] - )->where('object_type = ? AND assign_filter IS NOT NULL', 'apply'); + )->joinLeft( + ['hsb' => 'icinga_host_service_blacklist'], + 's.id = hsb.service_id', + [] + )->group('s.id')->where('object_type = ? AND assign_filter IS NOT NULL', 'apply'); return $db->fetchAll($query); } diff --git a/library/Director/Web/Table/IcingaServiceSetServiceTable.php b/library/Director/Web/Table/IcingaServiceSetServiceTable.php index d47c7918..43b8d6a0 100644 --- a/library/Director/Web/Table/IcingaServiceSetServiceTable.php +++ b/library/Director/Web/Table/IcingaServiceSetServiceTable.php @@ -78,6 +78,12 @@ class IcingaServiceSetServiceTable extends ZfQueryBasedTable return $parent; } + /** + * @param $row + * @return Link + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\ProgrammingError + */ protected function getServiceLink($row) { if ($this->affectedHost) { @@ -111,6 +117,9 @@ class IcingaServiceSetServiceTable extends ZfQueryBasedTable if ($row->disabled === 'y') { $tr->getAttributes()->add('class', 'disabled'); } + if ($row->blacklisted === 'y') { + $tr->getAttributes()->add('class', 'strike-links'); + } return $tr; } @@ -125,6 +134,11 @@ class IcingaServiceSetServiceTable extends ZfQueryBasedTable return $this->title ?: $this->translate('Servicename'); } + /** + * @param HtmlElement $parent + * @throws \Icinga\Exception\IcingaException + * @throws \Icinga\Exception\ProgrammingError + */ protected function addHostHeaderTo(HtmlElement $parent) { if (! $this->host) { @@ -164,9 +178,14 @@ class IcingaServiceSetServiceTable extends ZfQueryBasedTable $parent->add($this::th([$this->getTitle(), $deleteLink])); } + /** + * @return \Zend_Db_Select + * @throws \Icinga\Exception\IcingaException + * @throws \Zend_Db_Select_Exception + */ public function prepareQuery() { - return $this->db()->select()->from( + $query = $this->db()->select()->from( ['s' => 'icinga_service'], [ 'id' => 's.id', @@ -185,5 +204,19 @@ class IcingaServiceSetServiceTable extends ZfQueryBasedTable 's.service_set_id = ?', $this->set->get('id') )->order('s.object_name'); + + if ($this->affectedHost) { + $query->joinLeft( + ['hsb' => 'icinga_host_service_blacklist'], + 's.id = hsb.service_id', + [] + )->group('s.id')->columns([ + 'blacklisted' => "CASE WHEN hsb.service_id IS NULL THEN 'n' ELSE 'y' END", + ]); + } else { + $query->columns(['blacklisted' => "'n'"]); + } + + return $query; } } diff --git a/public/css/module.less b/public/css/module.less index 590ef670..249f7e68 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -380,7 +380,7 @@ form dl { padding: 0; } -.strike-links a { +.strike-links a, table.common-table .strike-links a { text-decoration: line-through; &:hover { text-decoration: line-through;