Merge pull request #1828 from Icinga/feature/scheduled-downtimes-347

Feature/scheduled downtimes, fixes #347
This commit is contained in:
Thomas Gelf 2019-04-11 11:42:36 +02:00 committed by GitHub
commit 2c4d492d97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1312 additions and 84 deletions

View File

@ -0,0 +1,38 @@
<?php
namespace Icinga\Module\Director\Controllers;
use Icinga\Module\Director\Forms\IcingaScheduledDowntimeRangeForm;
use Icinga\Module\Director\Objects\IcingaScheduledDowntime;
use Icinga\Module\Director\Web\Controller\ObjectController;
use Icinga\Module\Director\Web\Table\IcingaScheduledDowntimeRangeTable;
class ScheduledDowntimeController extends ObjectController
{
public function rangesAction()
{
/** @var IcingaScheduledDowntime $object */
$object = $this->object;
$this->tabs()->activate('ranges');
$this->addTitle($this->translate('Time period ranges'));
$form = IcingaScheduledDowntimeRangeForm::load()
->setScheduledDowntime($object);
if (null !== ($name = $this->params->get('range'))) {
$this->addBackLink($this->url()->without('range'));
$form->loadObject([
'scheduled_downtime_id' => $object->get('id'),
'range_key' => $name,
'range_type' => $this->params->get('range_type')
]);
}
$this->content()->add($form->handleRequest());
IcingaScheduledDowntimeRangeTable::load($object)->renderTo($this);
}
public function getType()
{
return 'scheduledDowntime';
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace Icinga\Module\Director\Controllers;
use Icinga\Module\Director\Web\Controller\ObjectsController;
class ScheduledDowntimesController extends ObjectsController
{
protected function addObjectsTabs()
{
$res = parent::addObjectsTabs();
$this->tabs()->remove('index');
$this->tabs()->remove('templates');
return $res;
}
protected function getTable()
{
return parent::getTable()
->setBaseObjectUrl('director/scheduled-downtime');
}
protected function getApplyRulesTable()
{
return parent::getApplyRulesTable()->createLinksWithNames();
}
public function getType()
{
return 'scheduledDowntime';
}
public function getBaseObjectUrl()
{
return 'scheduled-downtime';
}
}

View File

@ -79,6 +79,8 @@ class SuggestController extends ActionController
* TODO: Should not remain here
*
* @return array
* @throws \Icinga\Exception\ConfigurationError
* @throws \Icinga\Security\SecurityException
*/
protected function suggestLocations()
{
@ -152,39 +154,22 @@ class SuggestController extends ActionController
return $r;
}
protected function suggestHosttemplates()
{
$this->assertPermission('director/hosts');
$db = $this->db()->getDbAdapter();
$query = $db->select()
->from('icinga_host', 'object_name')
->order('object_name')
->where("object_type = 'template'")
->where('template_choice_id IS NULL');
return $db->fetchCol($query);
return $this->fetchTemplateNames('icinga_host', 'template_choice_id IS NULL');
}
protected function suggestServicetemplates()
{
$this->assertPermission('director/services');
$db = $this->db()->getDbAdapter();
$query = $db->select()
->from('icinga_service', 'object_name')
->order('object_name')
->where("object_type = 'template'");
return $db->fetchCol($query);
return $this->fetchTemplateNames('icinga_service', 'template_choice_id IS NULL');
}
protected function suggestNotificationtemplates()
{
$this->assertPermission('director/notifications');
$db = $this->db()->getDbAdapter();
$query = $db->select()
->from('icinga_notification', 'object_name')
->order('object_name')
->where("object_type = 'template'");
return $db->fetchCol($query);
return $this->fetchTemplateNames('icinga_notification');
}
protected function suggestCommandtemplates()
@ -200,12 +185,19 @@ class SuggestController extends ActionController
protected function suggestUsertemplates()
{
$this->assertPermission('director/users');
$db = $this->db()->getDbAdapter();
$query = $db->select()
->from('icinga_user', 'object_name')
->order('object_name')
->where("object_type = 'template'");
return $db->fetchCol($query);
return $this->fetchTemplateNames('icinga_user');
}
/**
* @return array
* @throws \Icinga\Security\SecurityException
* @codingStandardsIgnoreStart
*/
protected function suggestScheduled_downtimetemplates()
{
// @codingStandardsIgnoreEnd
$this->assertPermission('director/scheduled-downtimes');
return $this->fetchTemplateNames('icinga_scheduled_downtime');
}
protected function suggestCheckcommandnames()
@ -219,6 +211,21 @@ class SuggestController extends ActionController
return $db->fetchCol($query);
}
protected function fetchTemplateNames($table, $where = null)
{
$db = $this->db()->getDbAdapter();
$query = $db->select()
->from($table, 'object_name')
->where('object_type = ?', 'template')
->order('object_name');
if ($where !== null) {
$query->where('template_choice_id IS NULL');
}
return $db->fetchCol($query);
}
protected function suggestHostgroupnames()
{
$db = $this->db()->getDbAdapter();
@ -322,12 +329,7 @@ class SuggestController extends ActionController
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);
return $this->fetchTemplateNames('icinga_dependency');
}
protected function highlight($val, $search)

View File

