From b051b2da17838674afa472feae32d400c1fb8d05 Mon Sep 17 00:00:00 2001 From: Markus Frosch Date: Mon, 7 Nov 2016 17:27:01 +0100 Subject: [PATCH 1/7] ObjectsController: Unify feature detection and setAction Groups, Assign and Sets will be checked on the base Object for a group. refs #12891 --- .../Web/Controller/ObjectsController.php | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/library/Director/Web/Controller/ObjectsController.php b/library/Director/Web/Controller/ObjectsController.php index bc80e24c..2feb738f 100644 --- a/library/Director/Web/Controller/ObjectsController.php +++ b/library/Director/Web/Controller/ObjectsController.php @@ -51,6 +51,12 @@ abstract class ObjectsController extends ActionController $object = $this->dummyObject(); if ($object->isGroup()) { $type = substr($type, 0, -5); + /** @var IcingaObject $baseType */ + $baseType = $this->getObjectClassname($type); + $baseObject = $baseType::create(array()); + } + else { + $baseObject = $object; } $tabs->add('objects', array( @@ -66,14 +72,14 @@ abstract class ObjectsController extends ActionController )); } - if ($object->supportsGroups() || $object->isGroup()) { + if ($baseObject->supportsGroups()) { $tabs->add('objectgroups', array( 'url' => sprintf('director/%sgroups', $type), 'label' => $this->translate('Groups') )); } - if ($object->supportsSets() || $object->isGroup() /** Bullshit, need base object, wrong on users */) { + if ($baseObject->supportsSets()) { $tabs->add('sets', array( 'url' => sprintf('director/%ss/sets', $type), 'label' => $this->translate('Sets') @@ -202,14 +208,23 @@ abstract class ObjectsController extends ActionController public function setsAction() { $this->assertPermission('director/admin'); - $this->view->title = $this->translate('Service sets'); + + $dummy = $this->dummyObject(); + $type = $this->getType(); + $Type = ucfirst($type); + + if ($dummy->supportsSets() !== true) { + throw new NotFoundError('Sets are not available for %s', $type); + } + + $this->view->title = $this->translate('Icinga ' . $Type . ' Sets'); $this->view->table = $this - ->loadTable('IcingaServiceSet') + ->loadTable('Icinga' . $Type . 'Set') ->setConnection($this->db()); $this->view->addLink = $this->view->qlink( $this->translate('Add'), - 'director/serviceset/add', + 'director/' . $type . 'set/add', null, array( 'class' => 'icon-plus', @@ -227,6 +242,7 @@ abstract class ObjectsController extends ActionController protected function dummyObject() { if ($this->dummy === null) { + /** @var IcingaObject $class */ $class = $this->getObjectClassname(); $this->dummy = $class::create(array()); if ($this->dummy->hasProperty('object_type')) { @@ -257,9 +273,12 @@ abstract class ObjectsController extends ActionController ); } - protected function getObjectClassname() + protected function getObjectClassname($type = null) { + if ($type === null) { + $type = $this->getType(); + } return 'Icinga\\Module\\Director\\Objects\\Icinga' - . ucfirst($this->getType()); + . ucfirst($type); } } From 39538a3f331020ecf15ad4919a1caafc41638209 Mon Sep 17 00:00:00 2001 From: Markus Frosch Date: Wed, 9 Nov 2016 10:42:56 +0100 Subject: [PATCH 2/7] SyncRule: Support hasCombinedKey for serviceSet refs #12891 --- library/Director/Objects/SyncRule.php | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/library/Director/Objects/SyncRule.php b/library/Director/Objects/SyncRule.php index ebb7cffa..a5a0e9ad 100644 --- a/library/Director/Objects/SyncRule.php +++ b/library/Director/Objects/SyncRule.php @@ -272,6 +272,29 @@ class SyncRule extends DbObject } } + if ($hasHost !== false && $hasObjectName !== false) { + $this->hasCombinedKey = true; + $this->sourceKeyPattern = sprintf( + '%s!%s', + $hasHost, + $hasObjectName + ); + + $this->destinationKeyPattern = '${host}!${object_name}'; + } + } elseif ($this->get('object_type') === 'serviceSet') { + $hasHost = false; + $hasObjectName = false; + + foreach ($this->getSyncProperties() as $key => $property) { + if ($property->destination_field === 'host') { + $hasHost = $property->source_expression; + } + if ($property->destination_field === 'object_name') { + $hasObjectName = $property->source_expression; + } + } + if ($hasHost !== false && $hasObjectName !== false) { $this->hasCombinedKey = true; $this->sourceKeyPattern = sprintf( From 699b6a7293ea47357fab19e3ee78196a2c1b2ace Mon Sep 17 00:00:00 2001 From: Markus Frosch Date: Wed, 9 Nov 2016 10:44:02 +0100 Subject: [PATCH 3/7] SyncPropertyForm: Only try to enum imports when we are able to This allows a user to use fields or custom expression for serviceSet. refs #12891 --- application/forms/SyncPropertyForm.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/application/forms/SyncPropertyForm.php b/application/forms/SyncPropertyForm.php index 771083b9..b8d38359 100644 --- a/application/forms/SyncPropertyForm.php +++ b/application/forms/SyncPropertyForm.php @@ -160,14 +160,16 @@ class SyncPropertyForm extends DirectorObjectForm if ($destination === 'import') { $funcTemplates = 'enum' . ucfirst($this->rule->get('object_type')) . 'Templates'; - $templates = $this->db->$funcTemplates(); - if (! empty($templates)) { - $templates = array_combine($templates, $templates); - } + if (method_exists($this->db, $funcTemplates)) { + $templates = $this->db->$funcTemplates(); + if (! empty($templates)) { + $templates = array_combine($templates, $templates); + } - $importTitle = $this->translate('Existing templates'); - $columns[$importTitle] = $templates; - natsort($columns[$importTitle]); + $importTitle = $this->translate('Existing templates'); + $columns[$importTitle] = $templates; + natsort($columns[$importTitle]); + } } $xpTitle = $this->translate('Expert mode'); From c50b1b09a2c8172d55447678e2a6998135ca5c43 Mon Sep 17 00:00:00 2001 From: Markus Frosch Date: Wed, 9 Nov 2016 11:05:51 +0100 Subject: [PATCH 4/7] IcingaServiceTable: Hide ServiceSet Services from applied ones listing refs #12891 --- application/tables/IcingaServiceTable.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/tables/IcingaServiceTable.php b/application/tables/IcingaServiceTable.php index b2c6c2be..433dc12a 100644 --- a/application/tables/IcingaServiceTable.php +++ b/application/tables/IcingaServiceTable.php @@ -122,7 +122,8 @@ class IcingaServiceTable extends QuickTable 'i.service_id = s.id', array() )->where('i.parent_service_id = ?', $id) - ->where('s.object_type = ?', 'apply'); + ->where('s.object_type = ?', 'apply') + ->where('s.service_set_id IS NULL'); return $db->fetchAll($query); } From b08f3df8829e15678250e9fa3f18c60d3a327124 Mon Sep 17 00:00:00 2001 From: Markus Frosch Date: Wed, 9 Nov 2016 11:31:10 +0100 Subject: [PATCH 5/7] IcingaServiceSet*: Improve Table and view Add filter and pagination, and some nice display features. refs #12891 --- application/tables/IcingaServiceSetTable.php | 56 ++++++++++++++++--- .../Web/Controller/ObjectsController.php | 7 ++- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/application/tables/IcingaServiceSetTable.php b/application/tables/IcingaServiceSetTable.php index a28ef488..64dccd5f 100644 --- a/application/tables/IcingaServiceSetTable.php +++ b/application/tables/IcingaServiceSetTable.php @@ -6,35 +6,50 @@ use Icinga\Module\Director\Web\Table\IcingaObjectTable; class IcingaServiceSetTable extends IcingaObjectTable { + protected $searchColumns = array( + 'name', + ); + public function getColumns() { return array( 'id' => 'sset.id', 'name' => 'sset.object_name', 'object_type' => 'sset.object_type', + 'assign_filter' => 'sset.assign_filter', 'description' => 'sset.description', - 'host_name' => 'h.object_name', + 'count_hosts' => 'count(ssetobj.id)', + 'count_services' => 'count(s.id)', ); } + protected function getRowClasses($row) + { + $class = parent::getRowClasses($row); + + if ($row->object_type === 'template' && $row->assign_filter !== null) { + $class = 'icinga-apply'; + } + + return $class; + } + public function getTitles() { $view = $this->view(); return array( - 'name' => $view->translate('Service set'), + 'name' => $view->translate('Service set'), + 'count_services' => $view->translate('# Services'), + 'count_hosts' => $view->translate('# Hosts'), ); } protected function getActionUrl($row) { - // TODO: Remove once we got a separate apply table if ($row->object_type === 'apply') { $params['id'] = $row->id; } else { $params = array('name' => $row->name); - if ($row->host_name) { - $params['host'] = $row->host_name; - } } return $this->url('director/serviceset', $params); @@ -46,10 +61,33 @@ class IcingaServiceSetTable extends IcingaObjectTable array('sset' => 'icinga_service_set'), array() )->joinLeft( - array('h' => 'icinga_host'), - 'h.id = sset.host_id', + array('ssih' => 'icinga_service_set_inheritance'), + 'ssih.parent_service_set_id = sset.id', array() - )->where('sset.object_type = ?', 'template')->order('sset.object_name'); + )->joinLeft( + array('ssetobj' => 'icinga_service_set'), + 'ssetobj.id = ssih.service_set_id', + array() + )->joinLeft( + array('s' => 'icinga_service'), + 's.service_set_id = sset.id', + array() + )->group('sset.id') + ->where('sset.object_type = ?', 'template')->order('sset.object_name'); + } + + public function count() + { + $db = $this->db(); + $sub = clone($this->getBaseQuery()); + $sub->columns($this->getColumns()); + $this->applyFiltersToQuery($sub); + $query = $db->select()->from( + array('sub' => $sub), + 'COUNT(*)' + ); + + return $db->fetchOne($query); } public function getBaseQuery() diff --git a/library/Director/Web/Controller/ObjectsController.php b/library/Director/Web/Controller/ObjectsController.php index 2feb738f..550f3185 100644 --- a/library/Director/Web/Controller/ObjectsController.php +++ b/library/Director/Web/Controller/ObjectsController.php @@ -8,6 +8,8 @@ use Icinga\Exception\NotFoundError; use Icinga\Data\Filter\Filter; use Icinga\Module\Director\Objects\IcingaObject; use Icinga\Module\Director\Web\Table\IcingaObjectTable; +use Icinga\Module\Director\Web\Table\QuickTable; +use Icinga\Web\Widget\FilterEditor; abstract class ObjectsController extends ActionController { @@ -218,9 +220,7 @@ abstract class ObjectsController extends ActionController } $this->view->title = $this->translate('Icinga ' . $Type . ' Sets'); - $this->view->table = $this - ->loadTable('Icinga' . $Type . 'Set') - ->setConnection($this->db()); + $table = $this->loadTable('Icinga' . $Type . 'Set')->setConnection($this->db()); $this->view->addLink = $this->view->qlink( $this->translate('Add'), @@ -232,6 +232,7 @@ abstract class ObjectsController extends ActionController ) ); + $this->provideFilterEditorForTable($table); $this->getTabs()->activate('sets'); $this->setViewScript('objects/table'); } From ddd59ab274d1c6bff93d8ed18c9d35ef1210401a Mon Sep 17 00:00:00 2001 From: Markus Frosch Date: Thu, 10 Nov 2016 14:04:05 +0100 Subject: [PATCH 6/7] IcingaServiceSet: A simple key identifies a ServiceSet (template) refs #12891 --- library/Director/Objects/IcingaServiceSet.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/library/Director/Objects/IcingaServiceSet.php b/library/Director/Objects/IcingaServiceSet.php index 371e88c8..12c6f39a 100644 --- a/library/Director/Objects/IcingaServiceSet.php +++ b/library/Director/Objects/IcingaServiceSet.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Director\Objects; use Icinga\Data\Filter\Filter; +use Icinga\Exception\IcingaException; use Icinga\Module\Director\IcingaConfig\IcingaConfig; @@ -43,6 +44,26 @@ class IcingaServiceSet extends IcingaObject return true; } + protected function setKey($key) + { + if (is_int($key)) { + $this->id = $key; + } elseif (is_string($key)) { + $keyComponents = preg_split('~!~', $key); + if (count($keyComponents) === 1) { + $this->set('object_name', $keyComponents[0]); + $this->set('object_type', 'template'); + } + else { + throw new IcingaException('Can not parse key: %s', $key); + } + } else { + return parent::setKey($key); + } + + return $this; + } + /** * @return IcingaService[] */ From 8b5689545ca40dd5c5ea99b6ca171b081ceb4a16 Mon Sep 17 00:00:00 2001 From: Markus Frosch Date: Thu, 10 Nov 2016 14:05:18 +0100 Subject: [PATCH 7/7] Sync(Rule): Allow to sync services of a ServiceSet This extends the destination key pattern for Sync. refs #12891 --- library/Director/Import/Sync.php | 5 ++++- library/Director/Objects/SyncRule.php | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/library/Director/Import/Sync.php b/library/Director/Import/Sync.php index d7b518a6..330dd4c3 100644 --- a/library/Director/Import/Sync.php +++ b/library/Director/Import/Sync.php @@ -349,7 +349,10 @@ class Sync ) as $object) { if ($object instanceof IcingaService) { - if (! $object->host_id) { + if (strstr($destinationKeyPattern, '${host}') && $object->host_id === null) { + continue; + } + elseif (strstr($destinationKeyPattern, '${service_set}') && $object->service_set_id === null) { continue; } } diff --git a/library/Director/Objects/SyncRule.php b/library/Director/Objects/SyncRule.php index a5a0e9ad..452a3dda 100644 --- a/library/Director/Objects/SyncRule.php +++ b/library/Director/Objects/SyncRule.php @@ -267,6 +267,9 @@ class SyncRule extends DbObject if ($property->destination_field === 'host') { $hasHost = $property->source_expression; } + if ($property->destination_field === 'service_set') { + $hasServiceSet = $property->source_expression; + } if ($property->destination_field === 'object_name') { $hasObjectName = $property->source_expression; } @@ -282,6 +285,16 @@ class SyncRule extends DbObject $this->destinationKeyPattern = '${host}!${object_name}'; } + elseif ($hasServiceSet !== false && $hasObjectName !== false) { + $this->hasCombinedKey = true; + $this->sourceKeyPattern = sprintf( + '%s!%s', + $hasServiceSet, + $hasObjectName + ); + + $this->destinationKeyPattern = '${service_set}!${object_name}'; + } } elseif ($this->get('object_type') === 'serviceSet') { $hasHost = false; $hasObjectName = false;