Compare commits

..

13 Commits

Author SHA1 Message Date
raviks789
2423fad54c upgrade_188: Remove entries with duplicate checksums in director_activity_log
Update schema/mysql-migrations/upgrade_188.sql
2025-04-08 14:18:09 +02:00
Ravi Kumar Kempapura Srinivasa
2a4b78bfd2
Release 1.11.4 (#2969) 2025-03-26 11:24:49 +01:00
raviks789
38f3fe33c9
Release 1.11.4 2025-03-26 11:21:47 +01:00
Ravi Kumar Kempapura Srinivasa
a99a998540
Merge commit from fork
Apply relevant restrictions on REST url endpoints
2025-03-26 11:19:54 +01:00
Ravi Kumar Kempapura Srinivasa
cadac72f4c
Fix job behavior during summer and winter time change (#2954)
ref/IP/52977

`ts_last_attempt`, which is used in the callback function of
`React\EventLoop\LoopInterface::addPeriodicTimer`, had data type
timestamp and was saved as UTC time but retrieved as system time, and
during DST where the clock is moved forward or set back during DST, you
could expect time jumps in the retrieved `ts_last_attempt` value.

And the `React\EventLoop\LoopInterface::addPeriodicTimer` expects the
time source to be monotonous without any such time jumps. This causes
the jobs to be erratically scheduled after DST. And this happens mostly
when the `ts_last_attempt` saved time time falls in the skipped or time
range that was reset.

To avoid such time jumps the `ts_last_attempt` datatype is changed to
`BIGINT` and the values are saved as Unix time stamp.
2025-03-25 16:17:12 +01:00
raviks789
4c283a24c8
Fix: Support data type change of columns ts_last_attempt and ts_last_error from timestamp to bigint 2025-03-25 16:03:30 +01:00
raviks789
63dac34b0f
director_job: Change timestamp data types to integer 2025-03-25 16:03:26 +01:00
raviks789
5f4ad0f827 IcingaAddServiceForm: Check if service templates exist to add services 2025-03-25 16:01:29 +01:00
raviks789
b18cf4f80d Fix editing of custom variables for multi-selected objects 2025-03-25 15:56:11 +01:00
raviks789
42a47d6139 CustomVarRenderer: Fix illegal offset error if the value is an array 2025-03-25 15:53:09 +01:00
raviks789
778b25873c CustomVarRenderer: Fix custom var renderer for apply-for-rules 2025-03-25 15:49:30 +01:00
Mek Skëndo
2196de252d Fix MySQL8.4 nonstandard foreign keys deprecation
If you try to set up icinga director with the latest minor version of MySQL 8.4+, the schema creation will fail with confusing SQL error messages. This commit aims to fix that. https://dev.mysql.com/doc/refman/8.4/en/mysql-nutshell.html#mysql-nutshell-deprecations
2025-03-25 15:42:46 +01:00
raviks789
3fcb20178f
Apply correct restrictions to REST url endpoints
The following url end points should apply relevant restrictions to filter out objects and show correct HTTP status code:
- icingaweb2/director/service, if the host name is left out of the query (Example: icingaweb2/director/service?name=service-name)
- icingaweb2/directore/notification
- icingaweb2/director/serviceset
- icingaweb2/director/scheduled-downtime
2025-01-24 12:21:41 +01:00
23 changed files with 302 additions and 75 deletions

View File

@ -2,6 +2,7 @@
namespace Icinga\Module\Director\Controllers;
use Icinga\Exception\NotFoundError;
use Icinga\Module\Director\Web\Controller\ObjectController;
use Icinga\Module\Director\Objects\IcingaHost;
use Icinga\Module\Director\Objects\IcingaNotification;
@ -80,6 +81,10 @@ class NotificationController extends ObjectController
}
}
if (! $this->allowsObject($this->object)) {
throw new NotFoundError('No such object available');
}
return $this->object;
}
}

View File

