Merge branch 'Icinga:master' into adn77-fix_service_edit

This commit is contained in:
Alex 2025-07-22 21:41:24 +02:00 committed by GitHub
commit 5fe1fbc9bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 321 additions and 88 deletions

View file

@ -6,15 +6,6 @@ on:
- master
jobs:
trigger-update:
name: L10n Update Trigger
runs-on: ubuntu-latest
steps:
- name: Repository dispatch
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.ICINGABOT_TOKEN }}
repository: Icinga/L10n
event-type: update
client-payload: '{"origin": "${{ github.repository }}", "commit": "${{ github.sha }}"}'
update:
uses: icinga/github-actions/.github/workflows/L10n-update.yml@main
secrets: inherit

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

@ -13,7 +13,7 @@ trait DeployFormsBug7530
if (parent::hasBeenSubmitted()) {
return true;
} else {
return \strlen($this->getSentValue('confirm_7530')) > 0;
return strlen($this->getSentValue('confirm_7530', '')) > 0;
}
}

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.5"
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.5"
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,50 @@
Please make sure to always read our [Upgrading](05-Upgrading.md) documentation
before switching to a new version.
v1.11.5
-------
## Database Schema
- FIX: Failed schema migration of MySQL to version 188 due Integrity constraint violation ([#2970](https://github.com/Icinga/icingaweb2-module-director/issues/2970))
## Deployment
- Fix: Deprecation notice that sometimes appears on deployment form (no issue)
### Fixed Issues
You can find issues related to this release on our [roadmap](https://github.com/Icinga/icingaweb2-module-director/milestone/40?closed=1)
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.5
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());