Implement Icinga Dependency Configuration.

This commit is contained in:
Marc DeTrano 2016-09-16 16:25:11 -06:00 committed by Thomas Gelf
parent 8a6480e40e
commit 6bea1eff41
23 changed files with 1226 additions and 38 deletions

View File

@ -0,0 +1,15 @@
<?php
namespace Icinga\Module\Director\Clicommands;
use Icinga\Module\Director\Cli\ObjectCommand;
/**
* Manage Icinga Dependencies
*
* Use this command to show, create, modify or delete Icinga Dependency
* objects
*/
class DependencyCommand extends ObjectCommand
{
}

View File

@ -0,0 +1,9 @@
<?php
namespace Icinga\Module\Director\Controllers;
use Icinga\Module\Director\Web\Controller\ObjectsController;
class DependenciesController extends ObjectsController
{
}

View File

@ -0,0 +1,60 @@
<?php
namespace Icinga\Module\Director\Controllers;
use Icinga\Module\Director\Web\Controller\ObjectController;
use Icinga\Module\Director\Objects\IcingaDependency;
class DependencyController extends ObjectController
{
protected $apply;
protected function beforeTabs()
{
}
public function init()
{
parent::init();
if ($apply = $this->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)
{
if ($this->apply) {
$form->createApplyRuleFor($this->apply);
}
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Icinga\Module\Director\Controllers;
use Icinga\Module\Director\Objects\IcingaDependency;
use Icinga\Module\Director\Web\Controller\TemplateController;
class DependencytemplateController extends TemplateController
{
protected function requireTemplate()
{
return IcingaDependency::load([
'object_name' => $this->params->get('name')
], $this->db());
}
}

View File

@ -6,6 +6,7 @@ use Icinga\Module\Director\Objects\IcingaService;
use Icinga\Module\Director\Web\Controller\ActionController; use Icinga\Module\Director\Web\Controller\ActionController;
use Icinga\Data\Filter\Filter; use Icinga\Data\Filter\Filter;
use ipl\Html\Util; use ipl\Html\Util;
use Icinga\Module\Director\Objects\HostApplyMatches;
class SuggestController extends ActionController class SuggestController extends ActionController
{ {
@ -91,6 +92,48 @@ class SuggestController extends ActionController
return $db->fetchCol($query); 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() protected function suggestHosttemplates()
{ {
$this->assertPermission('director/hosts'); $this->assertPermission('director/hosts');
@ -199,6 +242,17 @@ class SuggestController extends ActionController
return $res; 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) protected function highlight($val, $search)
{ {
$search = ($search); $search = ($search);
@ -209,4 +263,33 @@ class SuggestController extends ActionController
$val $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);
}
} }

View File

@ -0,0 +1,252 @@
<?php
namespace Icinga\Module\Director\Forms;
use Icinga\Module\Director\Web\Form\DirectorObjectForm;
use Icinga\Module\Director\Objects\IcingaHost;
use Icinga\Module\Director\Objects\IcingaService;
use Icinga\Module\Director\Objects\IcingaDependency;
class IcingaDependencyForm extends DirectorObjectForm
{
public function setup()
{
$this->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') {
// 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',
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()
{
$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,
'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.'
),
'class' => "autosubmit director-suggest",
'data-suggestion-context' => 'hostnames',
'order' => 30,
'value' => $this->getObject()->get('child_host')
)
);
$sent_child=$this->getSentOrObjectValue("child_host");
if (!empty($sent_child)) {
$this->addElement(
'text',
'child_service',
array(
'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",
'data-suggestion-context' => 'servicenames',
'data-suggestion-for-host' => $sent_child,
'order' => 40,
'value' => $this->getObject()->get('child_service')
)
);
}
}
$elements=array('parent_host','child_host','parent_service','child_service');
$this->addDisplayGroup($elements, 'related_objects', array(
'decorators' => array(
'FormElements',
array('HtmlTag', array('tag' => 'dl')),
'Fieldset',
),
'order' => 25,
'legend' => $this->translate('Related Objects')
));
return $this;
}
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;
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace Icinga\Module\Director\Tables;
use Icinga\Module\Director\Web\Table\IcingaObjectTable;
class IcingaDependencyTable extends IcingaObjectTable
{
protected $searchColumns = array(
'dependency',
);
public function getColumns()
{
return array(
'id' => '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 = " <tr" . $this->getRowClassesString($row) . ">\n";
$htm .= '<td>' . $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: <ul class="apply-rules">';
foreach ($extra as $id => $dependency) {
$htm .= '<li>'
. $v->qlink($dependency, 'director/dependency', array('id' => $id))
. '</li>';
}
$htm .= '</ul>';
$htm .= $v->qlink(
'Add more',
'director/dependency/add',
array('apply' => $row->dependency),
array('class' => 'icon-plus')
);
}
$htm .= '</td>';
return $htm . " </tr>\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');
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace Icinga\Module\Director\Tables;
use Icinga\Module\Director\Tables\IcingaDependencyTable;
class IcingaDependencyTemplateTable extends IcingaDependencyTable
{
public function getBaseQuery()
{
return $this->getUnfilteredQuery()->where('d.object_type = ?', 'template');
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace Icinga\Module\Director\Dashboard\Dashlet;
class DependencyObjectDashlet extends Dashlet
{
protected $icon = '';
protected $requiredStats = array('dependency');
public function getTitle()
{
return $this->translate('Dependencies.');
}
public function getSummary()
{
return $this->translate('Define object dependency relationships.')
. ' ' . parent::getSummary();
}
public function getUrl()
{
return 'director/dependencies';
}
}

View File

@ -9,6 +9,7 @@ class ObjectsDashboard extends Dashboard
'ServiceObject', 'ServiceObject',
'CommandObject', 'CommandObject',
// 'Notifications', // 'Notifications',
'DependencyObject',
); );
public function getTitle() public function getTitle()

View File

@ -389,6 +389,7 @@ class Db extends DbConnection
'apiuser', 'apiuser',
'endpoint', 'endpoint',
'zone', 'zone',
'dependency',
); );
$queries = array(); $queries = array();

View File

@ -477,6 +477,7 @@ class IcingaConfig
->createFileFromDb('userGroup') ->createFileFromDb('userGroup')
->createFileFromDb('user') ->createFileFromDb('user')
->createFileFromDb('notification') ->createFileFromDb('notification')
->createFileFromDb('dependency')
; ;
if (! $this->isLegacy()) { if (! $this->isLegacy()) {
@ -719,7 +720,8 @@ apply Service for (title => params in host.vars["%s"]) {
'user', 'user',
'userGroup', 'userGroup',
'timePeriod', 'timePeriod',
'notification' 'notification',
'dependency'
); );
return in_array($type, $types); return in_array($type, $types);

View File

@ -0,0 +1,421 @@
<?php
namespace Icinga\Module\Director\Objects;
use Icinga\Exception\ConfigurationError;
use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
use Icinga\Exception\NotFoundError;
use Icinga\Data\Filter\Filter;
use Icinga\Module\Director\Objects\HostApplyMatches;
class IcingaDependency extends IcingaObject
{
protected $table = 'icinga_dependency';
protected $defaultProperties = array(
'id' => 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_s' => null,
);
protected $supportsCustomVars = false;
protected $supportsImports = true;
protected $supportsApplyRules = true;
protected $relatedSets = array(
'states' => 'StateFilterSet',
);
protected $relations = array(
'zone' => 'IcingaZone',
'parent_host' => 'IcingaHost',
'parent_service' => 'IcingaService',
'child_host' => 'IcingaHost',
'child_service' => 'IcingaService',
'period' => 'IcingaTimePeriod',
);
protected $booleans = array(
'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)
{
if (is_int($key)) {
$this->id = $key;
} elseif (is_array($key)) {
foreach (array('id', 'parent_host_id', 'parent_service_id', 'child_host_id', 'child_service_id', 'object_name') as $k) {
if (array_key_exists($k, $key)) {
$this->set($k, $key[$k]);
}
}
} else {
return parent::setKey($key);
}
return $this;
}
protected function renderAssignments()
{
if ($this->hasBeenAssignedToServiceApply()) {
$tmpService= $this->getRelatedObject('child_service', $this->child_service_id);
$assigns = $tmpService->assignments()->toConfigString();
$filter = sprintf(
'%s && service.name == "%s"',
trim($assigns), $this->child_service
);
return "\n " . $filter . "\n";
}
if ($this->hasBeenAssignedToHostTemplateService()) {
$filter = sprintf(
'assign where "%s" in host.templates && service.name == "%s"',
$this->child_host, $this->child_service
);
return "\n " . $filter . "\n";
}
if ($this->hasBeenAssignedToHostTemplate()) {
$filter = sprintf(
'assign where "%s" in host.templates',
$this->child_host
);
return "\n " . $filter . "\n";
}
if ($this->hasBeenAssignedToServiceTemplate()) {
$filter = sprintf(
'assign where "%s" in service.templates',
$this->child_service
);
return "\n " . $filter . "\n";
}
return parent::renderAssignments();
}
protected function hasBeenAssignedToHostTemplate()
{
try {
return $this->child_host_id && $this->getRelatedObject(
'child_host',
$this->child_host_id
)->object_type === 'template';
} catch (NotFoundError $e) {
return false;
}
}
protected function hasBeenAssignedToServiceTemplate()
{
try {
return $this->child_service_id && $this->getRelatedObject(
'child_service',
$this->child_service_id
)->object_type === 'template';
} catch (NotFoundError $e) {
return false;
}
}
protected function hasBeenAssignedToHostTemplateService()
{
if (!$this->hasBeenAssignedToHostTemplate()) return false;
try {
return $this->child_service_id && $this->getRelatedObject(
'child_service',
$this->child_service_id
)->object_type === 'object';
} catch (NotFoundError $e) {
return false;
}
}
protected function hasBeenAssignedToServiceApply()
{
try {
return $this->child_service_id && $this->getRelatedObject(
'child_service',
$this->child_service_id
)->object_type === 'apply';
} 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->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->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()) {
return '';
}
if ($this->hasBeenAssignedToHostTemplateService()) {
return '';
}
if ($this->hasBeenAssignedToServiceApply()) {
return '';
}
return $this->renderRelationProperty('child_service', $this->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->parent_service_id, 'parent_service_name');
}
//special case for parent service set as plain string for Apply rules
public function renderParent_service_s()
{
return "\n parent_service_name = \"" . $this->parent_service_s ."\"\n";
}
public function isApplyRule()
{
if ($this->hasBeenAssignedToHostTemplate()) {
return true;
}
if ($this->hasBeenAssignedToServiceTemplate()) {
return true;
}
if ($this->hasBeenAssignedToServiceApply()) {
return true;
}
return $this->hasProperty('object_type')
&& $this->object_type === 'apply';
}
protected function resolveUnresolvedRelatedProperty($name)
{
$short = substr($name, 0, -3);
/** @var IcingaObject $class */
$class = $this->getRelationClass($short);
$obj_key = $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_s', $this->unresolvedRelatedProperties[$name]);
$this->reallySet('parent_service_id',null);
unset($this->unresolvedRelatedProperties[$name]);
return;
}
}
$this->reallySet('parent_service_s',null);
$host_id_prop=str_replace("service","host",$name);
if (isset($this->properties[$host_id_prop])) {
$obj_key=array("host_id" => $this->properties[$host_id_prop], "object_name" => $this->unresolvedRelatedProperties[$name]);
} else {
$obj_key=array("host_id" => null, "object_name" => $this->unresolvedRelatedProperties[$name]);
}
try {
$object = $class::load( $obj_key, $this->connection);
} catch (NotFoundError $e) {
// Not a simple service on host
// Hunt through inherited services, use service assigned to template if found
$tmp_host=IcingaHost::loadWithAutoIncId($this->properties[$host_id_prop], $this->connection);
//services for applicable templates
$resolver = $tmp_host->templateResolver();
foreach ($resolver->fetchResolvedParents() as $template_obj) {
$obj_key=array("host_id" => $template_obj->id, "object_name" => $this->unresolvedRelatedProperties[$name]);
try {
$object = $class::load( $obj_key, $this->connection);
} catch (NotFoundError $e) {
continue;
}
break;
}
if (!isset($object)) { //Not an inherited service, now try apply rules
$matcher = HostApplyMatches::prepare($tmp_host);
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(
$obj_key,
$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->get('object_name');
} else {
// handle special case for plain string parent service on Dependency Apply rules
if ($key == 'parent_service' && $this->get('parent_service_s') != null) {
return $this->get('parent_service_s');
}
}
return null;
}
}

View File

@ -2699,7 +2699,8 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
public function getOnDeleteUrl() public function getOnDeleteUrl()
{ {
return 'director/' . strtolower($this->getShortTableName()) . 's'; $plural= preg_replace('/cys$/','cies', strtolower($this->getShortTableName()) . 's');
return 'director/' . $plural;
} }
public function toJson( public function toJson(

View File

@ -9,6 +9,7 @@ class TemplateActionBar extends DirectorBaseActionBar
protected function assemble() protected function assemble()
{ {
$type = $this->type; $type = $this->type;
$pltype = preg_replace('/cys$/','cies', $type . 's');
$renderTree = $this->url->getParam('render') === 'tree'; $renderTree = $this->url->getParam('render') === 'tree';
$renderParams = $renderTree ? null : ['render' => 'tree']; $renderParams = $renderTree ? null : ['render' => 'tree'];
$this->add( $this->add(
@ -27,7 +28,7 @@ class TemplateActionBar extends DirectorBaseActionBar
)->add( )->add(
Link::create( Link::create(
$renderTree ? $this->translate('Table') : $this->translate('Tree'), $renderTree ? $this->translate('Table') : $this->translate('Tree'),
"director/${type}s/templates", "director/$pltype/templates",
$renderParams, $renderParams,
[ [
'class' => 'icon-' . ($renderTree ? 'doc-text' : 'sitemap'), 'class' => 'icon-' . ($renderTree ? 'doc-text' : 'sitemap'),

View File

@ -80,7 +80,7 @@ abstract class ObjectsController extends ActionController
$this $this
->addObjectsTabs() ->addObjectsTabs()
->setAutorefreshInterval(10) ->setAutorefreshInterval(10)
->addTitle($this->translate(ucfirst(strtolower($type)) . 's')) ->addTitle($this->translate(ucfirst($this->getPluralType())))
->actions(new ObjectsActionBar($type, $this->url())); ->actions(new ObjectsActionBar($type, $this->url()));
if ($type === 'command' && $this->params->get('type') === 'external_object') { if ($type === 'command' && $this->params->get('type') === 'external_object') {
@ -127,6 +127,7 @@ abstract class ObjectsController extends ActionController
)->content()->add($form); )->content()->add($form);
} }
/** /**
* Loads the TemplatesTable or the TemplateTreeRenderer * Loads the TemplatesTable or the TemplateTreeRenderer
* *
@ -139,7 +140,6 @@ abstract class ObjectsController extends ActionController
return; return;
} }
$type = $this->getType(); $type = $this->getType();
$shortType = IcingaObject::createByType($type)->getShortTableName(); $shortType = IcingaObject::createByType($type)->getShortTableName();
$this $this
->assertPermission('director/admin') ->assertPermission('director/admin')
@ -294,8 +294,8 @@ abstract class ObjectsController extends ActionController
{ {
// Strip final 's' and upcase an eventual 'group' // Strip final 's' and upcase an eventual 'group'
return preg_replace( return preg_replace(
array('/group$/', '/period$/', '/argument$/', '/apiuser$/'), array('/group$/', '/period$/', '/argument$/', '/apiuser$/', '/dependencie$/'),
array('Group', 'Period', 'Argument', 'ApiUser'), array('Group', 'Period', 'Argument', 'ApiUser', 'dependency'),
str_replace( str_replace(
'template', 'template',
'', '',
@ -306,11 +306,11 @@ abstract class ObjectsController extends ActionController
protected function getPluralType() protected function getPluralType()
{ {
return $this->getType() . 's'; return preg_replace("/cys$/","cies",$this->getType() . 's');
} }
protected function getPluralBaseType() protected function getPluralBaseType()
{ {
return $this->getBaseType() . 's'; return preg_replace("/cys$/","cies",$this->getBaseType() . 's');
} }
} }

View File

@ -177,7 +177,7 @@ abstract class TemplateController extends CompatController
protected function getPluralType() protected function getPluralType()
{ {
return $this->template()->getShortTableName() . 's'; return preg_replace('/cys$/','cies',$this->template()->getShortTableName() . 's');
} }
protected function getTranslatedType() protected function getTranslatedType()

View File

@ -523,6 +523,9 @@ abstract class DirectorObjectForm extends DirectorForm
'email', 'email',
'pager', 'pager',
'enable_notifications', 'enable_notifications',
'disable_checks', //Dependencies
'disable_notifications',
'ignore_soft_states',
'apply_for', 'apply_for',
'create_live', 'create_live',
'disabled', 'disabled',
@ -1508,8 +1511,9 @@ abstract class DirectorObjectForm extends DirectorForm
return $this; return $this;
} }
protected function addEventFilterElements() protected function addEventFilterElements($elements = array('states','types'))
{ {
if (in_array('states', $elements)) {
$this->addElement('extensibleSet', 'states', array( $this->addElement('extensibleSet', 'states', array(
'label' => $this->translate('States'), 'label' => $this->translate('States'),
'multiOptions' => $this->optionallyAddFromEnum($this->enumStates()), 'multiOptions' => $this->optionallyAddFromEnum($this->enumStates()),
@ -1517,7 +1521,9 @@ abstract class DirectorObjectForm extends DirectorForm
'The host/service states you want to get notifications for' 'The host/service states you want to get notifications for'
), ),
)); ));
}
if (in_array('types', $elements)) {
$this->addElement('extensibleSet', 'types', array( $this->addElement('extensibleSet', 'types', array(
'label' => $this->translate('Transition types'), 'label' => $this->translate('Transition types'),
'multiOptions' => $this->optionallyAddFromEnum($this->enumTypes()), 'multiOptions' => $this->optionallyAddFromEnum($this->enumTypes()),
@ -1525,11 +1531,8 @@ abstract class DirectorObjectForm extends DirectorForm
'The state transition types you want to get notifications for' 'The state transition types you want to get notifications for'
), ),
)); ));
}
$elements = array(
'states',
'types',
);
$this->addDisplayGroup($elements, 'event_filters', array( $this->addDisplayGroup($elements, 'event_filters', array(
'decorators' => array( 'decorators' => array(
'FormElements', 'FormElements',

View File

@ -18,17 +18,17 @@ class ObjectsTabs extends Tabs
$object = IcingaObject::createByType(substr($type, 0, -5)); $object = IcingaObject::createByType(substr($type, 0, -5));
} }
// TODO: plural? $pltype=strtolower(preg_replace('/cys$/','cies',$type . 's'));
if ($auth->hasPermission("director/${type}s")) { if ($auth->hasPermission("director/${pltype}")) {
$this->add('index', array( $this->add('index', array(
'url' => sprintf('director/%ss', strtolower($type)), 'url' => sprintf('director/%s', $pltype),
'label' => $this->translate(ucfirst($type) . 's'), 'label' => $this->translate(ucfirst($pltype)),
)); ));
} }
if ($object->getShortTableName() === 'command') { if ($object->getShortTableName() === 'command') {
$this->add('external', array( $this->add('external', array(
'url' => sprintf('director/%ss', strtolower($type)), 'url' => sprintf('director/%s', strtolower($pltype)),
'urlParams' => ['type' => 'external_object'], 'urlParams' => ['type' => 'external_object'],
'label' => $this->translate('External'), 'label' => $this->translate('External'),
)); ));
@ -39,7 +39,7 @@ class ObjectsTabs extends Tabs
)) { )) {
if ($object->supportsApplyRules()) { if ($object->supportsApplyRules()) {
$this->add('applyrules', array( $this->add('applyrules', array(
'url' => sprintf('director/%ss/applyrules', $type), 'url' => sprintf('director/%s/applyrules', $pltype),
'label' => $this->translate('Apply') 'label' => $this->translate('Apply')
)); ));
} }
@ -48,7 +48,7 @@ class ObjectsTabs extends Tabs
if ($auth->hasPermission('director/admin') && $type !== 'zone') { if ($auth->hasPermission('director/admin') && $type !== 'zone') {
if ($object->supportsImports()) { if ($object->supportsImports()) {
$this->add('templates', array( $this->add('templates', array(
'url' => sprintf('director/%ss/templates', strtolower($type)), 'url' => sprintf('director/%s/templates', $pltype),
'label' => $this->translate('Templates'), 'label' => $this->translate('Templates'),
)); ));
} }
@ -71,7 +71,7 @@ class ObjectsTabs extends Tabs
} }
if ($object->supportsSets() && $auth->hasPermission("director/${type}_sets")) { if ($object->supportsSets() && $auth->hasPermission("director/${type}_sets")) {
$this->add('sets', array( $this->add('sets', array(
'url' => sprintf('director/%ss/sets', $type), 'url' => sprintf('director/%s/sets', $pltype),
'label' => $this->translate('Sets') 'label' => $this->translate('Sets')
)); ));
} }

View File

@ -275,7 +275,8 @@
{ {
$suggestions.load(this.module.icinga.config.baseUrl + '/director/suggest', { $suggestions.load(this.module.icinga.config.baseUrl + '/director/suggest', {
value: $el.val(), value: $el.val(),
context: $el.data('suggestion-context') context: $el.data('suggestion-context'),
for_host: $el.data('suggestion-for-host')
}, function (responseText, textStatus, jqXHR) { }, function (responseText, textStatus, jqXHR) {
var $li = $suggestions.find('li'); var $li = $suggestions.find('li');
if ($li.length) { if ($li.length) {

View File

@ -0,0 +1,88 @@
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'),
disable_notifications ENUM('y', 'n'),
ignore_soft_states ENUM('y', 'n'),
period_id INT(10) UNSIGNED DEFAULT NULL,
zone_id INT(10) UNSIGNED DEFAULT NULL,
assign_filter TEXT DEFAULT NULL,
PRIMARY KEY (id),
CONSTRAINT icinga_dependency_parent_host
FOREIGN KEY parent_host (parent_host_id)
REFERENCES icinga_host (id)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT icinga_dependency_parent_service
FOREIGN KEY parent_service (parent_service_id)
REFERENCES icinga_service (id)
ON DELETE CASCADE
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;

View File

@ -0,0 +1,2 @@
ALTER TABLE icinga_dependency ADD COLUMN parent_service_s VARCHAR(255);

View File

@ -1575,6 +1575,94 @@ CREATE TABLE icinga_user_resolved_var (
ON UPDATE RESTRICT ON UPDATE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) 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'),
disable_notifications ENUM('y', 'n'),
ignore_soft_states ENUM('y', 'n'),
period_id INT(10) UNSIGNED DEFAULT NULL,
zone_id INT(10) UNSIGNED DEFAULT NULL,
assign_filter TEXT DEFAULT NULL,
parent_service_s 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 CASCADE
ON UPDATE CASCADE,
CONSTRAINT icinga_dependency_parent_service
FOREIGN KEY parent_service (parent_service_id)
REFERENCES icinga_service (id)
ON DELETE CASCADE
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 INSERT INTO director_schema_migration
(schema_version, migration_time) (schema_version, migration_time)
VALUES (143, NOW()); VALUES (143, NOW());