Merge branch 'feature/dependencies-1096'

fixes #132
This commit is contained in:
Thomas Gelf 2017-09-28 11:02:39 +02:00
commit 19537463c1
22 changed files with 1360 additions and 42 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,27 @@
<?php
namespace Icinga\Module\Director\Controllers;
use Icinga\Module\Director\Web\Controller\ObjectsController;
use ipl\Html\Html;
class DependenciesController extends ObjectsController
{
protected function addObjectsTabs()
{
$res = parent::addObjectsTabs();
$this->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();
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace Icinga\Module\Director\Controllers;
use Icinga\Module\Director\Forms\IcingaDependencyForm;
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)
{
/** @var IcingaDependencyForm $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

@ -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);
}
}

View File

@ -0,0 +1,240 @@
<?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', [
'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;
}
}

View File

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

View File

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

View File

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

View File

@ -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);

View File

@ -0,0 +1,468 @@
<?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;
class IcingaDependency extends IcingaObject
{
protected $table = 'icinga_dependency';
protected $defaultProperties = [
'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_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;
}
}

View File

@ -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(

View File

@ -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'),

View File

@ -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');
}
}

View File

@ -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()

View File

@ -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,8 +1511,9 @@ abstract class DirectorObjectForm extends DirectorForm
return $this;
}
protected function addEventFilterElements()
protected function addEventFilterElements($elements = array('states','types'))
{
if (in_array('states', $elements)) {
$this->addElement('extensibleSet', 'states', array(
'label' => $this->translate('States'),
'multiOptions' => $this->optionallyAddFromEnum($this->enumStates()),
@ -1517,7 +1521,9 @@ abstract class DirectorObjectForm extends DirectorForm
'The host/service states 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()),
@ -1525,11 +1531,8 @@ abstract class DirectorObjectForm extends DirectorForm
'The state transition types you want to get notifications for'
),
));
}
$elements = array(
'states',
'types',
);
$this->addDisplayGroup($elements, 'event_filters', array(
'decorators' => array(
'FormElements',

View File

@ -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')
));
}

View File

@ -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) {

View File

@ -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());

View File

@ -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());

View File

@ -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());

View File

@ -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());