Merge pull request #1639 from Icinga/feature/timeperiod-include-exclude

Add support for Timeperiod include/exclude
This commit is contained in:
Thomas Gelf 2018-09-17 09:10:59 +02:00 committed by GitHub
commit bf72c81062
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 388 additions and 25 deletions

View File

@ -52,8 +52,54 @@ class IcingaTimePeriodForm extends DirectorObjectForm
$this->addHidden('update_method', 'LegacyTimePeriod');
}
$this->addIncludeExclude();
$this->addImportsElement();
$this->setButtons();
}
protected function addIncludeExclude()
{
$periods = [];
foreach ($this->db->enumTimeperiods() as $id => $period) {
if ($this->object === null || $this->object->get('object_name') !== $period) {
$periods[$period] = $period;
}
}
if (empty($periods)) {
return;
}
$this->addElement(
'extensibleSet',
'includes',
array(
'label' => $this->translate('Include period'),
'description' => $this->translate(
'Include other time periods into this.'
),
'multiOptions' => $this->optionalEnum($periods),
)
);
$this->addElement(
'extensibleSet',
'excludes',
array(
'label' => $this->translate('Exclude period'),
'description' => $this->translate(
'Exclude other time periods from this.'
),
'multiOptions' => $this->optionalEnum($periods),
)
);
$this->optionalBoolean(
'prefer_includes',
$this->translate('Prefer includes'),
$this->translate('Whether to prefer timeperiods includes or excludes. Default to true.')
);
}
}

View File

@ -2176,6 +2176,19 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
return $out;
}
/**
* @return string
*/
protected function renderLegacyMultiRelations()
{
$out = '';
foreach ($this->loadAllMultiRelations() as $rel) {
$out .= $rel->toLegacyConfigString();
}
return $out;
}
/**
* @return string
*/
@ -2393,11 +2406,11 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
$this->renderLegacyObjectHeader(),
$this->renderLegacyImports(),
$this->renderLegacyProperties(),
$this->renderLegacyRanges(),
//$this->renderArguments(),
//$this->renderRelatedSets(),
$this->renderLegacyGroups(),
//$this->renderMultiRelations(),
$this->renderLegacyMultiRelations(),
$this->renderLegacyRanges(),
$this->renderLegacyCustomExtensions(),
$this->renderLegacyCustomVars(),
$this->renderLegacySuffix()
@ -2442,11 +2455,11 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
$this->renderPrioritizedProperties(),
$this->renderImports(),
$this->renderProperties(),
$this->renderRanges(),
$this->renderArguments(),
$this->renderRelatedSets(),
$this->renderGroups(),
$this->renderMultiRelations(),
$this->renderRanges(),
$this->renderCustomExtensions(),
$this->renderCustomVars(),
$this->renderSuffix()

View File

@ -28,17 +28,28 @@ class IcingaObjectMultiRelations implements Iterator, Countable, IcingaConfigRen
protected $relationIdColumn;
protected $relatedShortName;
protected $legacyPropertyName;
private $position = 0;
private $db;
protected $idx = array();
public function __construct(IcingaObject $object, $propertyName, $relatedObjectClass)
public function __construct(IcingaObject $object, $propertyName, $config)
{
$this->object = $object;
$this->propertyName = $propertyName;
$this->relatedObjectClass = $relatedObjectClass;
if (is_object($config) || is_array($config)) {
foreach ($config as $k => $v) {
$this->$k = $v;
}
} else {
$this->relatedObjectClass = $config;
}
}
public function getObjects()
@ -238,15 +249,26 @@ class IcingaObjectMultiRelations implements Iterator, Countable, IcingaConfigRen
return $this->propertyName;
}
protected function getRelatedShortName()
{
if ($this->relatedShortName === null) {
/** @var IcingaObject $class */
$class = $this->getRelatedClassName();
$this->relatedShortName = $class::create()->getShortTableName();
}
return $this->relatedShortName;
}
protected function getTableName()
{
$class = $this->getRelatedClassName();
return $this->object->getTableName() . '_' . $class::create()->getShortTableName();
return $this->object->getTableName() . '_' . $this->getRelatedShortName();
}
protected function getRelatedTableName()
{
if ($this->relatedTableName === null) {
/** @var IcingaObject $class */
$class = $this->getRelatedClassName();
$this->relatedTableName = $class::create()->getTableName();
}
@ -257,8 +279,7 @@ class IcingaObjectMultiRelations implements Iterator, Countable, IcingaConfigRen
protected function getRelationIdColumn()
{
if ($this->relationIdColumn === null) {
$class = $this->getRelatedClassName();
$this->relationIdColumn = $class::create()->getShortTableName();
$this->relationIdColumn = $this->getRelatedShortName();
}
return $this->relationIdColumn;
@ -420,6 +441,11 @@ class IcingaObjectMultiRelations implements Iterator, Countable, IcingaConfigRen
return '';
}
return c1::renderKeyValue($this->propertyName, c1::renderArray($relations));
if ($this->legacyPropertyName === null) {
return ' # not supported in legacy: ' .
c1::renderKeyValue($this->propertyName, c1::renderArray($relations), '');
}
return c1::renderKeyValue($this->legacyPropertyName, c1::renderArray($relations));
}
}