@ -49,14 +49,6 @@ class ServiceController extends ObjectController
$this->host = $this->getOptionalRelatedObjectFromParams('host', 'host');
$this->set = $this->getOptionalRelatedObjectFromParams('service_set', 'set');
parent::init();
if ($this->object) {
if ($this->host === null) {
$this->host = $this->loadOptionalRelatedObject($this->object, 'host');
}
if ($this->set === null) {
$this->set = $this->loadOptionalRelatedObject($this->object, 'service_set');
}
}
$this->addOptionalHostTabs();
$this->addOptionalSetTabs();
}
@ -77,6 +69,20 @@ class ServiceController extends ObjectController
return null;
}
protected function loadOptionalObject(): void
{
parent::loadOptionalObject();
if ($this->object) {
if ($this->host === null) {
$this->host = $this->loadOptionalRelatedObject($this->object, 'host');
}
if ($this->set === null) {
$this->set = $this->loadOptionalRelatedObject($this->object, 'service_set');
}
}
}
protected function loadOptionalRelatedObject(IcingaObject $object, $relation)
{
$key = $object->getUnresolvedRelated($relation);

View File

@ -3,6 +3,7 @@
namespace Icinga\Module\Director\Controllers;
use Icinga\Data\Filter\Filter;
use Icinga\Exception\NotFoundError;
use Icinga\Module\Director\Forms\IcingaServiceSetForm;
use Icinga\Module\Director\Objects\IcingaHost;
use Icinga\Module\Director\Objects\IcingaServiceSet;
@ -136,6 +137,10 @@ class ServicesetController extends ObjectController
}
}
if (! $this->allowsObject($this->object)) {
throw new NotFoundError('No such object available');
}
return $this->object;
}
}

View File

