Skip to content

Commit

Permalink
Implementation of live_modification
Browse files Browse the repository at this point in the history
  • Loading branch information
Mattia Codato committed Jun 22, 2021
1 parent c058359 commit 44846cd
Show file tree
Hide file tree
Showing 25 changed files with 1,110 additions and 12 deletions.
19 changes: 19 additions & 0 deletions application/forms/KickstartForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,22 @@ public function setup()
array($this->getElement('HINT_ready'))
);

$this->addElement('checkbox', 'liveModification', array(
'label' => $this->translate($this->translate('Live Modification enabled')),
'required' => false,
'value' => $this->config()->get('liveModification', 'enabled')
));

$this->addDisplayGroup(['liveModification'], 'liveModificationField', [
'decorators' => array(
'FormElements',
array('HtmlTag', array('tag' => 'dl')),
'Fieldset',
),
'order' => 50,
'legend' => $this->translate('Live modification')
]);

return;
}

Expand Down Expand Up @@ -304,6 +320,9 @@ protected function storeResourceConfig()

$config->setSection('db', array('resource' => $value));

$liveModificationEnabled = $this->getValue('liveModification');
$config->setSection('liveModification', array('enabled' => $liveModificationEnabled));

try {
$config->saveIni();
$this->setSuccessMessage($this->translate('Configuration has been stored'));
Expand Down
Binary file modified application/locale/de_DE/LC_MESSAGES/director.mo
Binary file not shown.
42 changes: 42 additions & 0 deletions application/locale/de_DE/LC_MESSAGES/director.po
Original file line number Diff line number Diff line change
Expand Up @@ -7449,6 +7449,48 @@ msgstr "warning"
msgid "yes"
msgstr "ja"

msgid "scheduled"
msgstr "Geplant"

msgid "succeeded"
msgstr "Erfolgreicht ausgeführt"

msgid "failed"
msgstr "Fehler während der Liveaktualisierung"

msgid "impossible"
msgstr "Liveaktualisierung nicht möglich"

msgid " (This change cannot be automatically applied on monitored object. Please deploy to align configuration.)"
msgstr " (Diese Änderung kann nicht automatisch auf das gemonitorte Object angewendet werden. Damit diese Änderung aktiv wird, muss die Konfiguration manuell ausgerollt werden.)"

msgid "There are %d pending live modifications"
msgstr "Es gibt %d ausstehende Liveänderungen"

msgid "Template is not supported by Live Modification"
msgstr "Das Template wird von der Liveaktualisierung nicht unterstützt"

msgid "Live Modification not supported: the host belongs to a modified group"
msgstr "Liveaktualisierung nicht möglich: Der Host geört zu einer geänderten Gruppe"

msgid "The host property %s is not supported by Live Modification"
msgstr "Liveaktualisierung für die Hosteigenschaft %s wird nicht unterstützt"

msgid "Service related to Host Template is not supported by Live Modification"
msgstr "Liveaktualisierung ist für Services von Hosttemplates nicht unterstützt"

msgid "Object not supported by Live Modification"
msgstr "Liveaktualisierung für dieses Objekt nicht möglich"

msgid "Live Modification enabled"
msgstr "Liveaktualisierung aktiviert"

msgid "Live Modification"
msgstr "Liveaktualisierung"

msgid "Disabled Services are not supported by Live Modification"
msgstr "Deaktivierte Dienste werden von Liveaktualisierung nicht unterstützt"

#~ msgid "Blacklist"
#~ msgstr "Blacklist"

Expand Down
Binary file modified application/locale/it_IT/LC_MESSAGES/director.mo
Binary file not shown.
44 changes: 43 additions & 1 deletion application/locale/it_IT/LC_MESSAGES/director.po
Original file line number Diff line number Diff line change
Expand Up @@ -7428,4 +7428,46 @@ msgid "e.g. "
msgstr "per es."

msgid "start using"
msgstr "inizia ad utilizzare"
msgstr "inizia ad utilizzare"

msgid "scheduled"
msgstr "Schedulata"

msgid "succeeded"
msgstr "Applicata con successo"

msgid "failed"
msgstr "Errore nell'aggiornamento live"

msgid "impossible"
msgstr "Impossibile effettuare l'aggiornamento live"

msgid " (This change cannot be automatically applied on monitored object. Please deploy to align configuration.)"
msgstr " (Questa modifica non puo' essere automaticamente applicata all'oggetto monitorato. Prego effettuare un deploy manuale della configurazione.)"

msgid "There are %d pending live modifications"
msgstr "Ci sono %d modifiche live in attesa di essere applicate"

msgid "Template is not supported by Live Modification"
msgstr "Il Template non è supportato dall'aggiornamento live"

msgid "Live Modification not supported: the host belongs to a modified group"
msgstr "L'aggiornamento live non è supportato: è stata modificata l'appartenenza dell'host ad uno o più gruppi"

msgid "The host property %s is not supported by Live Modification"
msgstr "La proprietà %s dell'host non è supportata dall'aggiornamento live"

msgid "Service related to Host Template is not supported by Live Modification"
msgstr "Un servizio associato ad un template non è supportato dall'aggiornamento live"

msgid "Object not supported by Live Modification"
msgstr "Oggetto non supportato dall'aggiornamento live"

msgid "Live Modification enabled"
msgstr "Aggiornamento live abilitato"

msgid "Live Modification"
msgstr "Aggiornamento live"

msgid "Disabled Services are not supported by Live Modification"
msgstr "I servizi didabilitati non sono supportati dall'aggiornamento live"
44 changes: 44 additions & 0 deletions doc/11-Live-modification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<a id="Live-modification"></a>Live Modification
===============================================

This chapter introduces the *Live Modification* functionality and describes how
it works.

*Live Modification* can be enabled by ticking the dedicated flag in the
`Configuration -> Modules -> Director -> Configuration` page.

Feature overview
----------------

*Live Modification* manages in real-time the monitoring objects defined by the
user without the necessity of a manual deployment.

At object creation/update/deletion time, the Director decides if changes are
eligible for the live modification. There are a few object types and properties
that are not yet covered by this feature and still require a manual deployment.
In those infrequent cases, however, the user will be notified that changes need
to be deployed by hand.

If changes are supported, they are queued in a pending state, then the
Director Daemon will take care of applying the modifications through the
Icinga2 API.

Activity Log
------------

All the real-time changes are tracked as usual in the
`Director -> Activity Log` page.
Each Activity Log entry has a `Live Modification` field with the following
possible values:

- **disabled**: if the feature is not enabled in the current installation
- **scheduled**: when changes are applied in the Director, but not yet
propagated to Icinga2
- **succeeded**: for all the changes already applied in monitoring
configuration
- **failed**: when changes are supported by the feature, but an error occurred
during the propagation
- **impossible**: if changes are not supported by the feature

A blue badge counter will be visible in sidebar if live modifications are still
pending on the system.
80 changes: 80 additions & 0 deletions library/Director/Core/CoreApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
use Exception;
use Icinga\Exception\NotFoundError;
use Icinga\Module\Director\Db;
use Icinga\Module\Director\DirectorObject\IcingaModifiedAttribute;
use Icinga\Module\Director\Exception\JsonEncodeException;
use Icinga\Module\Director\Hook\DeploymentHook;
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
use Icinga\Module\Director\Objects\DirectorActivityLog;
use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\Objects\IcingaCommand;
use Icinga\Module\Director\Objects\DirectorDeploymentLog;
Expand All @@ -16,6 +19,11 @@

class CoreApi implements DeploymentApiInterface
{
const OBJECT_PLURALITY = [
'Host' => 'hosts',
'Service' => 'services'
];

protected $client;

protected $initialized = false;
Expand Down Expand Up @@ -914,4 +922,76 @@ protected function client()

return $this->client;
}

public function sendModification(IcingaModifiedAttribute $modifiedAttribute)
{
$action = $modifiedAttribute->getProperty('action');
$objectType = $modifiedAttribute->getProperty('icinga_object_type');
$objectName = $modifiedAttribute->getProperty('icinga_object_name');
try {
$modification = $modifiedAttribute->getModifiedAttributes();
} catch (JsonEncodeException $e) {
return false;
}

switch ($action) {
case 'create':
$result = $this->createObject($objectType, $objectName, $modification);
break;
case 'modify':
$result = $this->modifyObject($objectType, $objectName, $modification);
break;
case 'delete':
$result = $this->deleteObject($objectType, $objectName);
break;
default:
return false;
}

return $result && $result->succeeded();
}

protected function createObject($type, $name, $attrs)
{
try {
return $this->client()->put(
'objects/' . self::OBJECT_PLURALITY[$type] . '/' . $name,
(object)array(
'package' => 'director',
'attrs' => $attrs
)
);
} catch (Exception $e) {
return false;
}
}

protected function modifyObject($type, $name, $attrs)
{
try {
return $this->client()->post(
'objects/' . self::OBJECT_PLURALITY[$type] . '/' . $name,
(object)array(
'package' => 'director',
'attrs' => $attrs
)
);
} catch (Exception $e) {
return false;
}
}

protected function deleteObject($type, $name)
{
try {
return $this->client()->delete(
'objects/' . self::OBJECT_PLURALITY[$type] . '/' . $name,
(object) array(
'cascade' => true
)
);
} catch (Exception $e) {
return false;
}
}
}
1 change: 1 addition & 0 deletions library/Director/Daemon/BackgroundDaemon.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ protected function initialize()
->register($this->jobRunner)
->register($this->logProxy)
->register(new DeploymentChecker($this->loop))
->register(new LiveCreation($this->loop))
->run($this->loop);
$this->setState('running');
}
Expand Down
103 changes: 103 additions & 0 deletions library/Director/Daemon/LiveCreation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

