diff --git a/application/controllers/ScheduledDowntimeController.php b/application/controllers/ScheduledDowntimeController.php new file mode 100644 index 00000000..65ba2572 --- /dev/null +++ b/application/controllers/ScheduledDowntimeController.php @@ -0,0 +1,38 @@ +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'; + } +} diff --git a/application/controllers/ScheduledDowntimesController.php b/application/controllers/ScheduledDowntimesController.php new file mode 100644 index 00000000..9f1bcb82 --- /dev/null +++ b/application/controllers/ScheduledDowntimesController.php @@ -0,0 +1,37 @@ +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'; + } +} diff --git a/application/controllers/SuggestController.php b/application/controllers/SuggestController.php index e778357a..08de4781 100644 --- a/application/controllers/SuggestController.php +++ b/application/controllers/SuggestController.php @@ -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) diff --git a/application/forms/IcingaScheduledDowntimeForm.php b/application/forms/IcingaScheduledDowntimeForm.php new file mode 100644 index 00000000..e9231b6b --- /dev/null +++ b/application/forms/IcingaScheduledDowntimeForm.php @@ -0,0 +1,116 @@ +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() + ); + } +} diff --git a/application/forms/IcingaScheduledDowntimeRangeForm.php b/application/forms/IcingaScheduledDowntimeRangeForm.php new file mode 100644 index 00000000..aced5c0a --- /dev/null +++ b/application/forms/IcingaScheduledDowntimeRangeForm.php @@ -0,0 +1,110 @@ +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); + } +} diff --git a/library/Director/Dashboard/AlertsDashboard.php b/library/Director/Dashboard/AlertsDashboard.php index 78c4781d..447f74f2 100644 --- a/library/Director/Dashboard/AlertsDashboard.php +++ b/library/Director/Dashboard/AlertsDashboard.php @@ -8,6 +8,8 @@ class AlertsDashboard extends Dashboard 'Notifications', 'Users', 'Timeperiods', + 'DependencyObject', + 'ScheduledDowntimeApply', ); public function getTitle() diff --git a/library/Director/Dashboard/Dashlet/ScheduledDowntimeApplyDashlet.php b/library/Director/Dashboard/Dashlet/ScheduledDowntimeApplyDashlet.php new file mode 100644 index 00000000..8e39851e --- /dev/null +++ b/library/Director/Dashboard/Dashlet/ScheduledDowntimeApplyDashlet.php @@ -0,0 +1,20 @@ +translate('Scheduled Downtimes'); + } + + public function getUrl() + { + return 'director/scheduled-downtimes/applyrules'; + } +} diff --git a/library/Director/Dashboard/ObjectsDashboard.php b/library/Director/Dashboard/ObjectsDashboard.php index 0688d4ab..02c2a4b7 100644 --- a/library/Director/Dashboard/ObjectsDashboard.php +++ b/library/Director/Dashboard/ObjectsDashboard.php @@ -8,8 +8,6 @@ class ObjectsDashboard extends Dashboard 'HostObject', 'ServiceObject', 'CommandObject', - // 'Notifications', - 'DependencyObject', ); public function getTitle() diff --git a/library/Director/Db.php b/library/Director/Db.php index 3c779ea6..436aeded 100644 --- a/library/Director/Db.php +++ b/library/Director/Db.php @@ -457,6 +457,7 @@ class Db extends DbConnection 'usergroup', 'command', 'timeperiod', + 'scheduled_downtime', 'notification', 'apiuser', 'endpoint', diff --git a/library/Director/Db/IcingaObjectFilterHelper.php b/library/Director/Db/IcingaObjectFilterHelper.php index b5e262fb..2eef4068 100644 --- a/library/Director/Db/IcingaObjectFilterHelper.php +++ b/library/Director/Db/IcingaObjectFilterHelper.php @@ -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); diff --git a/library/Director/IcingaConfig/IcingaConfig.php b/library/Director/IcingaConfig/IcingaConfig.php index cb1253bd..07de66dd 100644 --- a/library/Director/IcingaConfig/IcingaConfig.php +++ b/library/Director/IcingaConfig/IcingaConfig.php @@ -470,6 +470,7 @@ class IcingaConfig ->createFileFromDb('user') ->createFileFromDb('notification') ->createFileFromDb('dependency') + ->createFileFromDb('scheduledDowntime') ; PrefetchCache::forget(); diff --git a/library/Director/Objects/IcingaObject.php b/library/Director/Objects/IcingaObject.php index 6f2b0301..073f67cb 100644 --- a/library/Director/Objects/IcingaObject.php +++ b/library/Director/Objects/IcingaObject.php @@ -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()); diff --git a/library/Director/Objects/IcingaScheduledDowntime.php b/library/Director/Objects/IcingaScheduledDowntime.php new file mode 100644 index 00000000..c8f59d54 --- /dev/null +++ b/library/Director/Objects/IcingaScheduledDowntime.php @@ -0,0 +1,108 @@ + 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; + } +} diff --git a/library/Director/Objects/IcingaScheduledDowntimeRange.php b/library/Director/Objects/IcingaScheduledDowntimeRange.php new file mode 100644 index 00000000..c2984e5a --- /dev/null +++ b/library/Director/Objects/IcingaScheduledDowntimeRange.php @@ -0,0 +1,88 @@ + 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; + } +} diff --git a/library/Director/Objects/IcingaScheduledDowntimeRanges.php b/library/Director/Objects/IcingaScheduledDowntimeRanges.php new file mode 100644 index 00000000..ba2ec0d3 --- /dev/null +++ b/library/Director/Objects/IcingaScheduledDowntimeRanges.php @@ -0,0 +1,297 @@ +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()); + } + } + } +} diff --git a/library/Director/Web/ActionBar/TemplateActionBar.php b/library/Director/Web/ActionBar/TemplateActionBar.php index 9278559b..5364c16e 100644 --- a/library/Director/Web/ActionBar/TemplateActionBar.php +++ b/library/Director/Web/ActionBar/TemplateActionBar.php @@ -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']; diff --git a/library/Director/Web/Controller/ObjectController.php b/library/Director/Web/Controller/ObjectController.php index 0dbcba34..75ef7d4d 100644 --- a/library/Director/Web/Controller/ObjectController.php +++ b/library/Director/Web/Controller/ObjectController.php @@ -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 + )); } } } diff --git a/library/Director/Web/Controller/ObjectsController.php b/library/Director/Web/Controller/ObjectsController.php index b056e58c..ec868a82 100644 --- a/library/Director/Web/Controller/ObjectsController.php +++ b/library/Director/Web/Controller/ObjectsController.php @@ -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 */ diff --git a/library/Director/Web/Form/DirectorObjectForm.php b/library/Director/Web/Form/DirectorObjectForm.php index 83447810..f5a6d1bf 100644 --- a/library/Director/Web/Form/DirectorObjectForm.php +++ b/library/Director/Web/Form/DirectorObjectForm.php @@ -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(); diff --git a/library/Director/Web/Form/QuickForm.php b/library/Director/Web/Form/QuickForm.php index 83bf5cf8..bad222d1 100644 --- a/library/Director/Web/Form/QuickForm.php +++ b/library/Director/Web/Form/QuickForm.php @@ -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; diff --git a/library/Director/Web/Table/ApplyRulesTable.php b/library/Director/Web/Table/ApplyRulesTable.php index 01dc7cf9..18131847 100644 --- a/library/Director/Web/Table/ApplyRulesTable.php +++ b/library/Director/Web/Table/ApplyRulesTable.php @@ -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'); } diff --git a/library/Director/Web/Table/IcingaScheduledDowntimeRangeTable.php b/library/Director/Web/Table/IcingaScheduledDowntimeRangeTable.php new file mode 100644 index 00000000..5edd5d87 --- /dev/null +++ b/library/Director/Web/Table/IcingaScheduledDowntimeRangeTable.php @@ -0,0 +1,67 @@ +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); + } +} diff --git a/library/Director/Web/Table/ObjectsTable.php b/library/Director/Web/Table/ObjectsTable.php index bb49c162..4d20209e 100644 --- a/library/Director/Web/Table/ObjectsTable.php +++ b/library/Director/Web/Table/ObjectsTable.php @@ -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 ]); diff --git a/library/Director/Web/Table/TemplatesTable.php b/library/Director/Web/Table/TemplatesTable.php index 8f94ced0..366790a9 100644 --- a/library/Director/Web/Table/TemplatesTable.php +++ b/library/Director/Web/Table/TemplatesTable.php @@ -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( diff --git a/library/Director/Web/Tabs/ObjectTabs.php b/library/Director/Web/Tabs/ObjectTabs.php index f17f01e6..8953ccf1 100644 --- a/library/Director/Web/Tabs/ObjectTabs.php +++ b/library/Director/Web/Tabs/ObjectTabs.php @@ -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') ]); diff --git a/library/Director/Web/Tabs/ObjectsTabs.php b/library/Director/Web/Tabs/ObjectsTabs.php index 7519eaee..017d042d 100644 --- a/library/Director/Web/Tabs/ObjectsTabs.php +++ b/library/Director/Web/Tabs/ObjectsTabs.php @@ -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') diff --git a/library/Director/Web/Widget/ActivityLogInfo.php b/library/Director/Web/Widget/ActivityLogInfo.php index abb548f0..4a8c651d 100644 --- a/library/Director/Web/Widget/ActivityLogInfo.php +++ b/library/Director/Web/Widget/ActivityLogInfo.php @@ -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( diff --git a/library/vendor/ipl/Html/FormElement/BaseFormElement.php b/library/vendor/ipl/Html/FormElement/BaseFormElement.php index db50241b..2db0efad 100644 --- a/library/vendor/ipl/Html/FormElement/BaseFormElement.php +++ b/library/vendor/ipl/Html/FormElement/BaseFormElement.php @@ -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); } /** diff --git a/schema/mysql-migrations/upgrade_161.sql b/schema/mysql-migrations/upgrade_161.sql new file mode 100644 index 00000000..f8134a4b --- /dev/null +++ b/schema/mysql-migrations/upgrade_161.sql @@ -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()); diff --git a/schema/mysql.sql b/schema/mysql.sql index 4b6f7e0b..1cdc064b 100644 --- a/schema/mysql.sql +++ b/schema/mysql.sql @@ -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()); diff --git a/schema/pgsql-migrations/upgrade_161.sql b/schema/pgsql-migrations/upgrade_161.sql new file mode 100644 index 00000000..b8618d97 --- /dev/null +++ b/schema/pgsql-migrations/upgrade_161.sql @@ -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()); diff --git a/schema/pgsql.sql b/schema/pgsql.sql index 09cb381e..c3073948 100644 --- a/schema/pgsql.sql +++ b/schema/pgsql.sql @@ -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());