@ -0,0 +1,116 @@
<?php
namespace Icinga\Module\Director\Forms;
use Icinga\Module\Director\Web\Form\DirectorObjectForm;
class IcingaScheduledDowntimeForm extends DirectorObjectForm
{
/**
* @throws \Zend_Form_Exception
*/
public function setup()
{
if ($this->isTemplate()) {
$this->addElement('text', 'object_name', [
'label' => $this->translate('Template name'),
'required' => true,
]);
} else {
$this->addElement('text', 'object_name', [
'label' => $this->translate('Downtime name'),
'required' => true,
]);
}
$this->addImportsElement();
$this->addElement('text', 'author', [
'label' => $this->translate('Author'),
'description' => $this->translate(
'This name will show up as the author for ever related downtime'
. ' comment'
),
'required' => ! $this->isTemplate()
]);
$this->addElement('textarea', 'comment', [
'label' => $this->translate('Comment'),
'description' => $this->translate(
'Every related downtime will show this comment'
),
'required' => ! $this->isTemplate(),
'rows' => 4,
]);
$this->addBoolean('fixed', [
'label' => $this->translate('Fixed'),
'description' => $this->translate(
'Whether this downtime is fixed or flexible. If unsure please'
. ' check the related documentation:'
. ' https://icinga.com/docs/icinga2/latest/doc/08-advanced-topics/#downtimes'
),
'required' => ! $this->isTemplate(),
]);
$this->addElement('text', 'duration', [
'label' => $this->translate('Duration'),
'description' => $this->translate(
'How long the downtime lasts. Only has an effect for flexible'
. ' (non-fixed) downtimes. Time in seconds, supported suffixes'
. ' include ms (milliseconds), s (seconds), m (minutes),'
. ' h (hours) and d (days). To express "90 minutes" you might'
. ' want to write 1h 30m'
)
]);
$this->addDisabledElement();
$this->addAssignmentElements();
$this->setButtons();
}
/**
* @return $this
* @throws \Zend_Form_Exception
*/
protected function addAssignmentElements()
{
if ($this->isTemplate()) {
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) {
return $this;
}
$suggestionContext = ucfirst($applyTo) . 'FilterColumns';
$this->addAssignFilter([
'suggestionContext' => $suggestionContext,
'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 setObjectSuccessUrl()
{
$this->setSuccessUrl(
'director/scheduled-downtime',
$this->object()->getUrlParams()
);
}
}

View File

@ -0,0 +1,110 @@
<?php
namespace Icinga\Module\Director\Forms;
use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\Objects\IcingaScheduledDowntime;
use Icinga\Module\Director\Objects\IcingaScheduledDowntimeRange;
use Icinga\Module\Director\Web\Form\DirectorObjectForm;
class IcingaScheduledDowntimeRangeForm extends DirectorObjectForm
{
/** @var IcingaScheduledDowntime */
private $downtime;
/**
* @throws \Zend_Form_Exception
*/
public function setup()
{
$this->addHidden('scheduled_downtime_id', $this->downtime->get('id'));
$this->addElement('text', 'range_key', [
'label' => $this->translate('Day(s)'),
'description' => $this->translate(
'Might be, monday, tuesday, 2016-01-28 - have a look at the documentation for more examples'
),
]);
$this->addElement('text', 'range_value', [
'label' => $this->translate('Timeperiods'),
'description' => $this->translate(
'One or more time periods, e.g. 00:00-24:00 or 00:00-09:00,17:00-24:00'
),
]);
$this->setButtons();
}
public function setScheduledDowntime(IcingaScheduledDowntime $downtime)
{
$this->downtime = $downtime;
$this->setDb($downtime->getConnection());
return $this;
}
/**
* @param IcingaScheduledDowntimeRange $object
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
*/
protected function deleteObject($object)
{
$key = $object->get('range_key');
$downtime = $this->downtime;
$downtime->ranges()->remove($key);
$downtime->store();
$msg = sprintf(
$this->translate('Time range "%s" has been removed from %s'),
$key,
$downtime->getObjectName()
);
$url = $this->getSuccessUrl()->without(
['range', 'range_type']
);
$this->setSuccessUrl($url);
$this->redirectOnSuccess($msg);
}
/**
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
*/
public function onSuccess()
{
$object = $this->object();
if ($object->hasBeenModified()) {
$this->downtime->ranges()->setRange(
$this->getValue('range_key'),
$this->getValue('range_value')
);
}
if ($this->downtime->hasBeenModified()) {
if (! $object->hasBeenLoadedFromDb()) {
$this->setHttpResponseCode(201);
}
$msg = sprintf(
$object->hasBeenLoadedFromDb()
? $this->translate('The %s has successfully been stored')
: $this->translate('A new %s has successfully been created'),
$this->translate($this->getObjectShortClassName())
);
$this->downtime->store($this->db);
} else {
if ($this->isApiRequest()) {
$this->setHttpResponseCode(304);
}
$msg = $this->translate('No action taken, object has not been modified');
}
if ($object instanceof IcingaObject) {
$this->setSuccessUrl(
'director/' . strtolower($this->getObjectShortClassName()),
$object->getUrlParams()
);
}
$this->redirectOnSuccess($msg);
}
}

View File

@ -8,6 +8,8 @@ class AlertsDashboard extends Dashboard
'Notifications',
'Users',
'Timeperiods',
'DependencyObject',
'ScheduledDowntimeApply',
);
public function getTitle()

View File

@ -0,0 +1,20 @@
<?php
namespace Icinga\Module\Director\Dashboard\Dashlet;
class ScheduledDowntimeApplyDashlet extends Dashlet
{
protected $icon = 'plug';
protected $requiredStats = ['scheduled_downtime'];
public function getTitle()
{
return $this->translate('Scheduled Downtimes');
}
public function getUrl()
{
return 'director/scheduled-downtimes/applyrules';
}
}

View File

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

View File

@ -457,6 +457,7 @@ class Db extends DbConnection
'usergroup',
'command',
'timeperiod',
'scheduled_downtime',
'notification',
'apiuser',
'endpoint',

View File

@ -2,9 +2,10 @@
namespace Icinga\Module\Director\Db;
use Icinga\Exception\ProgrammingError;
use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\Resolver\TemplateTree;
use InvalidArgumentException;
use RuntimeException;
use Zend_Db_Select as ZfSelect;
class IcingaObjectFilterHelper
@ -16,7 +17,6 @@ class IcingaObjectFilterHelper
/**
* @param IcingaObject|int|string $id
* @return int
* @throws ProgrammingError
*/
public static function wantId($id)
{
@ -27,11 +27,11 @@ class IcingaObjectFilterHelper
} elseif (is_string($id) && ctype_digit($id)) {
return (int) $id;
} else {
throw new ProgrammingError(
throw new InvalidArgumentException(sprintf(
'Numeric ID or IcingaObject expected, got %s',
// TODO: just type/class info?
var_export($id, 1)
);
));
}
}
@ -41,7 +41,6 @@ class IcingaObjectFilterHelper
* @param string $tableAlias
* @param string $inheritanceType
* @return ZfSelect
* @throws ProgrammingError
*/
public static function filterByTemplate(
ZfSelect $query,
@ -76,10 +75,10 @@ class IcingaObjectFilterHelper
$sub->where("$i.parent_${type}_id IN (?)", $ids);
}
} else {
throw new ProgrammingError(
throw new RuntimeException(sprintf(
'Unable to understand "%s" inheritance',
$inheritanceType
);
));
}
return $query->where('EXISTS ?', $sub);

