diff --git a/application/clicommands/DependencyCommand.php b/application/clicommands/DependencyCommand.php new file mode 100644 index 00000000..ff5cbdc6 --- /dev/null +++ b/application/clicommands/DependencyCommand.php @@ -0,0 +1,15 @@ +tabs()->remove('index'); + return $res; + } + + public function applyrulesAction() + { + $this->content()->add(Html::tag( + 'p', + ['class' => 'warning'], + $this->translate('This feature is still experimental') + )); + + parent::applyrulesAction(); + } +} diff --git a/application/controllers/DependencyController.php b/application/controllers/DependencyController.php new file mode 100644 index 00000000..f9346462 --- /dev/null +++ b/application/controllers/DependencyController.php @@ -0,0 +1,58 @@ +params->get('apply')) { + $this->apply = IcingaDependency::load( + array('object_name' => $apply, 'object_type' => 'template'), + $this->db() + ); + } + } + + protected function loadObject() + { + if ($this->object === null) { + if ($name = $this->params->get('name')) { + $params = array('object_name' => $name); + $db = $this->db(); + + $this->object = IcingaDependency::load($params, $db); + } else { + parent::loadObject(); + } + } + + return $this->object; + } + + public function loadForm($name) + { + $form = parent::loadForm($name); + return $form; + } + + protected function beforeHandlingAddRequest($form) + { + /** @var IcingaDependencyForm $form */ + if ($this->apply) { + $form->createApplyRuleFor($this->apply); + } + } +} diff --git a/application/controllers/DependencytemplateController.php b/application/controllers/DependencytemplateController.php new file mode 100644 index 00000000..e2bc49d1 --- /dev/null +++ b/application/controllers/DependencytemplateController.php @@ -0,0 +1,16 @@ + $this->params->get('name') + ], $this->db()); + } +} diff --git a/application/controllers/SuggestController.php b/application/controllers/SuggestController.php index dad12236..323ff4a7 100644 --- a/application/controllers/SuggestController.php +++ b/application/controllers/SuggestController.php @@ -2,10 +2,12 @@ namespace Icinga\Module\Director\Controllers; +use Icinga\Module\Director\Objects\IcingaHost; use Icinga\Module\Director\Objects\IcingaService; use Icinga\Module\Director\Web\Controller\ActionController; use Icinga\Data\Filter\Filter; use ipl\Html\Util; +use Icinga\Module\Director\Objects\HostApplyMatches; class SuggestController extends ActionController { @@ -91,6 +93,47 @@ class SuggestController extends ActionController return $db->fetchCol($query); } + protected function suggestServicenames() + { + $r=array(); + $this->assertPermission('director/services'); + $db = $this->db()->getDbAdapter(); + $for_host = $this->getRequest()->getPost('for_host'); + if (!empty($for_host)) { + $tmp_host = IcingaHost::load($for_host, $this->db()); + } + + $query = $db->select()->distinct() + ->from('icinga_service', 'object_name') + ->order('object_name') + ->where("object_type IN ('object','apply')"); + if (!empty($tmp_host)) { + $query->where('host_id = ?', $tmp_host->id); + } + $r = array_merge($r, $db->fetchCol($query)); + if (!empty($tmp_host)) { + $resolver = $tmp_host->templateResolver(); + foreach ($resolver->fetchResolvedParents() as $template_obj) { + $query = $db->select()->distinct() + ->from('icinga_service', 'object_name') + ->order('object_name') + ->where("object_type IN ('object','apply')") + ->where('host_id = ?', $template_obj->id); + $r = array_merge($r, $db->fetchCol($query)); + } + + $matcher = HostApplyMatches::prepare($tmp_host); + foreach ($this->getAllApplyRules() as $rule) { + if ($matcher->matchesFilter($rule->filter)) { //TODO + $r[]=$rule->name; + } + } + } + natcasesort($r); + return $r; + } + + protected function suggestHosttemplates() { $this->assertPermission('director/hosts'); @@ -199,6 +242,17 @@ class SuggestController extends ActionController return $res; } + protected function suggestDependencytemplates() + { + $this->assertPermission('director/hosts'); + $db = $this->db()->getDbAdapter(); + $query = $db->select() + ->from('icinga_dependency', 'object_name') + ->order('object_name') + ->where("object_type = 'template'"); + return $db->fetchCol($query); + } + protected function highlight($val, $search) { $search = ($search); @@ -209,4 +263,29 @@ class SuggestController extends ActionController $val ); } + + protected function getAllApplyRules() + { + $allApplyRules=$this->fetchAllApplyRules(); + foreach ($allApplyRules as $rule) { + $rule->filter = Filter::fromQueryString($rule->assign_filter); + } + + return $allApplyRules; + } + + protected function fetchAllApplyRules() + { + $db = $this->db()->getDbAdapter(); + $query = $db->select()->from( + array('s' => 'icinga_service'), + array( + 'id' => 's.id', + 'name' => 's.object_name', + 'assign_filter' => 's.assign_filter', + ) + )->where('object_type = ? AND assign_filter IS NOT NULL', 'apply'); + + return $db->fetchAll($query); + } } diff --git a/application/forms/IcingaDependencyForm.php b/application/forms/IcingaDependencyForm.php new file mode 100644 index 00000000..7be8b313 --- /dev/null +++ b/application/forms/IcingaDependencyForm.php @@ -0,0 +1,240 @@ +setupDependencyElements(); + } + + protected function setupDependencyElements() + { + $this->addObjectTypeElement(); + if (! $this->hasObjectType()) { + $this->groupMainProperties(); + return; + } + + $this->addNameElement() + ->addDisabledElement() + ->addImportsElement() + ->addObjectsElement() + ->addBooleanElements() + ->addPeriodElement() + ->addAssignmentElements() + ->addEventFilterElements(array('states')) + ->groupMainProperties() + ->setButtons(); + } + + protected function addNameElement() + { + $this->addElement('text', 'object_name', [ + 'label' => $this->translate('Name'), + 'required' => true, + 'description' => $this->translate('Name for the Icinga dependency you are going to create') + ]); + + return $this; + } + + + protected function addAssignmentElements() + { + if (!$this->object || !$this->object->isApplyRule()) { + return $this; + } + + $this->addElement('select', 'apply_to', [ + 'label' => $this->translate('Apply to'), + 'description' => $this->translate( + 'Whether this dependency should affect hosts or services' + ), + 'required' => true, + 'class' => 'autosubmit', + 'multiOptions' => $this->optionalEnum([ + 'host' => $this->translate('Hosts'), + 'service' => $this->translate('Services'), + ]) + ]); + + $applyTo = $this->getSentOrObjectValue('apply_to'); + + if ($applyTo === 'host') { + $columns = IcingaHost::enumProperties($this->db, 'host.'); + } elseif ($applyTo === 'service') { + // TODO: Also add host properties + $columns = IcingaService::enumProperties($this->db, 'service.'); + } else { + return $this; + } + + $this->addAssignFilter(array( + 'columns' => $columns, + 'required' => true, + 'description' => $this->translate( + 'This allows you to configure an assignment filter. Please feel' + . ' free to combine as many nested operators as you want' + ) + )); + return $this; + } + + protected function addPeriodElement() + { + $periods = $this->db->enumTimeperiods(); + if (empty($periods)) { + return $this; + } + + $this->addElement( + 'select', + 'period_id', + array( + 'label' => $this->translate('Time period'), + 'description' => $this->translate( + 'The name of a time period which determines when this' + . ' notification should be triggered. Not set by default.' + ), + 'multiOptions' => $this->optionalEnum($periods), + ) + ); + + return $this; + } + + protected function addBooleanElements() + { + $this->addBoolean('disable_checks', [ + 'label' => $this->translate('Disable Checks'), + 'description' => $this->translate( + 'Whether to disable checks when this dependency fails.' + . ' Defaults to false.' + ) + ], null); + + $this->addBoolean('disable_notifications', [ + 'label' => $this->translate('Disable Notificiations'), + 'description' => $this->translate( + 'Whether to disable notifications when this dependency fails.' + . ' Defaults to true.' + ) + ], null); + + $this->addBoolean('ignore_soft_states', [ + 'label' => $this->translate('Ignore Soft States'), + 'description' => $this->translate( + 'Whether to ignore soft states for the reachability calculation.' + . ' Defaults to true.' + ) + ], null); + + return $this; + } + + protected function addObjectsElement() + { + $this->addElement( + 'text', + 'parent_host', + array( + 'label' => $this->translate('Parent Host'), + 'description' => $this->translate( + 'The parent host.' + ), + 'class' => "autosubmit director-suggest", + 'data-suggestion-context' => 'hostnames', + 'order' => 10, + 'required' => $this->isObject(), + 'value' => $this->getObject()->get('parent_host') + + ) + ); + $sent_parent=$this->getSentOrObjectValue("parent_host"); + + if (!empty($sent_parent) || $this->object->isApplyRule()) { + $this->addElement( + 'text', + 'parent_service', + array( + 'label' => $this->translate('Parent Service'), + 'description' => $this->translate( + 'Optional. The parent service. If omitted this dependency object is treated as host dependency.' + ), + 'class' => "autosubmit director-suggest", + 'data-suggestion-context' => 'servicenames', + 'data-suggestion-for-host' => $sent_parent, + 'order' => 20, + 'value' => $this->getObject()->get('parent_service') + ) + ); + } + + // If configuring Object, allow selection of child host and/or service, + // otherwise apply rules will determine child object. + if ($this->isObject()) { + $this->addElement( + 'text', + 'child_host', + array( + 'label' => $this->translate('Child Host'), + 'description' => $this->translate( + 'The child host.' + ), + 'value' => $this->getObject()->get('child_host'), + 'order' => 30, + 'class' => "autosubmit director-suggest", + 'required' => $this->isObject(), + 'data-suggestion-context' => 'hostnames', + ) + ); + + $sent_child=$this->getSentOrObjectValue("child_host"); + + if (!empty($sent_child)) { + $this->addElement('text', 'child_service', [ + 'label' => $this->translate('Child Service'), + 'description' => $this->translate( + 'Optional. The child service. If omitted this dependency' + . ' object is treated as host dependency.' + ), + 'class' => "autosubmit director-suggest", + 'order' => 40, + 'value' => $this->getObject()->get('child_service'), + 'data-suggestion-context' => 'servicenames', + 'data-suggestion-for-host' => $sent_child, + ]); + } + } + + $elements = ['parent_host', 'child_host', 'parent_service', 'child_service']; + $this->addDisplayGroup($elements, 'related_objects', [ + 'decorators' => [ + 'FormElements', + ['HtmlTag', ['tag' => 'dl']], + 'Fieldset', + ], + 'order' => 25, + 'legend' => $this->translate('Related Objects') + ]); + + return $this; + } + + public function createApplyRuleFor(IcingaDependency $dependency) + { + $object = $this->object(); + $object->setImports($dependency->getObjectName()); + $object->set('object_type', 'apply'); + $object->set('object_name', $dependency->getObjectName()); + + return $this; + } +} diff --git a/library/Director/Dashboard/Dashlet/DependencyObjectDashlet.php b/library/Director/Dashboard/Dashlet/DependencyObjectDashlet.php new file mode 100644 index 00000000..47a18aa8 --- /dev/null +++ b/library/Director/Dashboard/Dashlet/DependencyObjectDashlet.php @@ -0,0 +1,26 @@ +translate('Dependencies'); + } + + public function getSummary() + { + return $this->translate('Object dependency relationships.') + . ' ' . parent::getSummary(); + } + + public function getUrl() + { + return 'director/dependencies/applyrules'; + } +} diff --git a/library/Director/Dashboard/ObjectsDashboard.php b/library/Director/Dashboard/ObjectsDashboard.php index 5de0d866..0688d4ab 100644 --- a/library/Director/Dashboard/ObjectsDashboard.php +++ b/library/Director/Dashboard/ObjectsDashboard.php @@ -9,6 +9,7 @@ class ObjectsDashboard extends Dashboard 'ServiceObject', 'CommandObject', // 'Notifications', + 'DependencyObject', ); public function getTitle() diff --git a/library/Director/Db.php b/library/Director/Db.php index 1196d9f1..4c621501 100644 --- a/library/Director/Db.php +++ b/library/Director/Db.php @@ -389,6 +389,7 @@ class Db extends DbConnection 'apiuser', 'endpoint', 'zone', + 'dependency', ); $queries = array(); diff --git a/library/Director/IcingaConfig/IcingaConfig.php b/library/Director/IcingaConfig/IcingaConfig.php index 3713f0ed..e79e9707 100644 --- a/library/Director/IcingaConfig/IcingaConfig.php +++ b/library/Director/IcingaConfig/IcingaConfig.php @@ -477,6 +477,7 @@ class IcingaConfig ->createFileFromDb('userGroup') ->createFileFromDb('user') ->createFileFromDb('notification') + ->createFileFromDb('dependency') ; if (! $this->isLegacy()) { @@ -719,7 +720,8 @@ apply Service for (title => params in host.vars["%s"]) { 'user', 'userGroup', 'timePeriod', - 'notification' + 'notification', + 'dependency' ); return in_array($type, $types); diff --git a/library/Director/Objects/IcingaDependency.php b/library/Director/Objects/IcingaDependency.php new file mode 100644 index 00000000..efd481d5 --- /dev/null +++ b/library/Director/Objects/IcingaDependency.php @@ -0,0 +1,468 @@ + null, + 'object_name' => null, + 'object_type' => null, + 'disabled' => 'n', + 'apply_to' => null, + 'parent_host_id' => null, + 'parent_service_id' => null, + 'child_host_id' => null, + 'child_service_id' => null, + 'disable_checks' => null, + 'disable_notifications' => null, + 'ignore_soft_states' => null, + 'period_id' => null, + 'zone_id' => null, + 'assign_filter' => null, + 'parent_service_by_name' => null, + ]; + + protected $supportsCustomVars = false; + + protected $supportsImports = true; + + protected $supportsApplyRules = true; + + protected $relatedSets = [ + 'states' => 'StateFilterSet', + ]; + + protected $relations = [ + 'zone' => 'IcingaZone', + 'parent_host' => 'IcingaHost', + 'parent_service' => 'IcingaService', + 'child_host' => 'IcingaHost', + 'child_service' => 'IcingaService', + 'period' => 'IcingaTimePeriod', + ]; + + protected $booleans = [ + 'disable_checks' => 'disable_checks', + 'disable_notifications' => 'disable_notifications', + 'ignore_soft_states' => 'ignore_soft_states' + ]; + + /** + * Do not render internal property apply_to + * + * Avoid complaints for method names with underscore: + * @codingStandardsIgnoreStart + * + * @return string + */ + public function renderApply_to() + { + // @codingStandardsIgnoreEnd + return ''; + } + + protected function renderObjectHeader() + { + if ($this->isApplyRule()) { + if (($to = $this->get('apply_to')) === null) { + throw new ConfigurationError( + 'Applied dependency "%s" has no valid object type', + $this->getObjectName() + ); + } + + return sprintf( + "%s %s %s to %s {\n", + $this->getObjectTypeName(), + $this->getType(), + c::renderString($this->getObjectName()), + ucfirst($to) + ); + } else { + return parent::renderObjectHeader(); + } + } + + protected function setKey($key) + { + // TODO: Check if this method can be removed + if (is_int($key)) { + $this->id = $key; + } elseif (is_array($key)) { + $keys = [ + 'id', + 'parent_host_id', + 'parent_service_id', + 'child_host_id', + 'child_service_id', + 'object_name' + ]; + + foreach ($keys as $k) { + if (array_key_exists($k, $key)) { + $this->set($k, $key[$k]); + } + } + } else { + return parent::setKey($key); + } + + return $this; + } + + protected function renderAssignments() + { + // TODO: this will never be reached + if ($this->hasBeenAssignedToServiceApply()) { + /** @var IcingaService $tmpService */ + $tmpService = $this->getRelatedObject( + 'child_service', + $this->get('child_service_id') + ); + // TODO: fix this, will crash: + $assigns = $tmpService->assignments()->toConfigString(); + + $filter = sprintf( + '%s && service.name == "%s"', + trim($assigns), + $this->get('child_service') + ); + return "\n " . $filter . "\n"; + } + + if ($this->hasBeenAssignedToHostTemplateService()) { + $filter = sprintf( + 'assign where "%s" in host.templates && service.name == "%s"', + $this->get('child_host'), + $this->get('child_service') + ); + return "\n " . $filter . "\n"; + } + if ($this->hasBeenAssignedToHostTemplate()) { + $filter = sprintf( + 'assign where "%s" in host.templates', + $this->get('child_host') + ); + return "\n " . $filter . "\n"; + } + + if ($this->hasBeenAssignedToServiceTemplate()) { + $filter = sprintf( + 'assign where "%s" in service.templates', + $this->get('child_service') + ); + return "\n " . $filter . "\n"; + } + + return parent::renderAssignments(); + } + + protected function hasBeenAssignedToHostTemplate() + { + try { + $id = $this->get('child_host_id'); + return $id && $this->getRelatedObject( + 'child_host', + $id + )->isTemplate(); + } catch (NotFoundError $e) { + return false; + } + } + + protected function hasBeenAssignedToServiceTemplate() + { + try { + $id = $this->get('child_service_id'); + return $id && $this->getRelatedObject( + 'child_service', + $id + )->isTemplate(); + } catch (NotFoundError $e) { + return false; + } + } + + protected function hasBeenAssignedToHostTemplateService() + { + if (!$this->hasBeenAssignedToHostTemplate()) { + return false; + } + try { + $id = $this->get('child_service_id'); + return $id && $this->getRelatedObject( + 'child_service', + $id + )->isObject(); + } catch (NotFoundError $e) { + return false; + } + } + + protected function hasBeenAssignedToServiceApply() + { + try { + $id = $this->get('child_service_id'); + return $id && $this->getRelatedObject( + 'child_service', + $id + )->isApplyRule(); + } catch (NotFoundError $e) { + return false; + } + } + + /** + * Render child_host_id as host_name + * + * Avoid complaints for method names with underscore: + * @codingStandardsIgnoreStart + * + * @return string + */ + public function renderChild_host_id() + { + // @codingStandardsIgnoreEnd + + if ($this->hasBeenAssignedToHostTemplate()) { + return ''; + } + + return $this->renderRelationProperty( + 'child_host', + $this->get('child_host_id'), + 'child_host_name' + ); + } + + /** + * Render parent_host_id as parent_host_name + * + * Avoid complaints for method names with underscore: + * @codingStandardsIgnoreStart + * + * @return string + */ + public function renderParent_host_id() + { + // @codingStandardsIgnoreEnd + + return $this->renderRelationProperty( + 'parent_host', + $this->get('parent_host_id'), + 'parent_host_name' + ); + } + + /** + * Render child_service_id as host_name + * + * Avoid complaints for method names with underscore: + * @codingStandardsIgnoreStart + * + * @return string + */ + public function renderChild_service_id() + { + // @codingStandardsIgnoreEnd + if ($this->hasBeenAssignedToServiceTemplate() + || $this->hasBeenAssignedToHostTemplateService() + || $this->hasBeenAssignedToServiceApply() + ) { + return ''; + } + + return $this->renderRelationProperty( + 'child_service', + $this->get('child_service_id'), + 'child_service_name' + ); + } + + /** + * Render parent_service_id as parent_service_name + * + * Avoid complaints for method names with underscore: + * @codingStandardsIgnoreStart + * + * @return string + */ + public function renderParent_service_id() + { + return $this->renderRelationProperty( + 'parent_service', + $this->get('parent_service_id'), + 'parent_service_name' + ); + } + + //special case for parent service set as plain string for Apply rules + public function renderParent_service_by_name() + { + return c::renderKeyValue( + 'parent_service_name', + c::renderString($this->get('parent_service_by_name')) + ); + } + + public function isApplyRule() + { + if ($this->hasBeenAssignedToHostTemplate() + || $this->hasBeenAssignedToServiceTemplate() + || $this->hasBeenAssignedToServiceApply() + ) { + return true; + } + + return parent::isApplyRule(); + } + + protected function resolveUnresolvedRelatedProperty($name) + { + $short = substr($name, 0, -3); + /** @var IcingaObject $class */ + $class = $this->getRelationClass($short); + $objKey = $this->unresolvedRelatedProperties[$name]; + + # related services need array key + if ($class == "Icinga\Module\Director\Objects\IcingaService" ) { + if ($name === 'parent_service_id' && $this->object_type === 'apply' ) { + //special case , parent service can be set as simple string for Apply + if ($this->properties['parent_host_id'] === null) { + $this->reallySet( + 'parent_service_by_name', + $this->unresolvedRelatedProperties[$name] + ); + $this->reallySet('parent_service_id', null); + unset($this->unresolvedRelatedProperties[$name]); + return; + } + } + + $this->reallySet('parent_service_by_name', null); + $hostIdProperty = str_replace('service', 'host', $name); + if (isset($this->properties[$hostIdProperty])) { + $objKey = [ + 'host_id' => $this->properties[$hostIdProperty], + 'object_name' => $this->unresolvedRelatedProperties[$name] + ]; + } else { + $objKey = [ + 'host_id' => null, + 'object_name' => $this->unresolvedRelatedProperties[$name] + ]; + } + + try { + $class::load( $objKey, $this->connection); + } catch (NotFoundError $e) { + // Not a simple service on host + // Hunt through inherited services, use service assigned to + // template if found + $tmpHost = IcingaHost::loadWithAutoIncId( + $this->properties[$hostIdProperty], + $this->connection + ); + + // services for applicable templates + $resolver = $tmpHost->templateResolver(); + foreach ($resolver->fetchResolvedParents() as $template_obj) { + $objKey = [ + 'host_id' => $template_obj->id, + 'object_name' => $this->unresolvedRelatedProperties[$name] + ]; + try { + $object = $class::load( $objKey, $this->connection); + } catch (NotFoundError $e) { + continue; + } + break; + } + + if (!isset($object)) { + // Not an inherited service, now try apply rules + $matcher = HostApplyMatches::prepare($tmpHost); + foreach ($this->getAllApplyRules() as $rule) { + if ($matcher->matchesFilter($rule->filter)) { + if ($rule->name === $this->unresolvedRelatedProperties[$name]) { + $object = IcingaService::loadWithAutoIncId( + $rule->id, + $this->connection + ); + break; + } + } + } + } + } + } else { + $object = $class::load($objKey, $this->connection); + } + + if (isset($object)) { + $this->reallySet($name, $object->get('id')); + unset($this->unresolvedRelatedProperties[$name]); + } else { + throw new NotFoundError('Unable to resolve related property: "%s"', $name); + } + } + + protected function getAllApplyRules() + { + $allApplyRules = $this->fetchAllApplyRules(); + foreach ($allApplyRules as $rule) { + $rule->filter = Filter::fromQueryString($rule->assign_filter); + } + + return $allApplyRules; + } + + protected function fetchAllApplyRules() + { + $db = $this->connection->getDbAdapter(); + $query = $db->select()->from( + array('s' => 'icinga_service'), + array( + 'id' => 's.id', + 'name' => 's.object_name', + 'assign_filter' => 's.assign_filter', + ) + )->where('object_type = ? AND assign_filter IS NOT NULL', 'apply'); + + return $db->fetchAll($query); + } + + protected function getRelatedProperty($key) + { + $idKey = $key . '_id'; + if ($this->hasUnresolvedRelatedProperty($idKey)) { + return $this->unresolvedRelatedProperties[$idKey]; + } + + if ($id = $this->get($idKey)) { + /** @var IcingaObject $class */ + $class = $this->getRelationClass($key); + $object = $class::loadWithAutoIncId($id, $this->connection); + + return $object->getObjectName(); + } else { + // handle special case for plain string parent service on Dependency + // Apply rules + if ($key === 'parent_service' + && null !== $this->get('parent_service_by_name') + ) { + return $this->get('parent_service_by_name'); + } + } + + return null; + } +} diff --git a/library/Director/Objects/IcingaObject.php b/library/Director/Objects/IcingaObject.php index 9f756046..a4eecfc3 100644 --- a/library/Director/Objects/IcingaObject.php +++ b/library/Director/Objects/IcingaObject.php @@ -2699,7 +2699,8 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer public function getOnDeleteUrl() { - return 'director/' . strtolower($this->getShortTableName()) . 's'; + $plural= preg_replace('/cys$/', 'cies', strtolower($this->getShortTableName()) . 's'); + return 'director/' . $plural; } public function toJson( diff --git a/library/Director/Web/ActionBar/TemplateActionBar.php b/library/Director/Web/ActionBar/TemplateActionBar.php index e6d7f766..56eeee95 100644 --- a/library/Director/Web/ActionBar/TemplateActionBar.php +++ b/library/Director/Web/ActionBar/TemplateActionBar.php @@ -9,6 +9,7 @@ class TemplateActionBar extends DirectorBaseActionBar protected function assemble() { $type = $this->type; + $plType = preg_replace('/cys$/', 'cies', $type . 's'); $renderTree = $this->url->getParam('render') === 'tree'; $renderParams = $renderTree ? null : ['render' => 'tree']; $this->add( @@ -27,7 +28,7 @@ class TemplateActionBar extends DirectorBaseActionBar )->add( Link::create( $renderTree ? $this->translate('Table') : $this->translate('Tree'), - "director/${type}s/templates", + "director/$plType/templates", $renderParams, [ 'class' => 'icon-' . ($renderTree ? 'doc-text' : 'sitemap'), diff --git a/library/Director/Web/Controller/ObjectsController.php b/library/Director/Web/Controller/ObjectsController.php index 98c39b01..975277c9 100644 --- a/library/Director/Web/Controller/ObjectsController.php +++ b/library/Director/Web/Controller/ObjectsController.php @@ -33,6 +33,7 @@ abstract class ObjectsController extends ActionController $this->assertPermission('director/' . $this->getPluralBaseType()); } } + /** * @return $this */ @@ -80,7 +81,7 @@ abstract class ObjectsController extends ActionController $this ->addObjectsTabs() ->setAutorefreshInterval(10) - ->addTitle($this->translate(ucfirst(strtolower($type)) . 's')) + ->addTitle($this->translate(ucfirst($this->getPluralType()))) ->actions(new ObjectsActionBar($type, $this->url())); if ($type === 'command' && $this->params->get('type') === 'external_object') { @@ -139,7 +140,6 @@ abstract class ObjectsController extends ActionController return; } $type = $this->getType(); - $shortType = IcingaObject::createByType($type)->getShortTableName(); $this ->assertPermission('director/admin') @@ -294,8 +294,8 @@ abstract class ObjectsController extends ActionController { // Strip final 's' and upcase an eventual 'group' return preg_replace( - array('/group$/', '/period$/', '/argument$/', '/apiuser$/'), - array('Group', 'Period', 'Argument', 'ApiUser'), + array('/group$/', '/period$/', '/argument$/', '/apiuser$/', '/dependencie$/'), + array('Group', 'Period', 'Argument', 'ApiUser', 'dependency'), str_replace( 'template', '', @@ -306,11 +306,11 @@ abstract class ObjectsController extends ActionController protected function getPluralType() { - return $this->getType() . 's'; + return preg_replace('/cys$/', 'cies', $this->getType() . 's'); } protected function getPluralBaseType() { - return $this->getBaseType() . 's'; + return preg_replace('/cys$/', 'cies', $this->getBaseType() . 's'); } } diff --git a/library/Director/Web/Controller/TemplateController.php b/library/Director/Web/Controller/TemplateController.php index 98607792..c675f32a 100644 --- a/library/Director/Web/Controller/TemplateController.php +++ b/library/Director/Web/Controller/TemplateController.php @@ -177,7 +177,11 @@ abstract class TemplateController extends CompatController protected function getPluralType() { - return $this->template()->getShortTableName() . 's'; + return preg_replace( + '/cys$/', + 'cies', + $this->template()->getShortTableName() . 's' + ); } protected function getTranslatedType() diff --git a/library/Director/Web/Form/DirectorObjectForm.php b/library/Director/Web/Form/DirectorObjectForm.php index 3461624b..72a63d0a 100644 --- a/library/Director/Web/Form/DirectorObjectForm.php +++ b/library/Director/Web/Form/DirectorObjectForm.php @@ -523,6 +523,9 @@ abstract class DirectorObjectForm extends DirectorForm 'email', 'pager', 'enable_notifications', + 'disable_checks', //Dependencies + 'disable_notifications', + 'ignore_soft_states', 'apply_for', 'create_live', 'disabled', @@ -1508,28 +1511,28 @@ abstract class DirectorObjectForm extends DirectorForm return $this; } - protected function addEventFilterElements() + protected function addEventFilterElements($elements = array('states','types')) { - $this->addElement('extensibleSet', 'states', array( - 'label' => $this->translate('States'), - 'multiOptions' => $this->optionallyAddFromEnum($this->enumStates()), - 'description' => $this->translate( - 'The host/service states you want to get notifications for' - ), - )); + if (in_array('states', $elements)) { + $this->addElement('extensibleSet', 'states', array( + 'label' => $this->translate('States'), + 'multiOptions' => $this->optionallyAddFromEnum($this->enumStates()), + 'description' => $this->translate( + 'The host/service states you want to get notifications for' + ), + )); + } - $this->addElement('extensibleSet', 'types', array( - 'label' => $this->translate('Transition types'), - 'multiOptions' => $this->optionallyAddFromEnum($this->enumTypes()), - 'description' => $this->translate( - 'The state transition types you want to get notifications for' - ), - )); + if (in_array('types', $elements)) { + $this->addElement('extensibleSet', 'types', array( + 'label' => $this->translate('Transition types'), + 'multiOptions' => $this->optionallyAddFromEnum($this->enumTypes()), + 'description' => $this->translate( + 'The state transition types you want to get notifications for' + ), + )); + } - $elements = array( - 'states', - 'types', - ); $this->addDisplayGroup($elements, 'event_filters', array( 'decorators' => array( 'FormElements', diff --git a/library/Director/Web/Tabs/ObjectsTabs.php b/library/Director/Web/Tabs/ObjectsTabs.php index adef4a54..ea3a3f09 100644 --- a/library/Director/Web/Tabs/ObjectsTabs.php +++ b/library/Director/Web/Tabs/ObjectsTabs.php @@ -18,28 +18,29 @@ class ObjectsTabs extends Tabs $object = IcingaObject::createByType(substr($type, 0, -5)); } - // TODO: plural? - if ($auth->hasPermission("director/${type}s")) { + $plType = strtolower(preg_replace('/cys$/', 'cies', $type . 's')); + if ($auth->hasPermission("director/${plType}")) { $this->add('index', array( - 'url' => sprintf('director/%ss', strtolower($type)), - 'label' => $this->translate(ucfirst($type) . 's'), + 'url' => sprintf('director/%s', $plType), + 'label' => $this->translate(ucfirst($plType)), )); } if ($object->getShortTableName() === 'command') { $this->add('external', array( - 'url' => sprintf('director/%ss', strtolower($type)), + 'url' => sprintf('director/%s', strtolower($plType)), 'urlParams' => ['type' => 'external_object'], 'label' => $this->translate('External'), )); } if ($auth->hasPermission('director/admin') || ( - $object->getShortTableName() === 'notification' && $auth->hasPermission('director/notifications') - )) { + $object->getShortTableName() === 'notification' + && $auth->hasPermission('director/notifications') + )) { if ($object->supportsApplyRules()) { $this->add('applyrules', array( - 'url' => sprintf('director/%ss/applyrules', $type), + 'url' => sprintf('director/%s/applyrules', $plType), 'label' => $this->translate('Apply') )); } @@ -48,7 +49,7 @@ class ObjectsTabs extends Tabs if ($auth->hasPermission('director/admin') && $type !== 'zone') { if ($object->supportsImports()) { $this->add('templates', array( - 'url' => sprintf('director/%ss/templates', strtolower($type)), + 'url' => sprintf('director/%s/templates', $plType), 'label' => $this->translate('Templates'), )); } @@ -71,7 +72,7 @@ class ObjectsTabs extends Tabs } if ($object->supportsSets() && $auth->hasPermission("director/${type}_sets")) { $this->add('sets', array( - 'url' => sprintf('director/%ss/sets', $type), + 'url' => sprintf('director/%s/sets', $plType), 'label' => $this->translate('Sets') )); } diff --git a/public/js/module.js b/public/js/module.js index 2057e88f..c8093778 100644 --- a/public/js/module.js +++ b/public/js/module.js @@ -275,7 +275,8 @@ { $suggestions.load(this.module.icinga.config.baseUrl + '/director/suggest', { value: $el.val(), - context: $el.data('suggestion-context') + context: $el.data('suggestion-context'), + for_host: $el.data('suggestion-for-host') }, function (responseText, textStatus, jqXHR) { var $li = $suggestions.find('li'); if ($li.length) { diff --git a/schema/mysql-migrations/upgrade_144.sql b/schema/mysql-migrations/upgrade_144.sql new file mode 100644 index 00000000..fff6f8f8 --- /dev/null +++ b/schema/mysql-migrations/upgrade_144.sql @@ -0,0 +1,91 @@ +CREATE TABLE icinga_dependency ( + id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL, + object_name VARCHAR(255) DEFAULT NULL, + object_type ENUM('object', 'template', 'apply') NOT NULL, + disabled ENUM('y', 'n') NOT NULL DEFAULT 'n', + apply_to ENUM('host', 'service') DEFAULT NULL, + parent_host_id INT(10) UNSIGNED DEFAULT NULL, + parent_service_id INT(10) UNSIGNED DEFAULT NULL, + child_host_id INT(10) UNSIGNED DEFAULT NULL, + child_service_id INT(10) UNSIGNED DEFAULT NULL, + disable_checks ENUM('y', 'n') DEFAULT NULL, + disable_notifications ENUM('y', 'n') DEFAULT NULL, + ignore_soft_states ENUM('y', 'n') DEFAULT NULL, + period_id INT(10) UNSIGNED DEFAULT NULL, + zone_id INT(10) UNSIGNED DEFAULT NULL, + assign_filter TEXT DEFAULT NULL, + parent_service_by_name VARCHAR(255) DEFAULT NULL, + PRIMARY KEY (id), + CONSTRAINT icinga_dependency_parent_host + FOREIGN KEY parent_host (parent_host_id) + REFERENCES icinga_host (id) + ON DELETE RESTRICT + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_parent_service + FOREIGN KEY parent_service (parent_service_id) + REFERENCES icinga_service (id) + ON DELETE RESTRICT + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_child_host + FOREIGN KEY child_host (child_host_id) + REFERENCES icinga_host (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_child_service + FOREIGN KEY child_service (child_service_id) + REFERENCES icinga_service (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_period + FOREIGN KEY period (period_id) + REFERENCES icinga_timeperiod (id) + ON DELETE RESTRICT + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_zone + FOREIGN KEY zone (zone_id) + REFERENCES icinga_zone (id) + ON DELETE RESTRICT + ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE icinga_dependency_inheritance ( + dependency_id INT(10) UNSIGNED NOT NULL, + parent_dependency_id INT(10) UNSIGNED NOT NULL, + weight MEDIUMINT UNSIGNED DEFAULT NULL, + PRIMARY KEY (dependency_id, parent_dependency_id), + UNIQUE KEY unique_order (dependency_id, weight), + CONSTRAINT icinga_dependency_inheritance_dependency + FOREIGN KEY dependency (dependency_id) + REFERENCES icinga_dependency (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_inheritance_parent_dependency + FOREIGN KEY parent_dependency (parent_dependency_id) + REFERENCES icinga_dependency (id) + ON DELETE RESTRICT + ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE icinga_dependency_states_set ( + dependency_id INT(10) UNSIGNED NOT NULL, + property ENUM( + 'OK', + 'Warning', + 'Critical', + 'Unknown', + 'Up', + 'Down' + ) NOT NULL, + merge_behaviour ENUM('override', 'extend', 'blacklist') NOT NULL DEFAULT 'override' + COMMENT 'override: = [], extend: += [], blacklist: -= []', + PRIMARY KEY (dependency_id, property, merge_behaviour), + CONSTRAINT icinga_dependency_states_set_dependency + FOREIGN KEY icinga_dependency (dependency_id) + REFERENCES icinga_dependency (id) + ON DELETE CASCADE + ON UPDATE CASCADE +) ENGINE=InnoDB; + +INSERT INTO director_schema_migration + (schema_version, migration_time) + VALUES (144, NOW()); diff --git a/schema/mysql.sql b/schema/mysql.sql index 5f0887db..b8b1dfd7 100644 --- a/schema/mysql.sql +++ b/schema/mysql.sql @@ -1575,6 +1575,94 @@ CREATE TABLE icinga_user_resolved_var ( ON UPDATE RESTRICT ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE icinga_dependency ( + id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL, + object_name VARCHAR(255) DEFAULT NULL, + object_type ENUM('object', 'template', 'apply') NOT NULL, + disabled ENUM('y', 'n') NOT NULL DEFAULT 'n', + apply_to ENUM('host', 'service') DEFAULT NULL, + parent_host_id INT(10) UNSIGNED DEFAULT NULL, + parent_service_id INT(10) UNSIGNED DEFAULT NULL, + child_host_id INT(10) UNSIGNED DEFAULT NULL, + child_service_id INT(10) UNSIGNED DEFAULT NULL, + disable_checks ENUM('y', 'n') DEFAULT NULL, + disable_notifications ENUM('y', 'n') DEFAULT NULL, + ignore_soft_states ENUM('y', 'n') DEFAULT NULL, + period_id INT(10) UNSIGNED DEFAULT NULL, + zone_id INT(10) UNSIGNED DEFAULT NULL, + assign_filter TEXT DEFAULT NULL, + parent_service_by_name VARCHAR(255) DEFAULT NULL, + PRIMARY KEY (id), + CONSTRAINT icinga_dependency_parent_host + FOREIGN KEY parent_host (parent_host_id) + REFERENCES icinga_host (id) + ON DELETE RESTRICT + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_parent_service + FOREIGN KEY parent_service (parent_service_id) + REFERENCES icinga_service (id) + ON DELETE RESTRICT + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_child_host + FOREIGN KEY child_host (child_host_id) + REFERENCES icinga_host (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_child_service + FOREIGN KEY child_service (child_service_id) + REFERENCES icinga_service (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_period + FOREIGN KEY period (period_id) + REFERENCES icinga_timeperiod (id) + ON DELETE RESTRICT + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_zone + FOREIGN KEY zone (zone_id) + REFERENCES icinga_zone (id) + ON DELETE RESTRICT + ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE icinga_dependency_inheritance ( + dependency_id INT(10) UNSIGNED NOT NULL, + parent_dependency_id INT(10) UNSIGNED NOT NULL, + weight MEDIUMINT UNSIGNED DEFAULT NULL, + PRIMARY KEY (dependency_id, parent_dependency_id), + UNIQUE KEY unique_order (dependency_id, weight), + CONSTRAINT icinga_dependency_inheritance_dependency + FOREIGN KEY dependency (dependency_id) + REFERENCES icinga_dependency (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_inheritance_parent_dependency + FOREIGN KEY parent_dependency (parent_dependency_id) + REFERENCES icinga_dependency (id) + ON DELETE RESTRICT + ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE icinga_dependency_states_set ( + dependency_id INT(10) UNSIGNED NOT NULL, + property ENUM( + 'OK', + 'Warning', + 'Critical', + 'Unknown', + 'Up', + 'Down' + ) NOT NULL, + merge_behaviour ENUM('override', 'extend', 'blacklist') NOT NULL DEFAULT 'override' + COMMENT 'override: = [], extend: += [], blacklist: -= []', + PRIMARY KEY (dependency_id, property, merge_behaviour), + CONSTRAINT icinga_dependency_states_set_dependency + FOREIGN KEY icinga_dependency (dependency_id) + REFERENCES icinga_dependency (id) + ON DELETE CASCADE + ON UPDATE CASCADE +) ENGINE=InnoDB; + INSERT INTO director_schema_migration (schema_version, migration_time) - VALUES (143, NOW()); + VALUES (144, NOW()); diff --git a/schema/pgsql-migrations/upgrade_144.sql b/schema/pgsql-migrations/upgrade_144.sql new file mode 100644 index 00000000..4516f5e9 --- /dev/null +++ b/schema/pgsql-migrations/upgrade_144.sql @@ -0,0 +1,99 @@ +CREATE TABLE icinga_dependency ( + id serial, + object_name character varying(255) NOT NULL, + object_type enum_object_type_all NOT NULL, + disabled enum_boolean DEFAULT 'n', + apply_to enum_host_service NULL DEFAULT NULL, + parent_host_id integer DEFAULT NULL, + parent_service_id integer DEFAULT NULL, + child_host_id integer DEFAULT NULL, + child_service_id integer DEFAULT NULL, + disable_checks enum_boolean DEFAULT NULL, + disable_notifications enum_boolean DEFAULT NULL, + ignore_soft_states enum_boolean DEFAULT NULL, + period_id integer DEFAULT NULL, + zone_id integer DEFAULT NULL, + assign_filter text DEFAULT NULL, + parent_service_by_name character varying(255), + PRIMARY KEY (id), + CONSTRAINT icinga_dependency_parent_host + FOREIGN KEY (parent_host_id) + REFERENCES icinga_host (id) + ON DELETE RESTRICT + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_parent_service + FOREIGN KEY (parent_service_id) + REFERENCES icinga_service (id) + ON DELETE RESTRICT + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_child_host + FOREIGN KEY (child_host_id) + REFERENCES icinga_host (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_child_service + FOREIGN KEY (child_service_id) + REFERENCES icinga_service (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_period + FOREIGN KEY (period_id) + REFERENCES icinga_timeperiod (id) + ON DELETE RESTRICT + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_zone + FOREIGN KEY (zone_id) + REFERENCES icinga_zone (id) + ON DELETE RESTRICT + ON UPDATE CASCADE +); + +CREATE INDEX dependency_parent_host ON icinga_dependency (parent_host_id); +CREATE INDEX dependency_parent_service ON icinga_dependency (parent_service_id); +CREATE INDEX dependency_child_host ON icinga_dependency (child_host_id); +CREATE INDEX dependency_child_service ON icinga_dependency (child_service_id); +CREATE INDEX dependency_period ON icinga_dependency (period_id); +CREATE INDEX dependency_zone ON icinga_dependency (zone_id); + + +CREATE TABLE icinga_dependency_inheritance ( + dependency_id integer NOT NULL, + parent_dependency_id integer NOT NULL, + weight integer DEFAULT NULL, + PRIMARY KEY (dependency_id, parent_dependency_id), + CONSTRAINT icinga_dependency_inheritance_dependency + FOREIGN KEY (dependency_id) + REFERENCES icinga_dependency (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_inheritance_parent_dependency + FOREIGN KEY (parent_dependency_id) + REFERENCES icinga_dependency (id) + ON DELETE RESTRICT + ON UPDATE CASCADE +); + +CREATE UNIQUE INDEX dependency_inheritance_unique_order ON icinga_dependency_inheritance (dependency_id, weight); +CREATE INDEX dependency_inheritance_dependency ON icinga_dependency_inheritance (dependency_id); +CREATE INDEX dependency_inheritance_dependency_parent ON icinga_dependency_inheritance (parent_dependency_id); + + +CREATE TABLE icinga_dependency_states_set ( + dependency_id integer NOT NULL, + property enum_state_name NOT NULL, + merge_behaviour enum_set_merge_behaviour NOT NULL DEFAULT 'override', + PRIMARY KEY (dependency_id, property, merge_behaviour), + CONSTRAINT icinga_dependency_states_set_dependency + FOREIGN KEY (dependency_id) + REFERENCES icinga_dependency (id) + ON DELETE CASCADE + ON UPDATE CASCADE +); + +CREATE INDEX dependency_states_set_dependency ON icinga_dependency_states_set (dependency_id); +COMMENT ON COLUMN icinga_dependency_states_set.merge_behaviour IS 'override: = [], extend: += [], blacklist: -= []'; + + +INSERT INTO director_schema_migration + (schema_version, migration_time) + VALUES (144, NOW()); diff --git a/schema/pgsql.sql b/schema/pgsql.sql index f227734b..5eb2233a 100644 --- a/schema/pgsql.sql +++ b/schema/pgsql.sql @@ -1855,6 +1855,102 @@ CREATE INDEX user_resolved_var_user_id ON icinga_user_resolved_var (user_id); CREATE INDEX user_resolved_var_schecksum ON icinga_user_resolved_var (checksum); +CREATE TABLE icinga_dependency ( + id serial, + object_name character varying(255) NOT NULL, + object_type enum_object_type_all NOT NULL, + disabled enum_boolean DEFAULT 'n', + apply_to enum_host_service NULL DEFAULT NULL, + parent_host_id integer DEFAULT NULL, + parent_service_id integer DEFAULT NULL, + child_host_id integer DEFAULT NULL, + child_service_id integer DEFAULT NULL, + disable_checks enum_boolean DEFAULT NULL, + disable_notifications enum_boolean DEFAULT NULL, + ignore_soft_states enum_boolean DEFAULT NULL, + period_id integer DEFAULT NULL, + zone_id integer DEFAULT NULL, + assign_filter text DEFAULT NULL, + parent_service_by_name character varying(255), + PRIMARY KEY (id), + CONSTRAINT icinga_dependency_parent_host + FOREIGN KEY (parent_host_id) + REFERENCES icinga_host (id) + ON DELETE RESTRICT + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_parent_service + FOREIGN KEY (parent_service_id) + REFERENCES icinga_service (id) + ON DELETE RESTRICT + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_child_host + FOREIGN KEY (child_host_id) + REFERENCES icinga_host (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_child_service + FOREIGN KEY (child_service_id) + REFERENCES icinga_service (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_period + FOREIGN KEY period (period_id) + REFERENCES icinga_timeperiod (id) + ON DELETE RESTRICT + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_zone + FOREIGN KEY zone (zone_id) + REFERENCES icinga_zone (id) + ON DELETE RESTRICT + ON UPDATE CASCADE +); + +CREATE INDEX dependency_parent_host ON icinga_dependency (parent_host_id); +CREATE INDEX dependency_parent_service ON icinga_dependency (parent_service_id); +CREATE INDEX dependency_child_host ON icinga_dependency (child_host_id); +CREATE INDEX dependency_child_service ON icinga_dependency (child_service_id); +CREATE INDEX dependency_period ON icinga_dependency (period_id); +CREATE INDEX dependency_zone ON icinga_dependency (zone_id); + + +CREATE TABLE icinga_dependency_inheritance ( + dependency_id integer NOT NULL, + parent_dependency_id integer NOT NULL, + weight integer DEFAULT NULL, + PRIMARY KEY (dependency_id, parent_dependency_id), + CONSTRAINT icinga_dependency_inheritance_dependency + FOREIGN KEY (dependency_id) + REFERENCES icinga_dependency (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT icinga_dependency_inheritance_parent_dependency + FOREIGN KEY (parent_dependency_id) + REFERENCES icinga_dependency (id) + ON DELETE RESTRICT + ON UPDATE CASCADE +); + +CREATE UNIQUE INDEX dependency_inheritance_unique_order ON icinga_dependency_inheritance (dependency_id, weight); +CREATE INDEX dependency_inheritance_dependency ON icinga_dependency_inheritance (dependency_id); +CREATE INDEX dependency_inheritance_dependency_parent ON icinga_dependency_inheritance (parent_dependency_id); + + +CREATE TABLE icinga_dependency_states_set ( + icinga_dependency integer NOT NULL, + property enum_state_name NOT NULL, + merge_behaviour enum_set_merge_behaviour NOT NULL DEFAULT 'override', + PRIMARY KEY (dependency_id, property, merge_behaviour), + CONSTRAINT icinga_dependency_states_set_dependency + FOREIGN KEY (dependency_id) + REFERENCES icinga_dependency (id) + ON DELETE CASCADE + ON UPDATE CASCADE +); + +CREATE INDEX dependency_states_set_dependency ON icinga_dependency_states_set (user_id); +COMMENT ON COLUMN icinga_dependency_states_set.merge_behaviour IS 'override: = [], extend: += [], blacklist: -= []'; + + INSERT INTO director_schema_migration (schema_version, migration_time) - VALUES (143, NOW()); + VALUES (144, NOW());