@ -30,7 +30,13 @@ class IcingaAddServiceForm extends DirectorObjectForm
);
}
$this->addSingleImportElement();
$this->addSingleImportElement(true);
if (empty($this->enumServiceTemplates())) {
$this->setSubmitLabel(false);
return;
}
if (! ($imports = $this->getSentOrObjectValue('imports'))) {
$this->setSubmitLabel($this->translate('Next'));

View File

@ -51,6 +51,7 @@ class IcingaMultiEditForm extends DirectorObjectForm
$loader = new IcingaObjectFieldLoader($object);
$loader->prepareElements($this);
$loader->addFieldsToForm($this);
$this->varNameMap = $loader->getNameMap();
if ($form = $this->relatedForm) {
if ($form instanceof DirectorObjectForm) {
@ -237,10 +238,6 @@ class IcingaMultiEditForm extends DirectorObjectForm
$this->removeElement($key);
$label = $element->getLabel();
if ($this->isCustomVar($key)) {
$this->varNameMap[$key] = $label;
}
$group = $this->getDisplayGroupForElement($element);
$description = $element->getDescription();

View File

@ -41,7 +41,7 @@ and extract it to a folder named `director` in one of your Icinga Web module pat
You might want to use a script as follows for this task:
```shell
MODULE_VERSION="1.11.3"
MODULE_VERSION="1.11.4"
ICINGAWEB_MODULEPATH="/usr/share/icingaweb2/modules"
REPO_URL="https://github.com/icinga/icingaweb2-module-director"
TARGET_DIR="${ICINGAWEB_MODULEPATH}/director"
@ -60,7 +60,7 @@ Simply clone the repository in one of your Icinga web module path directories.
You might want to use a script as follows for this task:
```shell
MODULE_VERSION="1.11.3"
MODULE_VERSION="1.11.4"
ICINGAWEB_MODULEPATH="/usr/share/icingaweb2/modules"
REPO_URL="https://github.com/icinga/icingaweb2-module-director"
TARGET_DIR="${ICINGAWEB_MODULEPATH}/director"

View File

@ -4,6 +4,35 @@
Please make sure to always read our [Upgrading](05-Upgrading.md) documentation
before switching to a new version.
v1.11.4
-------
### Security
- Rest API endpoints accessible to restricted users ([GHSA-3233-ggc5-m3qg](https://github.com/Icinga/icingaweb2-module-director/security/advisories/GHSA-3233-ggc5-m3qg))
### UI
- Fix editing of custom variables for multi-selected objects [#2950](https://github.com/Icinga/icingaweb2-module-director/issues/2950)
- Fix: Check for the existence of service templates to add services [#1249](https://github.com/Icinga/icingaweb2-module-director/issues/1249)
### Import and Sync
- Fix erratic job behavior during summer and winter time change (no issue)
### Integration
- Fix custom variable renderer for service apply for rules (no issue)
- Fix custom variable renderer for array type data lists [#2960](https://github.com/Icinga/icingaweb2-module-director/issues/2960)
### Database Schema
- Fix MySQL 8.4 nonstandard foreign keys deprecation [#2885](https://github.com/Icinga/icingaweb2-module-director/issues/2885)
### Fixed Issues
You can find issues related to this release on our [roadmap](https://github.com/Icinga/icingaweb2-module-director/milestone/39?closed=1)
v1.11.3
-------

View File

@ -3,6 +3,7 @@
namespace Icinga\Module\Director\Objects;
use Icinga\Exception\NotFoundError;
use Icinga\Module\Director\Daemon\DaemonUtil;
use Icinga\Module\Director\Daemon\Logger;
use Icinga\Module\Director\Data\Db\DbObjectWithSettings;
use Icinga\Module\Director\Db;
@ -84,7 +85,8 @@ class DirectorJob extends DbObjectWithSettings implements ExportInterface, Insta
public function run()
{
$job = $this->getInstance();
$this->set('ts_last_attempt', date('Y-m-d H:i:s'));
$currentTimestamp = DaemonUtil::timestampWithMilliseconds();
$this->set('ts_last_attempt', $currentTimestamp);
try {
$job->run();
@ -92,7 +94,7 @@ class DirectorJob extends DbObjectWithSettings implements ExportInterface, Insta
$success = true;
} catch (Exception $e) {
Logger::error($e->getMessage());
$this->set('ts_last_error', date('Y-m-d H:i:s'));
$this->set('ts_last_error', $currentTimestamp);
$this->set('last_error_message', $e->getMessage());
$this->set('last_attempt_succeeded', 'n');
$success = false;
@ -127,8 +129,8 @@ class DirectorJob extends DbObjectWithSettings implements ExportInterface, Insta
}
return (
strtotime($this->get('ts_last_attempt')) + $this->get('run_interval') * 2
) < time();
$this->get('ts_last_attempt') + $this->get('run_interval') * 2 * 1000
) < DaemonUtil::timestampWithMilliseconds();
}
public function hasBeenDisabled()
@ -145,7 +147,9 @@ class DirectorJob extends DbObjectWithSettings implements ExportInterface, Insta
return $this->isWithinTimeperiod();
}
if (strtotime($this->get('ts_last_attempt')) + $this->get('run_interval') < time()) {
if (
$this->get('ts_last_attempt') + $this->get('run_interval') * 1000 < DaemonUtil::timestampWithMilliseconds()
) {
return $this->isWithinTimeperiod();
}

View File

@ -5,6 +5,7 @@ namespace Icinga\Module\Director\ProvidedHook\Icingadb;
use Icinga\Application\Config;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotFoundError;
use Icinga\Module\Director\CustomVariable\CustomVariableArray;
use Icinga\Module\Director\Daemon\Logger;
use Icinga\Module\Director\Db;
use Icinga\Module\Director\Db\AppliedServiceSetLoader;
@ -113,22 +114,42 @@ class CustomVarRenderer extends CustomVarRendererHook
$appliedFilterQuery = IcingaHostAppliedServicesTable::load($directorHostObj)->getQuery();
foreach ($appliedFilterQuery->fetchAll() as $appliedService) {
if ($appliedService->name === $serviceName) {
$query = $db->getDbAdapter()->select()->from('icinga_service')
->where('object_name = ?', $serviceName)
->where("object_type = 'apply'")
->where('assign_filter = ?', $appliedService->assign_filter);
if ($appliedService->apply_for === null) {
$isAppliedService = $appliedService->name === $serviceName;
} else {
/** @var ?CustomVariableArray $hostVar */
$hostVar = $directorHostObj->vars()->get((substr($appliedService->apply_for, 10)));
if ($hostVar) {
$appliedServiceName = $appliedService->name;
$appliedForServiceLookup = [];
foreach ($hostVar->getValue() as $val) {
$appliedForServiceLookup[$appliedServiceName . $val] = true;
}
$directorAppliedServices = IcingaService::loadAll(
$db,
$query,
'object_name'
);
$directorServiceObj = current($directorAppliedServices);
break;
$isAppliedService = isset($appliedForServiceLookup[$serviceName]);
} else {
$isAppliedService = false;
}
}
if (! $isAppliedService) {
continue;
}
$query = $db->getDbAdapter()->select()->from('icinga_service')
->where('object_name = ?', $appliedService->name)
->where("object_type = 'apply'")
->where('assign_filter = ?', $appliedService->assign_filter);
$directorAppliedServices = IcingaService::loadAll(
$db,
$query,
'object_name'
);
$directorServiceObj = current($directorAppliedServices);
break;
}
} elseif ($serviceOrigin[$i] === 'service-set') {
$templateResolver = new IcingaTemplateResolver($directorHostObj);
@ -253,7 +274,24 @@ class CustomVarRenderer extends CustomVarRendererHook
return '***';
}
if (isset($this->datalistMaps[$key][$value])) {
if (is_array($value)) {
$renderedValue = [];
foreach ($value as $v) {
if (is_string($v) && isset($this->datalistMaps[$key][$v])) {
$renderedValue[] = new HtmlElement(
'span',
Attributes::create(['title' => $this->datalistMaps[$key][$v] . " [$v]"]),
Text::create($this->datalistMaps[$key][$v])
);
} else {
$renderedValue[] = $v;
}
}
return $renderedValue;
}
if (is_string($value) && isset($this->datalistMaps[$key][$value])) {
return new HtmlElement(
'span',
Attributes::create(['title' => $this->datalistMaps[$key][$value] . " [$value]"]),

View File

@ -5,6 +5,7 @@ namespace Icinga\Module\Director\ProvidedHook\Monitoring;
use Icinga\Application\Config;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotFoundError;
use Icinga\Module\Director\CustomVariable\CustomVariableArray;
use Icinga\Module\Director\Daemon\Logger;
use Icinga\Module\Director\Db;
use Icinga\Module\Director\Db\AppliedServiceSetLoader;
@ -114,22 +115,42 @@ class CustomVarRenderer extends CustomVarRendererHook
$appliedFilterQuery = IcingaHostAppliedServicesTable::load($directorHostObj)->getQuery();
foreach ($appliedFilterQuery->fetchAll() as $appliedService) {
if ($appliedService->name === $serviceName) {
$query = $db->getDbAdapter()->select()->from('icinga_service')
->where('object_name = ?', $serviceName)
->where("object_type = 'apply'")
->where('assign_filter = ?', $appliedService->assign_filter);
if ($appliedService->apply_for === null) {
$isAppliedService = $appliedService->name === $serviceName;
} else {
/** @var ?CustomVariableArray $hostVar */
$hostVar = $directorHostObj->vars()->get((substr($appliedService->apply_for, 10)));
if ($hostVar) {
$appliedServiceName = $appliedService->name;
$appliedForServiceLookup = [];
foreach ($hostVar->getValue() as $val) {
$appliedForServiceLookup[$appliedServiceName . $val] = true;
}
$directorAppliedServices = IcingaService::loadAll(
$db,
$query,
'object_name'
);
$directorServiceObj = current($directorAppliedServices);
break;
$isAppliedService = isset($appliedForServiceLookup[$serviceName]);
} else {
$isAppliedService = false;
}
}
if (! $isAppliedService) {
continue;
}
$query = $db->getDbAdapter()->select()->from('icinga_service')
->where('object_name = ?', $appliedService->name)
->where("object_type = 'apply'")
->where('assign_filter = ?', $appliedService->assign_filter);
$directorAppliedServices = IcingaService::loadAll(
$db,
$query,
'object_name'
);
$directorServiceObj = current($directorAppliedServices);
break;
}
} elseif ($serviceOrigin[$i] === 'service-set') {
$templateResolver = new IcingaTemplateResolver($directorHostObj);
@ -254,7 +275,24 @@ class CustomVarRenderer extends CustomVarRendererHook
return '***';
}
if (isset($this->datalistMaps[$key][$value])) {
if (is_array($value)) {
$renderedValue = [];
foreach ($value as $v) {
if (is_string($v) && isset($this->datalistMaps[$key][$v])) {
$renderedValue[] = new HtmlElement(
'span',
Attributes::create(['title' => $this->datalistMaps[$key][$v] . " [$v]"]),
Text::create($this->datalistMaps[$key][$v])
);
} else {
$renderedValue[] = $v;
}
}
return $renderedValue;
}
if (is_string($value) && isset($this->datalistMaps[$key][$value])) {
return new HtmlElement(
'span',
Attributes::create(['title' => $this->datalistMaps[$key][$value] . " [$value]"]),

View File

@ -5,6 +5,7 @@ namespace Icinga\Module\Director\Restriction;
use gipfl\IcingaWeb2\Zf1\Db\FilterRenderer;
use Icinga\Authentication\Auth;
use Icinga\Data\Filter\Filter;
use Icinga\Data\Filter\FilterEqual;
use Icinga\Module\Director\Db;
use Icinga\Module\Director\Objects\IcingaObject;
use Zend_Db_Select as ZfSelect;
@ -30,7 +31,11 @@ class FilterByNameRestriction extends ObjectRestriction
protected function setNameForType($type)
{
$this->name = "director/{$type}/filter-by-name";
if ($type === 'service_set') {
$this->name = "director/{$type}/filter-by-name";
} else {
$this->name = "director/{$type}/apply/filter-by-name";
}
}
public function allows(IcingaObject $object)
@ -39,9 +44,9 @@ class FilterByNameRestriction extends ObjectRestriction
return true;
}
return $this->getFilter()->matches([
return $this->getFilter()->matches(
(object) ['object_name' => $object->getObjectName()]
]);
);
}
public function getFilter()

View File

@ -5,6 +5,7 @@ namespace Icinga\Module\Director\Web\Controller\Extension;
use Icinga\Authentication\Auth;
use Icinga\Module\Director\Db;
use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\Restriction\FilterByNameRestriction;
use Icinga\Module\Director\Restriction\HostgroupRestriction;
use Icinga\Module\Director\Restriction\ObjectRestriction;
@ -13,6 +14,9 @@ trait ObjectRestrictions
/** @var ObjectRestriction[] */
private $objectRestrictions;
/** @var IcingaObject */
private $dummyRestrictedObject;
/**
* @return ObjectRestriction[]
*/
@ -30,13 +34,27 @@ trait ObjectRestrictions
*/
protected function loadObjectRestrictions(Db $db, Auth $auth)
{
return [
new HostgroupRestriction($db, $auth)
];
$objectType = $this->dummyRestrictedObject->getShortTableName();
if (
($objectType === 'service' && $this->dummyRestrictedObject->isApplyRule())
|| $objectType === 'notification'
|| $objectType === 'service_set'
|| $objectType === 'scheduled_downtime'
) {
if ($objectType === 'scheduled_downtime') {
$objectType = 'scheduled-downtime';
}
return [new FilterByNameRestriction($db, $auth, $objectType)];
}
// If the object is host or host group load HostgroupRestriction
return [new HostgroupRestriction($db, $auth)];
}
public function allowsObject(IcingaObject $object)
{
$this->dummyRestrictedObject = $object;
foreach ($this->getObjectRestrictions() as $restriction) {
if (! $restriction->allows($object)) {
return false;

View File

@ -17,6 +17,7 @@ use Icinga\Module\Director\Objects\IcingaService;
use Icinga\Module\Director\RestApi\IcingaObjectsHandler;
use Icinga\Module\Director\Web\ActionBar\ObjectsActionBar;
use Icinga\Module\Director\Web\ActionBar\TemplateActionBar;
use Icinga\Module\Director\Web\Controller\Extension\ObjectRestrictions;
use Icinga\Module\Director\Web\Form\FormLoader;
use Icinga\Module\Director\Web\Table\ApplyRulesTable;
use Icinga\Module\Director\Web\Table\ObjectSetTable;
@ -33,6 +34,7 @@ use Ramsey\Uuid\Uuid;
abstract class ObjectsController extends ActionController
{
use BranchHelper;
use ObjectRestrictions;
protected $isApified = true;
@ -75,9 +77,13 @@ abstract class ObjectsController extends ActionController
$table = $this->getTable();
if (
$request->getControllerName() === 'services'
&& $host = $this->params->get('host')
&& $hostName = $this->params->get('host')
) {
$host = IcingaHost::load($host, $this->db());
$host = IcingaHost::load($hostName, $this->db());
if (! $this->allowsObject($host)) {
throw new NotFoundError(sprintf('Failed to load %s "%s"', $host->getTableName(), $hostName));
}
$table->getQuery()->where('o.host_id = ?', $host->get('id'));
}

View File

@ -60,6 +60,16 @@ class IcingaObjectFieldLoader
return $this;
}
/**
* Get element names to variable names map (Example: ['elName' => 'varName'])
*
* @return array
*/
public function getNameMap(): array
{
return $this->nameMap;
}
public function loadFieldsForMultipleObjects($objects)
{
$fields = array();

View File

@ -4,6 +4,7 @@ namespace Icinga\Module\Director\Web\Table;
use gipfl\IcingaWeb2\Link;
use gipfl\IcingaWeb2\Table\ZfQueryBasedTable;
use Icinga\Module\Director\Daemon\DaemonUtil;
class JobTable extends ZfQueryBasedTable
{
@ -37,11 +38,11 @@ class JobTable extends ZfQueryBasedTable
protected function getJobClasses($row)
{
if ($row->unixts_last_attempt === null) {
if ($row->ts_last_attempt === null) {
return 'pending';
}
if ($row->unixts_last_attempt + $row->run_interval < time()) {
if ($row->ts_last_attempt + $row->run_interval * 1000 < DaemonUtil::timestampWithMilliseconds()) {
return 'pending';
}
@ -73,7 +74,6 @@ class JobTable extends ZfQueryBasedTable
'run_interval' => 'j.run_interval',
'last_attempt_succeeded' => 'j.last_attempt_succeeded',
'ts_last_attempt' => 'j.ts_last_attempt',
'unixts_last_attempt' => 'UNIX_TIMESTAMP(j.ts_last_attempt)',
'ts_last_error' => 'j.ts_last_error',
'last_error_message' => 'j.last_error_message',
]

View File

@ -226,11 +226,14 @@ class ObjectsTable extends ZfQueryBasedTable
{
/** @var Db $db */
$db = $this->connection();
$dummyObject = $this->getDummyObject();
$type = $dummyObject->getShortTableName();
return [
new HostgroupRestriction($db, $this->auth),
new FilterByNameRestriction($db, $this->auth, $this->getDummyObject()->getShortTableName())
];
if ($dummyObject->isApplyRule()) {
return [new FilterByNameRestriction($db, $this->auth, $type)];
} else {
return [new HostgroupRestriction($db, $this->auth)];
}
}
/**

View File

@ -45,7 +45,7 @@ class JobDetails extends HtmlDocument
$tsLastAttempt = $job->get('ts_last_attempt');
if ($tsLastAttempt) {
$ts = \strtotime($tsLastAttempt);
$ts = $tsLastAttempt / 1000;
$timeAgo = Html::tag('span', [
'class' => 'time-ago',
'title' => DateFormatter::formatDateTime($ts)

View File

@ -1,5 +1,5 @@
Name: Icinga Director
Version: 1.11.3
Version: 1.11.4
Depends: reactbundle (>=0.9.0), ipl (>=0.5.0), incubator (>=0.22.0)
Description: Director - Config tool for Icinga 2
Icinga Director is a configuration tool that has been designed to make

View File

@ -0,0 +1,22 @@
ALTER TABLE director_generated_config
DROP FOREIGN KEY director_generated_config_activity;
# Delete all entries with duplicate checksum except the first entry
DELETE log1 FROM director_activity_log log1
INNER JOIN director_activity_log log2 ON log1.checksum = log2.checksum
WHERE log1.id > log2.id;
ALTER TABLE director_activity_log
DROP INDEX checksum,
ADD UNIQUE INDEX checksum (checksum);
ALTER TABLE director_generated_config
ADD CONSTRAINT director_generated_config_activity
FOREIGN KEY (last_activity_checksum)
REFERENCES director_activity_log (checksum)
ON DELETE RESTRICT
ON UPDATE RESTRICT;
INSERT INTO director_schema_migration
(schema_version, migration_time)
VALUES (188, NOW());

View File

@ -0,0 +1,17 @@
ALTER TABLE director_job ADD COLUMN ts_last_attempt_tmp BIGINT(20) DEFAULT NULL;
ALTER TABLE director_job ADD COLUMN ts_last_error_tmp BIGINT(20) DEFAULT NULL;
UPDATE director_job
SET ts_last_attempt_tmp = UNIX_TIMESTAMP(ts_last_attempt) * 1000,
ts_last_error_tmp = UNIX_TIMESTAMP(ts_last_error) * 1000;
ALTER TABLE director_job
DROP COLUMN ts_last_attempt,
DROP COLUMN ts_last_error,
CHANGE ts_last_attempt_tmp ts_last_attempt BIGINT(20) DEFAULT NULL,
CHANGE ts_last_error_tmp ts_last_error BIGINT(20) DEFAULT NULL;
INSERT INTO director_schema_migration
(schema_version, migration_time)
VALUES (189, NOW());

View File

@ -46,7 +46,7 @@ CREATE TABLE director_activity_log (
INDEX search_idx (object_name),
INDEX search_idx2 (object_type(32), object_name(64), change_time),
INDEX search_author (author),
INDEX checksum (checksum)
UNIQUE INDEX checksum (checksum)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE director_activity_log_remark (
@ -114,7 +114,7 @@ CREATE TABLE director_generated_config (
last_activity_checksum VARBINARY(20) NOT NULL,
PRIMARY KEY (checksum),
CONSTRAINT director_generated_config_activity
FOREIGN KEY activity_checksum (last_activity_checksum)
FOREIGN KEY (last_activity_checksum)
REFERENCES director_activity_log (checksum)
ON DELETE RESTRICT
ON UPDATE RESTRICT
@ -347,8 +347,8 @@ CREATE TABLE director_job (
run_interval INT(10) UNSIGNED NOT NULL, -- seconds
timeperiod_id INT(10) UNSIGNED DEFAULT NULL,
last_attempt_succeeded ENUM('y', 'n') DEFAULT NULL,
ts_last_attempt TIMESTAMP NULL DEFAULT NULL,
ts_last_error TIMESTAMP NULL DEFAULT NULL,
ts_last_attempt BIGINT(20) NULL DEFAULT NULL,
ts_last_error BIGINT(20) NULL DEFAULT NULL,
last_error_message TEXT DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY (job_name),
@ -2446,4 +2446,4 @@ CREATE TABLE branched_icinga_dependency (
INSERT INTO director_schema_migration
(schema_version, migration_time)
VALUES (187, NOW());
VALUES (189, NOW());

View File

@ -0,0 +1,18 @@
ALTER TABLE director_job ADD COLUMN ts_last_attempt_tmp bigint DEFAULT NULL;
ALTER TABLE director_job ADD COLUMN ts_last_error_tmp bigint DEFAULT NULL;
UPDATE director_job
SET ts_last_attempt_tmp = UNIX_TIMESTAMP(ts_last_attempt) * 1000,
ts_last_error_tmp = UNIX_TIMESTAMP(ts_last_error) * 1000;
ALTER TABLE director_job
DROP COLUMN ts_last_attempt,
DROP COLUMN ts_last_error;
ALTER TABLE director_job RENAME COLUMN ts_last_attempt_tmp TO ts_last_attempt;
ALTER TABLE director_job RENAME COLUMN ts_last_error_tmp TO ts_last_error;
INSERT INTO director_schema_migration
(schema_version, migration_time)
VALUES (189, NOW());

View File

@ -448,8 +448,8 @@ CREATE TABLE director_job (
run_interval integer NOT NULL, -- seconds
timeperiod_id integer DEFAULT NULL,
last_attempt_succeeded enum_boolean DEFAULT NULL,
ts_last_attempt timestamp with time zone DEFAULT NULL,
ts_last_error timestamp with time zone DEFAULT NULL,
ts_last_attempt bigint DEFAULT NULL,
ts_last_error bigint DEFAULT NULL,
last_error_message text NULL DEFAULT NULL,
CONSTRAINT director_job_period
FOREIGN KEY (timeperiod_id)
@ -2781,4 +2781,4 @@ CREATE INDEX branched_dependency_search_object_name ON branched_icinga_dependenc
INSERT INTO director_schema_migration
(schema_version, migration_time)
VALUES (187, NOW());
VALUES (189, NOW());