View File

@ -470,6 +470,7 @@ class IcingaConfig
->createFileFromDb('user')
->createFileFromDb('notification')
->createFileFromDb('dependency')
->createFileFromDb('scheduledDowntime')
;
PrefetchCache::forget();

View File

@ -2558,6 +2558,8 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
$type = 'templateChoiceHost';
} elseif ($type === 'service_template_choice') {
$type = 'TemplateChoiceService';
} elseif ($type === 'scheduled_downtime') {
$type = 'ScheduledDowntime';
}
return 'Icinga\\Module\\Director\\Objects\\' . $prefix . ucfirst($type);
@ -3013,7 +3015,7 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
{
$params = array();
if ($this->isApplyRule()) {
if ($this->isApplyRule() && ! $this instanceof IcingaScheduledDowntime) {
$params['id'] = $this->get('id');
} else {
$params = array('name' => $this->getObjectName());

View File

@ -0,0 +1,108 @@
<?php
namespace Icinga\Module\Director\Objects;
use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
use RuntimeException;
class IcingaScheduledDowntime extends IcingaObject
{
protected $table = 'icinga_scheduled_downtime';
protected $defaultProperties = [
'id' => null,
'zone_id' => null,
'object_name' => null,
'object_type' => null,
'disabled' => 'n',
'author' => null,
'comment' => null,
'fixed' => null,
'duration' => null,
'apply_to' => null,
'assign_filter' => null,
];
protected $supportsImports = true;
protected $supportsRanges = true;
protected $supportsApplyRules = true;
protected $relations = [
'zone' => 'IcingaZone',
];
protected $booleans = [
'fixed' => 'fixed',
];
protected $intervalProperties = [
'duration' => 'duration',
];
protected $propertiesNotForRendering = [
'id',
'apply_to',
'object_name',
'object_type',
];
/**
* @return string
*/
protected function renderObjectHeader()
{
if ($this->isApplyRule()) {
if (($to = $this->get('apply_to')) === null) {
throw new RuntimeException(sprintf(
'Applied notification "%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();
}
}
public function getOnDeleteUrl()
{
if ($this->isApplyRule()) {
return 'director/scheduled-downtimes/applyrules';
} elseif ($this->isTemplate()) {
return 'director/scheduled-downtimes/templates';
} else {
return 'director/scheduled-downtimes';
}
}
public function isActive($now = null)
{
if ($now === null) {
$now = time();
}
foreach ($this->ranges()->getRanges() as $range) {
if ($range->isActive($now)) {
return true;
}
}
// TODO: no range currently means (and renders) "never", Icinga behaves
// different. Figure out whether and how we should support this
return false;
}
protected function prefersGlobalZone()
{
return false;
}
}

View File

@ -0,0 +1,88 @@
<?php
namespace Icinga\Module\Director\Objects;
use Icinga\Module\Director\Data\Db\DbObject;
class IcingaScheduledDowntimeRange extends DbObject
{
protected $keyName = ['scheduled_downtime_id', 'range_key', 'range_type'];
protected $table = 'icinga_scheduled_downtime_range';
protected $defaultProperties = [
'scheduled_downtime_id' => null,
'range_key' => null,
'range_value' => null,
'range_type' => 'include',
'merge_behaviour' => 'set',
];
public function isActive($now = null)
{
if ($now === null) {
$now = time();
}
if (false === ($weekDay = $this->getWeekDay($this->get('range_key')))) {
// TODO, dates are not yet supported
return false;
}
if ((int) strftime('%w', $now) !== $weekDay) {
return false;
}
$timeRanges = preg_split('/\s*,\s*/', $this->get('range_value'), -1, PREG_SPLIT_NO_EMPTY);
foreach ($timeRanges as $timeRange) {
if ($this->timeRangeIsActive($timeRange, $now)) {
return true;
}
}
return false;
}
protected function timeRangeIsActive($rangeString, $now)
{
$hBegin = $mBegin = $hEnd = $mEnd = null;
if (sscanf($rangeString, '%2d:%2d-%2d:%2d', $hBegin, $mBegin, $hEnd, $mEnd) === 4) {
if ($this->timeFromHourMin($hBegin, $mBegin, $now) <= $now
&& $this->timeFromHourMin($hEnd, $mEnd, $now) >= $now
) {
return true;
}
} else {
// TODO: throw exception?
}
return false;
}
protected function timeFromHourMin($hour, $min, $now)
{
return strtotime(sprintf('%s %02d:%02d:00', date('Y-m-d', $now), $hour, $min));
}
protected function getWeekDay($day)
{
switch ($day) {
case 'sunday':
return 0;
case 'monday':
return 1;
case 'tuesday':
return 2;
case 'wednesday':
return 3;
case 'thursday':
return 4;
case 'friday':
return 5;
case 'saturday':
return 6;
}
return false;
}
}

View File

@ -0,0 +1,297 @@
<?php
namespace Icinga\Module\Director\Objects;
use Countable;
use Exception;
use Iterator;
use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
use Icinga\Module\Director\IcingaConfig\IcingaConfigRenderer;
class IcingaScheduledDowntimeRanges implements Iterator, Countable, IcingaConfigRenderer
{
protected $storedRanges = [];
protected $ranges = [];
protected $modified = false;
protected $object;
private $position = 0;
protected $idx = array();
public function __construct(IcingaObject $object)
{
$this->object = $object;
}
public function count()
{
return count($this->ranges);
}
public function rewind()
{
$this->position = 0;
}
public function hasBeenModified()
{
return $this->modified;
}
public function current()
{
if (! $this->valid()) {
return null;
}
return $this->ranges[$this->idx[$this->position]];
}
public function key()
{
return $this->idx[$this->position];
}
public function next()
{
++$this->position;
}
public function valid()
{
return array_key_exists($this->position, $this->idx);
}
public function get($key)
{
if (array_key_exists($key, $this->ranges)) {
return $this->ranges[$key];
}
return null;
}
public function getValues()
{
$res = array();
foreach ($this->ranges as $key => $range) {
$res[$key] = $range->range_value;
}
return (object) $res;
}
public function getOriginalValues()
{
$res = array();
foreach ($this->storedRanges as $key => $range) {
$res[$key] = $range->range_value;
}
return (object) $res;
}
public function getRanges()
{
return $this->ranges;
}
protected function modify($range, $value)
{
$this->ranges[$range]->range_key = $value;
}
public function set($ranges)
{
foreach ($ranges as $range => $value) {
$this->setRange($range, $value);
}
$toDelete = array_diff(array_keys($this->ranges), array_keys($ranges));
foreach ($toDelete as $range) {
$this->remove($range);
}
return $this;
}
public function setRange($range, $value)
{
if ($value === null && array_key_exists($range, $this->ranges)) {
$this->remove($range);
return $this;
}
if (array_key_exists($range, $this->ranges)) {
if ($this->ranges[$range]->range_value === $value) {
return $this;
} else {
$this->ranges[$range]->range_value = $value;
$this->modified = true;
}
} else {
$this->ranges[$range] = IcingaScheduledDowntimeRange::create([
'scheduled_downtime_id' => $this->object->id,
'range_key' => $range,
'range_value' => $value,
]);
$this->modified = true;
}
return $this;
}
/**
* Magic isset check
*
* @return boolean
*/
public function __isset($range)
{
return array_key_exists($range, $this->ranges);
}
public function remove($range)
{
if (array_key_exists($range, $this->ranges)) {
unset($this->ranges[$range]);
}
$this->modified = true;
$this->refreshIndex();
}
public function clear()
{
$this->ranges = [];
$this->modified = true;
$this->refreshIndex();
}
protected function refreshIndex()
{
ksort($this->ranges);
$this->idx = array_keys($this->ranges);
}
protected function getRangeClass()
{
return __NAMESPACE__ . '\\Icinga' .ucfirst($this->object->getShortTableName()) . 'Range';
}
public function listRangesNames()
{
return array_keys($this->ranges);
}
public function getType()
{
return $this->object->getShortTableName();
}
public function getRangeTableName()
{
return $this->object->getTableName() . '_range';
}
protected function loadFromDb()
{
$db = $this->object->getDb();
$connection = $this->object->getConnection();
$table = $this->getRangeTableName();
$query = $db->select()
->from(['o' => $table])
->where('o.scheduled_downtime_id = ?', (int) $this->object->get('id'))
->order('o.range_key');
$this->ranges = IcingaScheduledDowntimeRange::loadAll($connection, $query, 'range_key');
$this->storedRanges = array();
foreach ($this->ranges as $key => $range) {
$this->storedRanges[$key] = clone($range);
}
return $this;
}
public function store()
{
$db = $this->object->getConnection();
foreach ($this->ranges as $range) {
$range->scheduled_downtime_id = $this->object->id;
$range->store($db);
}
foreach (array_diff(array_keys($this->storedRanges), array_keys($this->ranges)) as $delete) {
$db->getDbAdapter()->delete(
'icinga_scheduled_downtime_range',
$this->storedRanges[$delete]->createWhere()
);
}
$this->storedRanges = $this->ranges;
return true;
}
protected function getClass()
{
return __NAMESPACE__ . '\\IcingaScheduledDowntimeRange';
}
public static function loadForStoredObject(IcingaObject $object)
{
$ranges = new static($object);
return $ranges->loadFromDb();
}
public function toConfigString()
{
if (empty($this->ranges) && $this->object->object_type === 'template') {
return '';
}
$string = " ranges = {\n";
foreach ($this->ranges as $range) {
$string .= sprintf(
" %s\t= %s\n",
c::renderString($range->range_key),
c::renderString($range->range_value)
);
}
return $string . " }\n";
}
public function toLegacyConfigString()
{
return '';
}
public function __toString()
{
try {
return $this->toConfigString();
} catch (Exception $e) {
trigger_error($e);
$previousHandler = set_exception_handler(
function () {
}
);
restore_error_handler();
if ($previousHandler !== null) {
call_user_func($previousHandler, $e);
die();
} else {
die($e->getMessage());
}
}
}
}

View File

@ -8,7 +8,7 @@ class TemplateActionBar extends DirectorBaseActionBar
{
protected function assemble()
{
$type = $this->type;
$type = str_replace('_', '-', $this->type);
$plType = preg_replace('/cys$/', 'cies', $type . 's');
$renderTree = $this->url->getParam('render') === 'tree';
$renderParams = $renderTree ? null : ['render' => 'tree'];

View File

@ -79,7 +79,11 @@ abstract class ObjectController extends ActionController
'add'
);
} else {
$this->tabs(new ObjectTabs($this->getType(), $this->getAuth(), $this->object));
$this->tabs(new ObjectTabs(
$this->getRequest()->getControllerName(),
$this->getAuth(),
$this->object
));
}
}
}

View File

@ -48,8 +48,11 @@ abstract class ObjectsController extends ActionController
if (substr($this->getType(), -5) === 'Group') {
$tabName = 'groups';
}
$this->tabs(new ObjectsTabs($this->getBaseType(), $this->Auth()))
->activate($tabName);
$this->tabs(new ObjectsTabs(
$this->getBaseType(),
$this->Auth(),
$this->getBaseObjectUrl()
))->activate($tabName);
return $this;
}
@ -106,7 +109,7 @@ abstract class ObjectsController extends ActionController
->addObjectsTabs()
->setAutorefreshInterval(10)
->addTitle($this->translate(ucfirst($this->getPluralType())))
->actions(new ObjectsActionBar($type, $this->url()));
->actions(new ObjectsActionBar($this->getBaseObjectUrl(), $this->url()));
if ($type === 'command' && $this->params->get('type') === 'external_object') {
$this->tabs()->activate('external');
@ -126,7 +129,22 @@ abstract class ObjectsController extends ActionController
protected function getTable()
{
return ObjectsTable::create($this->getType(), $this->db())
->setAuth($this->getAuth());
->setAuth($this->getAuth())
->setBaseObjectUrl($this->getBaseObjectUrl());
}
/**
* @return ApplyRulesTable
* @throws NotFoundError
*/
protected function getApplyRulesTable()
{
$table = new ApplyRulesTable($this->db());
$table->setType($this->getType())
->setBaseObjectUrl($this->getBaseObjectUrl());
$this->eventuallyFilterCommand($table);
return $table;
}
/**
@ -240,12 +258,13 @@ abstract class ObjectsController extends ActionController
$this->translate('All your %s Apply Rules'),
$tType
);
$baseUrl = 'director/' . $this->getBaseObjectUrl();
$this->actions()
//->add($this->getBackToDashboardLink())
->add(
Link::create(
$this->translate('Add'),
"director/$type/add",
"${baseUrl}/add",
['type' => 'apply'],
[
'title' => sprintf(
@ -258,10 +277,7 @@ abstract class ObjectsController extends ActionController
)
);
$table = new ApplyRulesTable($this->db());
$table->setType($this->getType());
$this->eventuallyFilterCommand($table);
$table->renderTo($this);
$this->getApplyRulesTable()->renderTo($this);
}
/**
@ -429,6 +445,11 @@ abstract class ObjectsController extends ActionController
}
}
protected function getBaseObjectUrl()
{
return $this->getType();
}
/**
* @return string
*/

View File

@ -1154,7 +1154,7 @@ abstract class DirectorObjectForm extends DirectorForm
return $this;
}
if (in_array($this->getObjectShortClassName(), ['TimePeriod'])) {
if (in_array($this->getObjectShortClassName(), ['TimePeriod', 'ScheduledDowntime'])) {
$required = false;
} else {
$required = $required !== null ? $required : !$this->isTemplate();

View File

@ -3,12 +3,12 @@
namespace Icinga\Module\Director\Web\Form;
use Icinga\Application\Icinga;
use Icinga\Exception\ProgrammingError;
use Icinga\Web\Notification;
use Icinga\Web\Request;
use Icinga\Web\Response;
use Icinga\Web\Url;
use Exception;
use RuntimeException;
/**
* QuickForm wants to be a base class for simple forms
@ -225,7 +225,7 @@ abstract class QuickForm extends QuickBaseForm
{
if ($this->isApiRequest === null) {
if ($this->request === null) {
throw new ProgrammingError(
throw new RuntimeException(
'Early acess to isApiRequest(). This is not possible, sorry'
);
}
@ -510,7 +510,7 @@ abstract class QuickForm extends QuickBaseForm
public function setRequest(Request $request)
{
if ($this->request !== null) {
throw new ProgrammingError('Unable to set request twice');
throw new RuntimeException('Unable to set request twice');
}
$this->request = $request;

View File

@ -25,6 +25,13 @@ class ApplyRulesTable extends ZfQueryBasedTable
private $type;
/** @var IcingaObject */
protected $dummyObject;
protected $baseObjectUrl;
protected $linkWithName = false;
public static function create($type, Db $db)
{
$table = new static($db);
@ -35,6 +42,21 @@ class ApplyRulesTable extends ZfQueryBasedTable
public function setType($type)
{
$this->type = $type;
return $this;
}
public function setBaseObjectUrl($url)
{
$this->baseObjectUrl = $url;
return $this;
}
public function createLinksWithNames($linksWithName = true)
{
$this->linkWithName = (bool) $linksWithName;
return $this;
}
@ -50,9 +72,12 @@ class ApplyRulesTable extends ZfQueryBasedTable
public function renderRow($row)
{
$url = Url::fromPath("director/{$this->type}/edit", [
'id' => $row->id,
]);
if ($this->linkWithName) {
$params = ['name' => $row->object_name];
} else {
$params = ['id' => $row->id];
}
$url = Url::fromPath("director/{$this->baseObjectUrl}/edit", $params);
$tr = static::tr([
static::td(Link::create($row->object_name, $url)),
@ -67,6 +92,14 @@ class ApplyRulesTable extends ZfQueryBasedTable
return $tr;
}
/**
* Should be triggered from renderRow, still unused.
*
* @param IcingaObject $template
* @param string $inheritance
* @return $this
* @throws \Icinga\Exception\ProgrammingError
*/
public function filterTemplate(
IcingaObject $template,
$inheritance = IcingaObjectFilterHelper::INHERIT_DIRECT
@ -99,32 +132,32 @@ class ApplyRulesTable extends ZfQueryBasedTable
public function createActionLinks($row)
{
$type = $this->type;
$baseUrl = 'director/' . $this->baseObjectUrl;
$links = [];
$links[] = Link::create(
Icon::create('sitemap'),
"director/${type}template/applytargets",
"${baseUrl}template/applytargets",
['id' => $row->id],
['title' => $this->translate('Show affected Objects')]
);
$links[] = Link::create(
Icon::create('edit'),
"director/$type/edit",
"$baseUrl/edit",
['id' => $row->id],
['title' => $this->translate('Modify this Apply Rule')]
);
$links[] = Link::create(
Icon::create('doc-text'),
"director/$type/render",
"$baseUrl/render",
['id' => $row->id],
['title' => $this->translate('Apply Rule rendering preview')]
);
$links[] = Link::create(
Icon::create('history'),
"director/$type/history",
"$baseUrl/history",
['id' => $row->id],
['title' => $this->translate('Apply rule history')]
);
@ -149,9 +182,22 @@ class ApplyRulesTable extends ZfQueryBasedTable
return FilterRenderer::applyToQuery($filter, $query);
}
/**
* @return IcingaObject
*/
protected function getDummyObject()
{
if ($this->dummyObject === null) {
$type = $this->type;
$this->dummyObject = IcingaObject::createByType($type);
}
return $this->dummyObject;
}
public function prepareQuery()
{
$type = $this->type;
$table = $this->getDummyObject()->getTableName();
$columns = [
'id' => 'o.id',
'object_name' => 'o.object_name',
@ -159,13 +205,13 @@ class ApplyRulesTable extends ZfQueryBasedTable
'assign_filter' => 'o.assign_filter',
];
$query = $this->db()->select()->from(
['o' => "icinga_$type"],
['o' => $table],
$columns
)->where(
"object_type = 'apply'"
)->order('o.object_name');
if ($type === 'service') {
if ($this->type === 'service') {
$query->where('service_set_id IS NULL');
}

View File

@ -0,0 +1,67 @@
<?php
namespace Icinga\Module\Director\Web\Table;
use Icinga\Module\Director\Objects\IcingaScheduledDowntime;
use dipl\Html\Link;
use dipl\Web\Table\ZfQueryBasedTable;
class IcingaScheduledDowntimeRangeTable extends ZfQueryBasedTable
{
/** @var IcingaScheduledDowntime */
protected $downtime;
protected $searchColumns = [
'range_key',
'range_value',
];
/**
* @param IcingaScheduledDowntime $downtime
* @return static
*/
public static function load(IcingaScheduledDowntime $downtime)
{
$table = new static($downtime->getConnection());
$table->downtime = $downtime;
$table->getAttributes()->set('data-base-target', '_self');
return $table;
}
public function renderRow($row)
{
return $this::row([
Link::create(
$row->range_key,
'director/scheduled-downtime/ranges',
[
'name' => $this->downtime->getObjectName(),
'range' => $row->range_key,
'range_type' => 'include'
]
),
$row->range_value
]);
}
public function getColumnsToBeRendered()
{
return [
$this->translate('Day(s)'),
$this->translate('Timeperiods'),
];
}
public function prepareQuery()
{
return $this->db()->select()->from(
['r' => 'icinga_scheduled_downtime_range'],
[
'scheduled_downtime_id' => 'r.scheduled_downtime_id',
'range_key' => 'r.range_key',
'range_value' => 'r.range_value',
]
)->where('r.scheduled_downtime_id = ?', $this->downtime->id);
}
}

View File

@ -33,6 +33,8 @@ class ObjectsTable extends ZfQueryBasedTable
protected $type;
protected $baseObjectUrl;
/** @var IcingaObject */
protected $dummyObject;
@ -62,6 +64,17 @@ class ObjectsTable extends ZfQueryBasedTable
return $this->type;
}
/**
* @param string $url
* @return $this
*/
public function setBaseObjectUrl($url)
{
$this->baseObjectUrl = $url;
return $this;
}
/**
* @return Auth
*/
@ -119,7 +132,7 @@ class ObjectsTable extends ZfQueryBasedTable
protected function renderObjectNameColumn($row)
{
$type = $this->getType();
$type = $this->baseObjectUrl;
$url = Url::fromPath("director/${type}", [
'name' => $row->object_name
]);

View File

@ -54,7 +54,7 @@ class TemplatesTable extends ZfQueryBasedTable implements FilterableByUsage
public function renderRow($row)
{
$name = $row->object_name;
$type = $this->getType();
$type = str_replace('_', '-', $this->getType());
$caption = $row->is_used === 'y' ? $name : [
$name,
Html::tag(

View File

@ -117,7 +117,7 @@ class ObjectTabs extends Tabs
if ($object->supportsRanges()) {
$this->add('ranges', [
'url' => 'director/timeperiod/ranges',
'url' => "director/${type}/ranges",
'urlParams' => $params,
'label' => $this->translate('Ranges')
]);

View File

@ -11,14 +11,14 @@ class ObjectsTabs extends Tabs
{
use TranslationHelper;
public function __construct($type, Auth $auth)
public function __construct($type, Auth $auth, $typeUrl)
{
$object = IcingaObject::createByType($type);
if ($object->isGroup()) {
$object = IcingaObject::createByType(substr($type, 0, -5));
$object = IcingaObject::createByType(substr($typeUrl, 0, -5));
}
$plType = strtolower(preg_replace('/cys$/', 'cies', $type . 's'));
$plType = strtolower(preg_replace('/cys$/', 'cies', $typeUrl . 's'));
if ($auth->hasPermission("director/${plType}")) {
$this->add('index', array(
'url' => sprintf('director/%s', $plType),
@ -56,7 +56,7 @@ class ObjectsTabs extends Tabs
if ($object->supportsGroups()) {
$this->add('groups', array(
'url' => sprintf('director/%sgroups', $type),
'url' => sprintf('director/%sgroups', $typeUrl),
'label' => $this->translate('Groups')
));
}
@ -65,12 +65,12 @@ class ObjectsTabs extends Tabs
if ($auth->hasPermission('director/admin')) {
if ($object->supportsChoices()) {
$this->add('choices', array(
'url' => sprintf('director/templatechoices/%s', $type),
'url' => sprintf('director/templatechoices/%s', $typeUrl),
'label' => $this->translate('Choices')
));
}
}
if ($object->supportsSets() && $auth->hasPermission("director/${type}sets")) {
if ($object->supportsSets() && $auth->hasPermission("director/${typeUrl}sets")) {
$this->add('sets', array(
'url' => sprintf('director/%s/sets', $plType),
'label' => $this->translate('Sets')

View File

@ -504,12 +504,15 @@ class ActivityLogInfo extends HtmlDocument
protected function getLinkToObject()
{
// TODO: This logic is redundant and should be centralized
$entry = $this->entry;
$name = $entry->object_name;
$controller = preg_replace('/^icinga_/', '', $entry->object_type);
if ($controller === 'service_set') {
$controller = 'serviceset';
} elseif ($controller === 'scheduled_downtime') {
$controller = 'scheduled-downtime';
}
return Link::create(

View File

@ -41,10 +41,18 @@ abstract class BaseFormElement extends BaseHtmlElement
/**
* Link constructor.
* @param $name
* @param $value
* @param \dipl\Html\Attributes|array|null $attributes
*/
public function __construct($name, $attributes = null)
{
$this->registerAttributeCallbacks();
if ($attributes !== null) {
$this->addAttributes($attributes);
}
$this->setName($name);
}
protected function registerAttributeCallbacks()
{
$this->getAttributes()
->registerAttributeCallback('label', [$this, 'getNoAttribute'], [$this, 'setLabel'])
@ -53,10 +61,6 @@ abstract class BaseFormElement extends BaseHtmlElement
->registerAttributeCallback('description', [$this, 'getNoAttribute'], [$this, 'setDescription'])
->registerAttributeCallback('validators', null, [$this, 'setValidators'])
->registerAttributeCallback('required', [$this, 'getRequiredAttribute'], [$this, 'setRequired']);
if ($attributes !== null) {
$this->addAttributes($attributes);
}
$this->setName($name);
}
/**

View File

@ -0,0 +1,58 @@
CREATE TABLE icinga_scheduled_downtime (
id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
object_name VARCHAR(255) NOT NULL,
zone_id INT(10) UNSIGNED 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,
assign_filter TEXT DEFAULT NULL,
author VARCHAR(255) DEFAULT NULL,
comment TEXT DEFAULT NULL,
fixed ENUM('y', 'n') DEFAULT NULL,
duration INT(10) UNSIGNED DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE INDEX object_name (object_name),
CONSTRAINT icinga_scheduled_downtime_zone
FOREIGN KEY zone (zone_id)
REFERENCES icinga_zone (id)
ON DELETE RESTRICT
ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE icinga_scheduled_downtime_inheritance (
scheduled_downtime_id INT(10) UNSIGNED NOT NULL,
parent_scheduled_downtime_id INT(10) UNSIGNED NOT NULL,
weight MEDIUMINT UNSIGNED DEFAULT NULL,
PRIMARY KEY (scheduled_downtime_id, parent_scheduled_downtime_id),
UNIQUE KEY unique_order (scheduled_downtime_id, weight),
CONSTRAINT icinga_scheduled_downtime_inheritance_downtime
FOREIGN KEY host (scheduled_downtime_id)
REFERENCES icinga_scheduled_downtime (id)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT icinga_scheduled_downtime_inheritance_parent_downtime
FOREIGN KEY host (parent_scheduled_downtime_id)
REFERENCES icinga_scheduled_downtime (id)
ON DELETE RESTRICT
ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE icinga_scheduled_downtime_range (
scheduled_downtime_id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
range_key VARCHAR(255) NOT NULL COMMENT 'monday, ...',
range_value VARCHAR(255) NOT NULL COMMENT '00:00-24:00, ...',
range_type ENUM('include', 'exclude') NOT NULL DEFAULT 'include'
COMMENT 'include -> ranges {}, exclude ranges_ignore {} - not yet',
merge_behaviour ENUM('set', 'add', 'substract') NOT NULL DEFAULT 'set'
COMMENT 'set -> = {}, add -> += {}, substract -> -= {}',
PRIMARY KEY (scheduled_downtime_id, range_type, range_key),
CONSTRAINT icinga_scheduled_downtime_range_downtime
FOREIGN KEY scheduled_downtime (scheduled_downtime_id)
REFERENCES icinga_scheduled_downtime (id)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO director_schema_migration
(schema_version, migration_time)
VALUES (161, NOW());

View File

@ -1786,6 +1786,61 @@ CREATE TABLE icinga_timeperiod_exclude (
ON UPDATE CASCADE
);
CREATE TABLE icinga_scheduled_downtime (
id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
object_name VARCHAR(255) NOT NULL,
zone_id INT(10) UNSIGNED 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,
assign_filter TEXT DEFAULT NULL,
author VARCHAR(255) DEFAULT NULL,
comment TEXT DEFAULT NULL,
fixed ENUM('y', 'n') DEFAULT NULL,
duration INT(10) UNSIGNED DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE INDEX object_name (object_name),
CONSTRAINT icinga_scheduled_downtime_zone
FOREIGN KEY zone (zone_id)
REFERENCES icinga_zone (id)
ON DELETE RESTRICT
ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE icinga_scheduled_downtime_inheritance (
scheduled_downtime_id INT(10) UNSIGNED NOT NULL,
parent_scheduled_downtime_id INT(10) UNSIGNED NOT NULL,
weight MEDIUMINT UNSIGNED DEFAULT NULL,
PRIMARY KEY (scheduled_downtime_id, parent_scheduled_downtime_id),
UNIQUE KEY unique_order (scheduled_downtime_id, weight),
CONSTRAINT icinga_scheduled_downtime_inheritance_downtime
FOREIGN KEY host (scheduled_downtime_id)
REFERENCES icinga_scheduled_downtime (id)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT icinga_scheduled_downtime_inheritance_parent_downtime
FOREIGN KEY host (parent_scheduled_downtime_id)
REFERENCES icinga_scheduled_downtime (id)
ON DELETE RESTRICT
ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE icinga_scheduled_downtime_range (
scheduled_downtime_id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
range_key VARCHAR(255) NOT NULL COMMENT 'monday, ...',
range_value VARCHAR(255) NOT NULL COMMENT '00:00-24:00, ...',
range_type ENUM('include', 'exclude') NOT NULL DEFAULT 'include'
COMMENT 'include -> ranges {}, exclude ranges_ignore {} - not yet',
merge_behaviour ENUM('set', 'add', 'substract') NOT NULL DEFAULT 'set'
COMMENT 'set -> = {}, add -> += {}, substract -> -= {}',
PRIMARY KEY (scheduled_downtime_id, range_type, range_key),
CONSTRAINT icinga_scheduled_downtime_range_downtime
FOREIGN KEY scheduled_downtime (scheduled_downtime_id)
REFERENCES icinga_scheduled_downtime (id)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO director_schema_migration
(schema_version, migration_time)
VALUES (160, NOW());
VALUES (161, NOW());

View File

@ -0,0 +1,70 @@
CREATE TABLE icinga_scheduled_downtime (
id serial,
object_name character varying(255) NOT NULL,
zone_id integer DEFAULT NULL,
object_type enum_object_type_all NOT NULL,
disabled enum_boolean NOT NULL DEFAULT 'n',
apply_to enum_host_service NULL DEFAULT NULL,
assign_filter text DEFAULT NULL,
author character varying(255) DEFAULT NULL,
comment text DEFAULT NULL,
fixed enum_boolean DEFAULT NULL,
duration int DEFAULT NULL,
PRIMARY KEY (id),
CONSTRAINT icinga_scheduled_downtime_zone
FOREIGN KEY (zone_id)
REFERENCES icinga_zone (id)
ON DELETE RESTRICT
ON UPDATE CASCADE
);
CREATE UNIQUE INDEX scheduled_downtime_object_name ON icinga_scheduled_downtime (object_name);
CREATE INDEX scheduled_downtime_zone ON icinga_scheduled_downtime (zone_id);
CREATE TABLE icinga_scheduled_downtime_inheritance (
scheduled_downtime_id integer NOT NULL,
parent_scheduled_downtime_id integer NOT NULL,
weight integer DEFAULT NULL,
PRIMARY KEY (scheduled_downtime_id, parent_scheduled_downtime_id),
CONSTRAINT icinga_scheduled_downtime_inheritance_scheduled_downtime
FOREIGN KEY (scheduled_downtime_id)
REFERENCES icinga_scheduled_downtime (id)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT icinga_scheduled_downtime_inheritance_parent_scheduled_downtime
FOREIGN KEY (parent_scheduled_downtime_id)
REFERENCES icinga_scheduled_downtime (id)
ON DELETE RESTRICT
ON UPDATE CASCADE
);
CREATE UNIQUE INDEX scheduled_downtime_inheritance_unique_order ON icinga_scheduled_downtime_inheritance (scheduled_downtime_id, weight);
CREATE INDEX scheduled_downtime_inheritance_scheduled_downtime ON icinga_scheduled_downtime_inheritance (scheduled_downtime_id);
CREATE INDEX scheduled_downtime_inheritance_scheduled_downtime_parent ON icinga_scheduled_downtime_inheritance (parent_scheduled_downtime_id);
CREATE TABLE icinga_scheduled_downtime_range (
scheduled_downtime_id serial,
range_key character varying(255) NOT NULL,
range_value character varying(255) NOT NULL,
range_type enum_timeperiod_range_type NOT NULL DEFAULT 'include',
merge_behaviour enum_merge_behaviour NOT NULL DEFAULT 'set',
PRIMARY KEY (scheduled_downtime_id, range_type, range_key),
CONSTRAINT icinga_scheduled_downtime_range_scheduled_downtime
FOREIGN KEY (scheduled_downtime_id)
REFERENCES icinga_scheduled_downtime (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE INDEX scheduled_downtime_range_scheduled_downtime ON icinga_scheduled_downtime_range (scheduled_downtime_id);
COMMENT ON COLUMN icinga_scheduled_downtime_range.range_key IS 'monday, ...';
COMMENT ON COLUMN icinga_scheduled_downtime_range.range_value IS '00:00-24:00, ...';
COMMENT ON COLUMN icinga_scheduled_downtime_range.range_type IS 'include -> ranges {}, exclude ranges_ignore {} - not yet';
COMMENT ON COLUMN icinga_scheduled_downtime_range.merge_behaviour IS 'set -> = {}, add -> += {}, substract -> -= {}';
INSERT INTO director_schema_migration
(schema_version, migration_time)
VALUES (161, NOW());

View File

@ -2085,6 +2085,74 @@ CREATE TABLE icinga_timeperiod_exclude (
ON UPDATE CASCADE
);
CREATE TABLE icinga_scheduled_downtime (
id serial,
object_name character varying(255) NOT NULL,
zone_id integer DEFAULT NULL,
object_type enum_object_type_all NOT NULL,
disabled enum_boolean NOT NULL DEFAULT 'n',
apply_to enum_host_service NULL DEFAULT NULL,
assign_filter text DEFAULT NULL,
author character varying(255) DEFAULT NULL,
comment text DEFAULT NULL,
fixed enum_boolean DEFAULT NULL,
duration int DEFAULT NULL,
PRIMARY KEY (id),
CONSTRAINT icinga_scheduled_downtime_zone
FOREIGN KEY (zone_id)
REFERENCES icinga_zone (id)
ON DELETE RESTRICT
ON UPDATE CASCADE
);
CREATE UNIQUE INDEX scheduled_downtime_object_name ON icinga_scheduled_downtime (object_name);
CREATE INDEX scheduled_downtime_zone ON icinga_scheduled_downtime (zone_id);
CREATE TABLE icinga_scheduled_downtime_inheritance (
scheduled_downtime_id integer NOT NULL,
parent_scheduled_downtime_id integer NOT NULL,
weight integer DEFAULT NULL,
PRIMARY KEY (scheduled_downtime_id, parent_scheduled_downtime_id),
CONSTRAINT icinga_scheduled_downtime_inheritance_scheduled_downtime
FOREIGN KEY (scheduled_downtime_id)
REFERENCES icinga_scheduled_downtime (id)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT icinga_scheduled_downtime_inheritance_parent_scheduled_downtime
FOREIGN KEY (parent_scheduled_downtime_id)
REFERENCES icinga_scheduled_downtime (id)
ON DELETE RESTRICT
ON UPDATE CASCADE
);
CREATE UNIQUE INDEX scheduled_downtime_inheritance_unique_order ON icinga_scheduled_downtime_inheritance (scheduled_downtime_id, weight);
CREATE INDEX scheduled_downtime_inheritance_scheduled_downtime ON icinga_scheduled_downtime_inheritance (scheduled_downtime_id);
CREATE INDEX scheduled_downtime_inheritance_scheduled_downtime_parent ON icinga_scheduled_downtime_inheritance (parent_scheduled_downtime_id);
CREATE TABLE icinga_scheduled_downtime_range (
scheduled_downtime_id serial,
range_key character varying(255) NOT NULL,
range_value character varying(255) NOT NULL,
range_type enum_timeperiod_range_type NOT NULL DEFAULT 'include',
merge_behaviour enum_merge_behaviour NOT NULL DEFAULT 'set',
PRIMARY KEY (scheduled_downtime_id, range_type, range_key),
CONSTRAINT icinga_scheduled_downtime_range_scheduled_downtime
FOREIGN KEY (scheduled_downtime_id)
REFERENCES icinga_scheduled_downtime (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE INDEX scheduled_downtime_range_scheduled_downtime ON icinga_scheduled_downtime_range (scheduled_downtime_id);
COMMENT ON COLUMN icinga_scheduled_downtime_range.range_key IS 'monday, ...';
COMMENT ON COLUMN icinga_scheduled_downtime_range.range_value IS '00:00-24:00, ...';
COMMENT ON COLUMN icinga_scheduled_downtime_range.range_type IS 'include -> ranges {}, exclude ranges_ignore {} - not yet';
COMMENT ON COLUMN icinga_scheduled_downtime_range.merge_behaviour IS 'set -> = {}, add -> += {}, substract -> -= {}';
INSERT INTO director_schema_migration
(schema_version, migration_time)
VALUES (160, NOW());
VALUES (161, NOW());