diff --git a/application/controllers/ServicetemplateController.php b/application/controllers/ServicetemplateController.php index 198efafc..ad1007b2 100644 --- a/application/controllers/ServicetemplateController.php +++ b/application/controllers/ServicetemplateController.php @@ -2,6 +2,7 @@ namespace Icinga\Module\Director\Controllers; +use Icinga\Module\Director\Objects\IcingaService; use Icinga\Module\Director\Web\Controller\SimpleController; use Icinga\Module\Director\Web\Table\ServicesOnHostsTable; @@ -14,4 +15,38 @@ class ServicetemplateController extends SimpleController new ServicesOnHostsTable($this->db()) ); } + + public function servicesAction() + { + $template = $this->requireTemplate(); + $this->addSingleTab( + $this->translate('Single Services') + )->addTitle( + $this->translate('Services based on %s'), + $template->getObjectName() + ); + + $this->content()->add( + new ServicesOnHostsTable($this->db()) + ); + } + + public function usageAction() + { + $template = $this->requireTemplate(); + + $this->addSingleTab( + $this->translate('Service Template Usage') + )->addTitle( + $this->translate('Template: %s'), + $template->getObjectName() + ); + } + + protected function requireTemplate() + { + return IcingaService::load([ + 'object_name' => $this->params->get('name') + ], $this->db()); + } } diff --git a/application/controllers/ServicetemplatesController.php b/application/controllers/ServicetemplatesController.php new file mode 100644 index 00000000..33f9ad1c --- /dev/null +++ b/application/controllers/ServicetemplatesController.php @@ -0,0 +1,96 @@ +addSingleTab($this->translate('Service Templates')); + if ($this->quickSearch()) { + + } + $this->addTitle($this->translate('All your Service Templates')); + $this->actions()->add( + $this->getBackToDashboardLink() + )->add( + Link::create( + $this->translate('Add'), + 'director/service/add', + ['type' => 'template'], + [ + 'title' => $this->translate('Create a new Service Template'), + 'class' => 'icon-plus' + ] + ) + ); + + $this->content()->add( + new ServiceTemplatesTable($this->db()) + ); + } + + // TODO: dedicated controller + public function applyrulesAction() + { + $this->addSingleTab($this->translate('Service Apply Rules')); + if ($this->quickSearch()) { + + } + $this->addTitle($this->translate('All your Service Apply Rules')); + $this->actions()->add( + $this->getBackToDashboardLink() + )->add( + Link::create( + $this->translate('Add'), + 'director/service/add', + ['type' => 'apply_rule'], + [ + 'title' => $this->translate('Create a new Service Apply Rule'), + 'class' => 'icon-plus' + ] + ) + ); + + $this->content()->add( + new ServiceApplyRulesTable($this->db()) + ); + } + + /** + * TODO: This should be director/services once it has REST API support + */ + public function servicesAction() + { + $this->addSingleTab( + $this->translate('Single Services') + )->addTitle( + $this->translate('Single Services configured for your hosts') + ); + + $this->content()->add( + new ServicesOnHostsTable($this->db()) + ); + } + + protected function getBackToDashboardLink() + { + return Link::create( + $this->translate('back'), + 'director/dashboard', + ['name' => 'services'], + [ + 'title' => $this->translate('Go back to Services Dashboard'), + 'class' => 'icon-left-big', + 'data-base-target' => '_main' + ] + ); + } +} diff --git a/library/Director/Dashboard/Dashlet/ServiceApplyRulesDashlet.php b/library/Director/Dashboard/Dashlet/ServiceApplyRulesDashlet.php new file mode 100644 index 00000000..92dabf59 --- /dev/null +++ b/library/Director/Dashboard/Dashlet/ServiceApplyRulesDashlet.php @@ -0,0 +1,31 @@ +translate('Service Apply Rules'); + } + + public function getEscapedSummary() + { + return $this->translate( + 'Using Apply Rules a Service can be applied to multiple hosts at once,' + . ' based on filters dealing with any combination of their properties' + ); + } + + public function getUrl() + { + return 'director/servicetemplates/applyrules'; + } + + public function listRequiredPermissions() + { + return array('director/admin'); + } +} diff --git a/library/Director/Dashboard/Dashlet/ServiceGroupsDashlet.php b/library/Director/Dashboard/Dashlet/ServiceGroupsDashlet.php new file mode 100644 index 00000000..4ad9886b --- /dev/null +++ b/library/Director/Dashboard/Dashlet/ServiceGroupsDashlet.php @@ -0,0 +1,31 @@ +translate('Service Groups'); + } + + public function getEscapedSummary() + { + return $this->translate( + 'Defining Service Groups get more structure. Great for Dashboards.' + . ' Notifications and Permissions might be based on groups.' + ); + } + + public function getUrl() + { + return 'director/servicegroups'; + } + + public function listRequiredPermissions() + { + return array('director/admin'); + } +} diff --git a/library/Director/Dashboard/Dashlet/ServiceObjectDashlet.php b/library/Director/Dashboard/Dashlet/ServiceObjectDashlet.php index aa7d41a7..18edbd30 100644 --- a/library/Director/Dashboard/Dashlet/ServiceObjectDashlet.php +++ b/library/Director/Dashboard/Dashlet/ServiceObjectDashlet.php @@ -15,7 +15,7 @@ class ServiceObjectDashlet extends Dashlet public function getUrl() { - return 'director/services'; + return 'director/dashboard?name=services'; } public function listRequiredPermissions() diff --git a/library/Director/Dashboard/Dashlet/ServiceSetsDashlet.php b/library/Director/Dashboard/Dashlet/ServiceSetsDashlet.php new file mode 100644 index 00000000..53b99235 --- /dev/null +++ b/library/Director/Dashboard/Dashlet/ServiceSetsDashlet.php @@ -0,0 +1,31 @@ +translate('Service Sets'); + } + + public function getEscapedSummary() + { + return $this->translate( + 'Grouping your Services into Sets allow you to quickly assign services' + . ' often used together in a single operation all at once' + ); + } + + public function getUrl() + { + return 'director/serviceapply/rules'; + } + + public function listRequiredPermissions() + { + return array('director/admin'); + } +} diff --git a/library/Director/Dashboard/Dashlet/ServiceTemplatesDashlet.php b/library/Director/Dashboard/Dashlet/ServiceTemplatesDashlet.php new file mode 100644 index 00000000..da36c18b --- /dev/null +++ b/library/Director/Dashboard/Dashlet/ServiceTemplatesDashlet.php @@ -0,0 +1,31 @@ +translate('Service Templates'); + } + + public function getEscapedSummary() + { + return $this->translate( + 'Manage your Service Templates. Use Fields to make it easy for' + . ' your users to get them customized.' + ); + } + + public function getUrl() + { + return 'director/servicetemplates'; + } + + public function listRequiredPermissions() + { + return array('director/admin'); + } +} diff --git a/library/Director/Dashboard/Dashlet/SingleServicesDashlet.php b/library/Director/Dashboard/Dashlet/SingleServicesDashlet.php new file mode 100644 index 00000000..5564a768 --- /dev/null +++ b/library/Director/Dashboard/Dashlet/SingleServicesDashlet.php @@ -0,0 +1,31 @@ +translate('Single Services'); + } + + public function getEscapedSummary() + { + return $this->translate( + 'Here you can find all single services directly attached to single' + . ' hosts' + ); + } + + public function getUrl() + { + return 'director/servicetemplates/services'; + } + + public function listRequiredPermissions() + { + return array('director/admin'); + } +} diff --git a/library/Director/Dashboard/ServicesDashboard.php b/library/Director/Dashboard/ServicesDashboard.php new file mode 100644 index 00000000..545decac --- /dev/null +++ b/library/Director/Dashboard/ServicesDashboard.php @@ -0,0 +1,31 @@ +translate('Manage your Icinga Service Checks'); + } + + public function getDescription() + { + return $this->translate( + 'This is where you manage your Icinga 2 Service Checks. Service' + . ' Templates are your base building blocks, Service Sets allow' + . ' you to assign multiple Services at once. Apply Rules make it' + . ' possible to assign Services based on Host properties. And' + . ' the list of all single Service Objects gives you the possibility' + . ' to still modify (or delete) many of them at once.' + ); + } +} diff --git a/library/Director/Web/Table/ReadOnlyFormAvpTable.php b/library/Director/Web/Table/ReadOnlyFormAvpTable.php new file mode 100644 index 00000000..c3b44f36 --- /dev/null +++ b/library/Director/Web/Table/ReadOnlyFormAvpTable.php @@ -0,0 +1,113 @@ +form = $form; + } + + protected function renderDisplayGroups(QuickForm $form) + { + $html = ''; + + foreach ($form->getDisplayGroups() as $group) { + $elements = $this->filterGroupElements($group); + + if (empty($elements)) { + continue; + } + + $html .= '' . $group->getLegend() . ''; + $html .= $this->renderElements($elements); + } + + return $html; + } + + /** + * @param ZfDisplayGroup $group + * @return ZfElement[] + */ + protected function filterGroupElements(ZfDisplayGroup $group) + { + $blacklist = array('disabled', 'assign_filter'); + $elements = array(); + /** @var ZfElement $element */ + foreach ($group->getElements() as $element) { + if ($element->getValue() === null) { + continue; + } + + if ($element->getType() === 'Zend_Form_Element_Hidden') { + continue; + } + + if (in_array($element->getName(), $blacklist)) { + continue; + } + + + $elements[] = $element; + } + + return $elements; + } + + protected function renderElements($elements) + { + $html = ''; + foreach ($elements as $element) { + $html .= $this->renderElement($element); + } + + return $html; + } + + /** + * @param ZfElement $element + * + * @return string + */ + protected function renderElement(ZfElement $element) + { + $value = $element->getValue(); + return '' + . $this->escape($element->getLabel()) + . '' + . $this->renderValue($value) + . ''; + } + + protected function renderValue($value) + { + if (is_string($value)) { + return $this->escape($value); + } elseif (is_array($value)) { + return $this->escape(implode(', ', $value)); + } + return $this->escape(PlainObjectRenderer::render($value)); + } + + protected function escape($string) + { + return htmlspecialchars($string); + } + + public function render() + { + $this->form->initializeForObject(); + return '' . "\n" + . $this->renderDisplayGroups($this->form) + . '
'; + } +} diff --git a/library/Director/Web/Table/ServiceApplyRulesTable.php b/library/Director/Web/Table/ServiceApplyRulesTable.php new file mode 100644 index 00000000..6b847449 --- /dev/null +++ b/library/Director/Web/Table/ServiceApplyRulesTable.php @@ -0,0 +1,133 @@ + ['simple', 'common-table', 'table-row-selectable'], + 'data-base-target' => '_next', + ]; + + private $db; + + public function __construct(Db $connection) + { + $this->db = $connection->getDbAdapter(); + $this->header(); + $this->fetchRows(); + } + + public function getColumnsToBeRendered() + { + return ['Service Name', 'assign where', 'Actions']; + } + + public function renderRow($row) + { + $url = Url::fromPath('director/service/edit', [ + 'id' => $row->id, + ]); + + return static::tr([ + Table::td(Link::create($row->service, $url)), + Table::td($this->renderApplyFilter($row->assign_filter)), + Table::td($this->createActionLinks($row))->setSeparator(' ') + ]); + } + + protected function renderApplyFilter($assignFilter) + { + try { + $string = AssignRenderer::forFilter( + Filter::fromQueryString($assignFilter) + )->renderAssign(); + // Do not prefix it + $string = preg_replace('/^assign where /', '', $string); + } catch (IcingaException $e) { + // ignore errors in filter rendering + $string = 'Error in Filter rendering: ' . $e->getMessage(); + } + + return $string; + } + + public function createActionLinks($row) + { + $links = []; + $links[] = Link::create( + Icon::create('sitemap'), + 'director/servicetemplate/applytargets', + ['id' => $row->id], + ['title' => $this->translate('Show affected Hosts')] + ); + + $links[] = Link::create( + Icon::create('edit'), + 'director/service/edit', + ['id' => $row->id], + ['title' => $this->translate('Modify this Apply Rule')] + ); + + $links[] = Link::create( + Icon::create('doc-text'), + 'director/service/render', + ['id' => $row->id], + ['title' => $this->translate('Apply Rule rendering preview')] + ); + + $links[] = Link::create( + Icon::create('history'), + 'director/service/history', + ['id' => $row->id], + ['title' => $this->translate('Apply rule history')] + ); + + return $links; + } + + protected function fetchRows() + { + $body = $this->body(); + foreach ($this->fetch() as $row) { + $body->add($this->renderRow($row)); + } + } + + public function fetch() + { + return $this->db->fetchAll( + $this->prepareQuery() + ); + } + + public function prepareQuery() + { + $columns = [ + 'id' => 's.id', + 'service' => 's.object_name', + 'assign_filter' => 's.assign_filter', + ]; + $query = $this->db->select()->from( + ['s' => 'icinga_service'], + $columns + )->where( + "object_type = 'apply'" + )->where('service_set_id IS NULL')->order('s.object_name'); + + return $query; + } +} diff --git a/library/Director/Web/Table/ServiceTemplatesTable.php b/library/Director/Web/Table/ServiceTemplatesTable.php new file mode 100644 index 00000000..5d1a7657 --- /dev/null +++ b/library/Director/Web/Table/ServiceTemplatesTable.php @@ -0,0 +1,111 @@ + ['simple', 'common-table', 'table-row-selectable'], + 'data-base-target' => '_next', + ]; + + private $db; + + public function __construct(Db $connection) + { + $this->db = $connection->getDbAdapter(); + $this->header(); + $this->fetchRows(); + } + + public function getColumnsToBeRendered() + { + return ['Template name', 'Actions']; + } + + public function renderRow($row) + { + $url = Url::fromPath('director/service/edit', [ + 'name' => $row->service, + ]); + + return static::tr([ + Table::td(Link::create($row->service, $url)), + Table::td($this->createActionLinks($row))->setSeparator(' ') + ]); + } + + public function createActionLinks($row) + { + $links = []; + $links[] = Link::create( + Icon::create('sitemap'), + 'director/servicetemplate/usage', + ['name' => $row->service], + ['title' => $this->translate('Show template usage')] + ); + + $links[] = Link::create( + Icon::create('edit'), + 'director/service/edit', + ['name' => $row->service], + ['title' => $this->translate('Modify this template')] + ); + + $links[] = Link::create( + Icon::create('doc-text'), + 'director/service/render', + ['name' => $row->service], + ['title' => $this->translate('Template rendering preview')] + ); + + $links[] = Link::create( + Icon::create('history'), + 'director/service/history', + ['name' => $row->service], + ['title' => $this->translate('Template history')] + ); + + return $links; + } + + protected function fetchRows() + { + $body = $this->body(); + foreach ($this->fetch() as $row) { + $body->add($this->renderRow($row)); + } + } + + public function fetch() + { + return $this->db->fetchAll( + $this->prepareQuery() + ); + } + + public function prepareQuery() + { + $columns = [ + 'service' => 's.object_name', + 'id' => 's.id', + ]; + $query = $this->db->select()->from( + ['s' => 'icinga_service'], + $columns + )->where( + "object_type = 'template'" + )->order('s.object_name'); + + return $query; + } +}