namespace Icinga\Module\Director\Daemon;

use Exception;
use Icinga\Module\Director\Db;
use Icinga\Module\Director\DirectorObject\IcingaModifiedAttribute;
use Icinga\Module\Director\Objects\DirectorActivityLog;
use React\EventLoop\LoopInterface;
use function React\Promise\resolve;

class LiveCreation implements DbBasedComponent
{
/** @var Db */
protected $db;

public function __construct(LoopInterface $loop)
{
$loop->addPeriodicTimer(5, function () {
if ($this->db) {
$this->run();
}
});
}

/**
* @return IcingaModifiedAttribute[]
*/
public function fetchPendingModifications()
{
return IcingaModifiedAttribute::loadAll(
$this->db,
$this->db->getDbAdapter()
->select()->from('icinga_modified_attribute')
->where('state != ?', 'applied')
->order('state')
->order('id')
);
}

public function applyModification(IcingaModifiedAttribute $modifiedAttribute)
{
try {
return $this->db->getDeploymentEndpoint()->api()->sendModification($modifiedAttribute);
} catch (Exception $e) {
return false;
}
}

public function run()
{
foreach ($this->fetchPendingModifications() as $modification) {
$activityLogStatus = null;
$activityId = $modification->get('activity_id');
if ($activityId !== null) {
$activityLog = DirectorActivityLog::load($activityId, $this->db);
}

if ($this->applyModification($modification)) {
if ($modification->get('state') === 'scheduled_for_reset') {
$modification->delete();
} else {
if ($activityId !== null && $activityLog->get('live_modification') ===
DirectorActivityLog::LIVE_MODIFICATION_VALUE_SCHEDULED) {
$activityLogStatus = DirectorActivityLog::LIVE_MODIFICATION_VALUE_SUCCEEDED;
}
$modification->set('state', 'applied');
$modification->set('ts_applied', DaemonUtil::timestampWithMilliseconds());
$modification->store();
}
} else {
$activityLogStatus = DirectorActivityLog::LIVE_MODIFICATION_VALUE_FAILED;
$modification->delete();
}

if ($activityId !== null && $activityLogStatus !== null) {
$activityLog->set('live_modification', $activityLogStatus);
$activityLog->store($this->db);
}
}
}

/**
* @param Db $connection
* @return \React\Promise\ExtendedPromiseInterface
*/
public function initDb(Db $connection)
{
$this->db = $connection;

return resolve();
}

/**
* @return \React\Promise\ExtendedPromiseInterface
*/
public function stopDb()
{
$this->db = null;

return resolve();
}
}
Loading

0 comments on commit 44846cd

Please sign in to comment.