diff --git a/application/clicommands/DependencyCommand.php b/application/clicommands/DependencyCommand.php new file mode 100644 index 000000000..ff5cbdc6c --- /dev/null +++ b/application/clicommands/DependencyCommand.php @@ -0,0 +1,15 @@ +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 addAction() + { + parent::addAction(); + + if ($this->apply) { + $this->view->title = sprintf( + $this->translate('Apply "%s"'), + $this->apply->object_name + ); + } + } + + public function loadForm($name) + { + $form = parent::loadForm($name); + return $form; + } + + protected function beforeHandlingAddRequest($form) + { + if ($this->apply) { + $form->createApplyRuleFor($this->apply); + } + } + + + +} diff --git a/application/forms/IcingaDependencyForm.php b/application/forms/IcingaDependencyForm.php new file mode 100644 index 000000000..0717f9651 --- /dev/null +++ b/application/forms/IcingaDependencyForm.php @@ -0,0 +1,338 @@ +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', array( + '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', array( + 'label' => $this->translate('Apply to'), + 'description' => $this->translate( + 'Whether this dependency should affect hosts or services' + ), + 'required' => true, + 'class' => 'autosubmit', + 'multiOptions' => $this->optionalEnum( + array( + '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') { + $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', + array( + 'label' => $this->translate('Disable Checks'), + 'description' => $this->translate( + 'Whether to disable checks when this dependency fails. Defaults to false.' + ) + ), + null + ); + + $this->addBoolean( + 'disable_notifications', + array( + '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', + array( + '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() + { + $hosts = $this->enumAllowedHosts(); + + if (!empty($hosts)) { + $this->addElement( + 'select', + 'parent_host_id', + array( + 'label' => $this->translate('Parent Host'), + 'description' => $this->translate( + 'The parent host.' + ), + 'multiOptions' => $this->optionalEnum($hosts), + 'sorted' => true, + 'class' => "autosubmit", + 'order' => 10, + ) + ); + } + + $parentHostId = $this->getSentOrObjectValue('parent_host_id'); + $parentServices = $this->enumAllowedServices($parentHostId); + + if (!empty($parentServices)) { + $this->addElement( + 'select', + 'parent_service_id', + array( + 'label' => $this->translate('Parent Service'), + 'description' => $this->translate( + 'Optional. The parent service. If omitted this dependency' + . ' object is treated as host dependency.' + ), + 'multiOptions' => $this->optionalEnum($parentServices), + 'order' => 20, + ) + ); + + } + + // If configuring Object, allow selection of child host and/or service, otherwise apply rules will determine child object. + if ($this->isObject()) { + + if (!empty($hosts) && $this->isObject()) { + $this->addElement( + 'select', + 'child_host_id', + array( + 'label' => $this->translate('Child Host'), + 'description' => $this->translate( + 'The child host.' + ), + 'multiOptions' => $this->optionalEnum($hosts), + 'sorted' => true, + 'class' => "autosubmit", + 'order' => 30, + ) + ); + } + + $childHostId = $this->getSentOrObjectValue('child_host_id'); + $childServices = $this->enumAllowedServices($childHostId); + + if (! empty($childServices) && ($childHostId != null)) { + + $this->addElement( + 'select', + 'child_service_id', + array( + 'label' => $this->translate('Child Service'), + 'description' => $this->translate( + 'Optional. The child service. If omitted this dependency' + . ' object is treated as host dependency.' + ), + 'multiOptions' => $this->optionalEnum($childServices), + 'sorted' => true, + 'order' => 40, + ) + ); + } + } + + $elements = array('parent_host_id','child_host_id','parent_service_id','child_service_id'); + $this->addDisplayGroup($elements, 'related_objects', array( + 'decorators' => array( + 'FormElements', + array('HtmlTag', array('tag' => 'dl')), + 'Fieldset', + ), + 'order' => 25, + 'legend' => $this->translate('Related Objects') + )); + + + return $this; + } + + protected function enumAllowedHosts() + { + $obj = $this->db->enumIcingaObjects('host'); + if (empty($obj)) { + return array(); + } + + return $obj; + } + + protected function enumAllowedServices($host_id = null, &$host_templates_done = null) + { + // Returns service enumeration. If host_id is given, services are limited + // to services on that host, or those inherited via host template, plus + // all service apply rules (no attempt is made to further limit apply rules) + // If host_id is null, only service apply rules are returned + + $r_services = array(); + $apply_services = array(); + $host_template_services = array(); + $host_services = array(); + + if ( $host_templates_done === null) { //don't redo applied service enumeration on recursive calls + $apply_services = $this->enumApplyServices(); + } + + if ($host_id != null) { + $host_services = $this->db->enumIcingaObjects('service', array('host_id = ?' => $host_id)); + asort($host_services); + + //services for applicable templates + $host_templates_done = array(); + $tmp_host = IcingaHost::loadWithAutoIncId($host_id, $this->db); + $host_templates = $tmp_host->imports()->getObjects(); + + foreach ($host_templates as $host_template => $template_obj) { + if (in_array($template_obj->id, $host_templates_done)) continue; + + $host_templates_done[] = $template_obj->id; + $get_template_services = $this->enumAllowedServices($template_obj->id, $host_templates_done); //recursively get services for this host's template tree + // indicate host template name in 'inherited' services + foreach ($get_template_services as $id => &$label) { + if (!preg_match("/via host template/", $label)) $get_template_services[$id]= $label.': via host template '.$host_template; + } + $host_template_services+=$get_template_services; + } + asort($host_template_services); + } + + $r_services += $host_services += $host_template_services += $apply_services; + + return $r_services; + } + + protected function enumApplyServices() + { + $db = $this->db->getDbAdapter(); + $query = $db->select()->from( + array('s' => 'icinga_service'), + array( + 's.id', + 's.object_name', + 's.assign_filter' + ) + )->where('s.object_type = ?', 'apply'); + $list = $db->fetchAll($query); + $services = array(); + foreach ($list as $row) { + $services[$row->id] = sprintf( + '%s where %s', + $row->object_name, + Filter::fromQueryString($row->assign_filter) + ); + } + + asort($services); + return $services; + } + + public function createApplyRuleFor(IcingaDependency $dependency) + { + $object = $this->object(); + $object->imports = $dependency->object_name; + $object->object_type = 'apply'; + $object->object_name = $dependency->object_name; + return $this; + } +} diff --git a/application/tables/IcingaDependencyTable.php b/application/tables/IcingaDependencyTable.php new file mode 100644 index 000000000..3c342f77d --- /dev/null +++ b/application/tables/IcingaDependencyTable.php @@ -0,0 +1,105 @@ + 'd.id', + 'object_type' => 'd.object_type', + 'dependency' => 'd.object_name', + ); + } + + protected function listTableClasses() + { + return array_merge(array('assignment-table'), parent::listTableClasses()); + } + + protected function getActionUrl($row) + { + return $this->url('director/dependency', array('id' => $row->id)); + } + + public function getTitles() + { + $view = $this->view(); + return array( + 'dependency' => $view->translate('Dependency'), + ); + } + + protected function renderRow($row) + { + $v = $this->view(); + $extra = $this->appliedOnes($row->id); + $htm = " getRowClassesString($row) . ">\n"; + $htm .= '' . $v->qlink($row->dependency, $this->getActionUrl($row)); + if (empty($extra)) { + $htm .= ' ' . $v->qlink( + 'Create apply-rule', + 'director/dependency/add', + array('apply' => $row->dependency, 'type' => 'apply'), + array('class' => 'icon-plus') + ); + + } else { + $htm .= '. Related apply rules: '; + $htm .= $v->qlink( + 'Add more', + 'director/dependency/add', + array('apply' => $row->dependency), + array('class' => 'icon-plus') + ); + } + $htm .= ''; + return $htm . " \n"; + } + + protected function appliedOnes($id) + { + $db = $this->db(); + $query = $db->select()->from( + array('s' => 'icinga_dependency'), + array( + 'id' => 's.id', + 'objectname' => 's.object_name', + ) + )->join( + array('i' => 'icinga_dependency_inheritance'), + 'i.dependency_id = s.id', + array() + )->where('i.parent_dependency_id = ?', $id) + ->where('s.object_type = ?', 'apply'); + + + return $db->fetchPairs($query); + } + + public function getUnfilteredQuery() + { + return $this->db()->select()->from( + array('d' => 'icinga_dependency'), + array() + ); + } + + public function getBaseQuery() + { + return $this->getUnfilteredQuery()->order('d.object_name'); + } +} diff --git a/application/tables/IcingaDependencyTemplateTable.php b/application/tables/IcingaDependencyTemplateTable.php new file mode 100644 index 000000000..60038b695 --- /dev/null +++ b/application/tables/IcingaDependencyTemplateTable.php @@ -0,0 +1,13 @@ +getUnfilteredQuery()->where('d.object_type = ?', 'template'); + } +} diff --git a/library/Director/Db.php b/library/Director/Db.php index adcc1c12c..afd2a2964 100644 --- a/library/Director/Db.php +++ b/library/Director/Db.php @@ -390,6 +390,7 @@ public function getObjectSummary() 'apiuser', 'endpoint', 'zone', + 'dependency', ); $queries = array(); diff --git a/library/Director/IcingaConfig/IcingaConfig.php b/library/Director/IcingaConfig/IcingaConfig.php index 1d1ffb8fa..3f515d98a 100644 --- a/library/Director/IcingaConfig/IcingaConfig.php +++ b/library/Director/IcingaConfig/IcingaConfig.php @@ -479,6 +479,7 @@ protected function generateFromDb() ->createFileFromDb('userGroup') ->createFileFromDb('user') ->createFileFromDb('notification') + ->createFileFromDb('dependency') ; if (! $this->isLegacy()) { @@ -720,7 +721,8 @@ protected function typeWantsMasterZone($type) 'user', 'userGroup', 'timePeriod', - 'notification' + 'notification', + 'dependency' ); return in_array($type, $types); diff --git a/library/Director/Objects/IcingaObject.php b/library/Director/Objects/IcingaObject.php index 1341eeac5..f9e131b3d 100644 --- a/library/Director/Objects/IcingaObject.php +++ b/library/Director/Objects/IcingaObject.php @@ -2543,7 +2543,8 @@ public function getUrlParams() 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/Controller/ObjectsController.php b/library/Director/Web/Controller/ObjectsController.php index d8ad8c215..ea1171683 100644 --- a/library/Director/Web/Controller/ObjectsController.php +++ b/library/Director/Web/Controller/ObjectsController.php @@ -33,15 +33,18 @@ public function init() $tabs = $this->getTabs(); $type = $this->getType(); + $ptype = preg_replace('/cys/', 'cies', $type . 's'); + $ltype = strtolower($type); + $pltype = strtolower($ptype); if (in_array(ucfirst($type), $this->globalTypes)) { - $ltype = strtolower($type); foreach ($this->globalTypes as $tabType) { $ltabType = strtolower($tabType); + $pltabType = preg_replace('/cys/','cies', $ltabType . 's'); $tabs->add($ltabType, array( - 'label' => $this->translate(ucfirst($ltabType) . 's'), - 'url' => sprintf('director/%ss', $ltabType) + 'label' => $this->translate(ucfirst($pltabType)), + 'url' => sprintf('director/%s', $pltabType) )); } $tabs->activate($ltype); @@ -62,14 +65,14 @@ public function init() } $tabs->add('objects', array( - 'url' => sprintf('director/%ss', strtolower($type)), - 'label' => $this->translate(ucfirst($type) . 's'), + 'url' => sprintf('director/%s', strtolower($ptype)), + 'label' => $this->translate(ucfirst($ptype)), )); if ($this->hasPermission('director/admin')) { if ($object->supportsImports()) { $tabs->add('templates', array( - 'url' => sprintf('director/%ss/templates', strtolower($type)), + 'url' => sprintf('director/%s/templates', $pltype), 'label' => $this->translate('Templates'), )); } @@ -83,13 +86,13 @@ public function init() if ($baseObject->supportsSets()) { $tabs->add('sets', array( - 'url' => sprintf('director/%ss/sets', $type), + 'url' => sprintf('director/%s/sets', $pltype), 'label' => $this->translate('Sets') )); } $tabs->add('tree', array( - 'url' => sprintf('director/%ss/templatetree', $type), + 'url' => sprintf('director/%s/templatetree', $pltype), 'label' => $this->translate('Tree'), )); } @@ -103,6 +106,8 @@ public function indexAction() $type = $this->getType(); $ltype = strtolower($type); + $ptype = preg_replace('/cys$/','cies',$type.'s'); + $pltype = strtolower($ptype); /** @var IcingaObject $dummy */ $dummy = $this->dummyObject(); @@ -134,7 +139,7 @@ public function indexAction() $table->enforceFilter(Filter::expression('object_type', '=', 'template')); } else { $addParams = array('type' => 'object'); - $title = $this->translate('Icinga ' . ucfirst($ltype) . 's'); + $title = $this->translate('Icinga ' . ucfirst($pltype)); if ($dummy->supportsImports() && array_key_exists('object_type', $table->getColumns()) && ! in_array(ucfirst($type), $this->globalTypes) @@ -147,7 +152,7 @@ public function indexAction() $this->view->addLink = $this->view->qlink( $this->translate('Add'), - 'director/' . $ltype .'/add', + 'director/' . $ltype . '/add', $addParams, array('class' => 'icon-plus') ); @@ -268,8 +273,8 @@ protected function getType() { // 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', '', diff --git a/library/Director/Web/Form/DirectorObjectForm.php b/library/Director/Web/Form/DirectorObjectForm.php index f812dd0e8..881b3bd1d 100644 --- a/library/Director/Web/Form/DirectorObjectForm.php +++ b/library/Director/Web/Form/DirectorObjectForm.php @@ -394,6 +394,9 @@ protected function groupMainProperties() 'apply_for', 'create_live', 'disabled', + 'disable_checks', //Dependencies + 'disable_notifications', + 'ignore_soft_states', ); $this->addDisplayGroup($elements, 'object_definition', array( @@ -1320,28 +1323,28 @@ protected function addFilterElement($name, $properties) 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',