View File

@ -14,10 +14,15 @@ class IcingaTimePeriod extends IcingaObject
'object_name' => null,
'object_type' => null,
'disabled' => 'n',
'prefer_includes' => null,
'display_name' => null,
'update_method' => null,
);
protected $booleans = [
'prefer_includes' => 'prefer_includes',
];
protected $supportsImports = true;
protected $supportsRanges = true;
@ -28,6 +33,18 @@ class IcingaTimePeriod extends IcingaObject
'zone' => 'IcingaZone',
);
protected $multiRelations = [
'includes' => [
'relatedObjectClass' => 'IcingaTimeperiod',
'relatedShortName' => 'include',
],
'excludes' => [
'relatedObjectClass' => 'IcingaTimeperiod',
'relatedShortName' => 'exclude',
'legacyPropertyName' => 'exclude'
],
];
/**
* Render update property
*
@ -48,15 +65,64 @@ class IcingaTimePeriod extends IcingaObject
. ' import "legacy-timeperiod"' . "\n";
}
protected function checkPeriodInRange($now, $name = null)
{
if ($name !== null) {
$period = static::load($name, $this->connection);
} else {
$period = $this;
}
foreach ($period->ranges()->getRanges() as $range) {
if ($range->isActive($now)) {
return true;
}
}
return false;
}
public function isActive($now = null)
{
if ($now === null) {
$now = time();
}
foreach ($this->ranges()->getRanges() as $range) {
if ($range->isActive($now)) {
$preferIncludes = $this->get('prefer_includes') !== 'n';
$active = $this->checkPeriodInRange($now);
$included = false;
$excluded = false;
$variants = [
'includes' => &$included,
'excludes' => &$excluded
];
foreach ($variants as $key => &$var) {
foreach ($this->get($key) as $name) {
if ($this->checkPeriodInRange($now, $name)) {
$var = true;
break;
}
}
}
if ($preferIncludes) {
if ($included) {
return true;
} elseif ($excluded) {
return false;
} else {
return $active;
}
} else {
if ($excluded) {
return false;
} elseif ($included) {
return true;
} else {
return $active;
}
}

View File

@ -0,0 +1,38 @@
ALTER TABLE icinga_timeperiod
ADD COLUMN prefer_includes ENUM('y', 'n') DEFAULT NULL;
CREATE TABLE icinga_timeperiod_include (
timeperiod_id INT(10) UNSIGNED NOT NULL,
include_id INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (timeperiod_id, include_id),
CONSTRAINT icinga_timeperiod_include
FOREIGN KEY timeperiod (include_id)
REFERENCES icinga_timeperiod (id)
ON DELETE RESTRICT
ON UPDATE RESTRICT,
CONSTRAINT icinga_timeperiod_include_timeperiod
FOREIGN KEY include (timeperiod_id)
REFERENCES icinga_timeperiod (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE icinga_timeperiod_exclude (
timeperiod_id INT(10) UNSIGNED NOT NULL,
exclude_id INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (timeperiod_id, exclude_id),
CONSTRAINT icinga_timeperiod_exclude
FOREIGN KEY timeperiod (exclude_id)
REFERENCES icinga_timeperiod (id)
ON DELETE RESTRICT
ON UPDATE RESTRICT,
CONSTRAINT icinga_timeperiod_exclude_timeperiod
FOREIGN KEY exclude (timeperiod_id)
REFERENCES icinga_timeperiod (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
INSERT INTO director_schema_migration
(schema_version, migration_time)
VALUES (151, NOW());

View File

@ -197,6 +197,7 @@ CREATE TABLE icinga_timeperiod (
zone_id INT(10) UNSIGNED DEFAULT NULL,
object_type ENUM('object', 'template') NOT NULL,
disabled ENUM('y', 'n') NOT NULL DEFAULT 'n',
prefer_includes ENUM('y', 'n') DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE INDEX object_name (object_name, zone_id),
CONSTRAINT icinga_timeperiod_zone
@ -1695,6 +1696,38 @@ CREATE TABLE icinga_dependency_states_set (
ON UPDATE CASCADE
) ENGINE=InnoDB;
CREATE TABLE icinga_timeperiod_include (
timeperiod_id INT(10) UNSIGNED NOT NULL,
include_id INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (timeperiod_id, include_id),
CONSTRAINT icinga_timeperiod_include
FOREIGN KEY timeperiod (include_id)
REFERENCES icinga_timeperiod (id)
ON DELETE RESTRICT
ON UPDATE RESTRICT,
CONSTRAINT icinga_timeperiod_include_timeperiod
FOREIGN KEY include (timeperiod_id)
REFERENCES icinga_timeperiod (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE icinga_timeperiod_exclude (
timeperiod_id INT(10) UNSIGNED NOT NULL,
exclude_id INT(10) UNSIGNED NOT NULL,
PRIMARY KEY (timeperiod_id, exclude_id),
CONSTRAINT icinga_timeperiod_exclude
FOREIGN KEY timeperiod (exclude_id)
REFERENCES icinga_timeperiod (id)
ON DELETE RESTRICT
ON UPDATE RESTRICT,
CONSTRAINT icinga_timeperiod_exclude_timeperiod
FOREIGN KEY exclude (timeperiod_id)
REFERENCES icinga_timeperiod (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
INSERT INTO director_schema_migration
(schema_version, migration_time)
VALUES (150, NOW());
VALUES (151, NOW());

View File

@ -0,0 +1,38 @@
ALTER TABLE icinga_timeperiod
ADD COLUMN prefer_includes enum_boolean DEFAULT NULL;
CREATE TABLE icinga_timeperiod_include (
timeperiod_id integer NOT NULL,
include_id integer NOT NULL,
PRIMARY KEY (timeperiod_id, include_id),
CONSTRAINT icinga_timeperiod_timeperiod_include
FOREIGN KEY (include_id)
REFERENCES icinga_timeperiod (id)
ON DELETE RESTRICT
ON UPDATE CASCADE,
CONSTRAINT icinga_timeperiod_include
FOREIGN KEY (timeperiod_id)
REFERENCES icinga_timeperiod (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE icinga_timeperiod_exclude (
timeperiod_id integer NOT NULL,
exclude_id integer NOT NULL,
PRIMARY KEY (timeperiod_id, exclude_id),
CONSTRAINT icinga_timeperiod_timeperiod_exclude
FOREIGN KEY (exclude_id)
REFERENCES icinga_timeperiod (id)
ON DELETE RESTRICT
ON UPDATE CASCADE,
CONSTRAINT icinga_timeperiod_exclude
FOREIGN KEY (timeperiod_id)
REFERENCES icinga_timeperiod (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
INSERT INTO director_schema_migration
(schema_version, migration_time)
VALUES (151, NOW());

View File

@ -273,6 +273,7 @@ CREATE TABLE icinga_timeperiod (
zone_id integer DEFAULT NULL,
object_type enum_object_type_all NOT NULL,
disabled enum_boolean NOT NULL DEFAULT 'n',
prefer_includes enum_boolean DEFAULT NULL,
PRIMARY KEY (id),
CONSTRAINT icinga_timeperiod_zone
FOREIGN KEY (zone_id)
@ -1990,7 +1991,38 @@ CREATE TABLE icinga_dependency_states_set (
CREATE INDEX dependency_states_set_dependency ON icinga_dependency_states_set (dependency_id);
COMMENT ON COLUMN icinga_dependency_states_set.merge_behaviour IS 'override: = [], extend: += [], blacklist: -= []';
CREATE TABLE icinga_timeperiod_include (
timeperiod_id integer NOT NULL,
include_id integer NOT NULL,
PRIMARY KEY (timeperiod_id, include_id),
CONSTRAINT icinga_timeperiod_timeperiod_include
FOREIGN KEY (include_id)
REFERENCES icinga_timeperiod (id)
ON DELETE RESTRICT
ON UPDATE CASCADE,
CONSTRAINT icinga_timeperiod_include
FOREIGN KEY (timeperiod_id)
REFERENCES icinga_timeperiod (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE icinga_timeperiod_exclude (
timeperiod_id integer NOT NULL,
exclude_id integer NOT NULL,
PRIMARY KEY (timeperiod_id, exclude_id),
CONSTRAINT icinga_timeperiod_timeperiod_exclude
FOREIGN KEY (exclude_id)
REFERENCES icinga_timeperiod (id)
ON DELETE RESTRICT
ON UPDATE CASCADE,
CONSTRAINT icinga_timeperiod_exclude
FOREIGN KEY (timeperiod_id)
REFERENCES icinga_timeperiod (id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
INSERT INTO director_schema_migration
(schema_version, migration_time)
VALUES (150, NOW());
VALUES (151, NOW());

View File

@ -9,6 +9,8 @@ class IcingaTimePeriodTest extends BaseTestCase
{
protected $testPeriodName = '___TEST___timeperiod';
protected $createdNames = [];
public function testWhetherUpdatedTimeperiodsAreCorrectlyStored()
{
if ($this->skipForMissingDb()) {
@ -54,12 +56,15 @@ class IcingaTimePeriodTest extends BaseTestCase
);
}
protected function createTestPeriod()
protected function createTestPeriod($suffix = '', $testRanges = [])
{
$db = $this->getDb();
$name = $this->testPeriodName . $suffix;
$this->createdNames[] = $name;
$object = IcingaTimePeriod::create(
array(
'object_name' => $this->testPeriodName,
'object_name' => $name,
'object_type' => 'object'
),
$db
@ -67,11 +72,13 @@ class IcingaTimePeriodTest extends BaseTestCase
$object->store();
$ranges = $object->ranges();
$testRanges = array(
'monday' => '00:00-24:00',
'tuesday' => '00:00-24:00',
'wednesday' => '00:00-24:00',
);
if (empty($testRanges)) {
$testRanges = array(
'monday' => '00:00-24:00',
'tuesday' => '00:00-24:00',
'wednesday' => '00:00-24:00',
);
}
$ranges->set($testRanges);
$object->store();
@ -98,16 +105,80 @@ class IcingaTimePeriodTest extends BaseTestCase
$this->assertFalse($period->isActive(strtotime('2016-05-19 10:00:00')));
}
protected function loadTestPeriod()
public function testPeriodWithIncludes()
{
return IcingaTimePeriod::load($this->testPeriodName, $this->getDb());
$period = $this->createTestPeriod();
$include = $this->createTestPeriod('include', ['thursday' => '00:00-24:00']);
$period->set('includes', $include->object_name);
$period->store();
// Wednesday:
$this->assertTrue($period->isActive(strtotime('2016-05-18 10:00:00')));
// Thursday:
$this->assertTrue($period->isActive(strtotime('2016-05-19 10:00:00')));
}
public function testPeriodWithExcludes()
{
$period = $this->createTestPeriod();
$exclude = $this->createTestPeriod('exclude', ['wednesday' => '00:00-24:00']);
$period->set('excludes', $exclude->object_name);
$period->store();
// Wednesday:
$this->assertFalse($period->isActive(strtotime('2016-05-18 10:00:00')));
// Thursday:
$this->assertFalse($period->isActive(strtotime('2016-05-19 10:00:00')));
}
public function testPeriodPreferingIncludes()
{
$period = $this->createTestPeriod();
$include = $this->createTestPeriod('include', ['thursday' => '00:00-24:00']);
$exclude = $this->createTestPeriod('exclude', ['thursday' => '00:00-24:00']);
$period->set('includes', $include->object_name);
$period->set('excludes', $exclude->object_name);
$period->store();
// Wednesday:
$this->assertTrue($period->isActive(strtotime('2016-05-18 10:00:00')));
// Thursday:
$this->assertTrue($period->isActive(strtotime('2016-05-19 10:00:00')));
}
public function testPeriodPreferingExcludes()
{
$period = $this->createTestPeriod();
$include = $this->createTestPeriod('include', ['thursday' => '00:00-24:00']);
$exclude = $this->createTestPeriod('exclude', ['thursday' => '00:00-24:00']);
$period->set('prefer_includes', false);
$period->set('includes', $include->object_name);
$period->set('excludes', $exclude->object_name);
$period->store();
// Wednesday:
$this->assertTrue($period->isActive(strtotime('2016-05-18 10:00:00')));
// Thursday:
$this->assertFalse($period->isActive(strtotime('2016-05-19 10:00:00')));
}
protected function loadTestPeriod($suffix = '')
{
return IcingaTimePeriod::load($this->testPeriodName . $suffix, $this->getDb());
}
public function tearDown()
{
$db = $this->getDb();
if (IcingaTimePeriod::exists($this->testPeriodName, $db)) {
IcingaTimePeriod::load($this->testPeriodName, $db)->delete();
foreach ($this->createdNames as $name) {
if (IcingaTimePeriod::exists($name, $db)) {
IcingaTimePeriod::load($name, $db)->delete();
}
}
}
}