Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion application/clicommands/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Command extends CliCommand
{
private $loopStarted = false;

protected $logger;
protected Logger $logger;

/** @var RemoteClient */
protected $remoteClient;
Expand Down
37 changes: 37 additions & 0 deletions application/clicommands/DaemonCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

namespace Icinga\Module\Vspheredb\Clicommands;

use gipfl\Log\Filter\LogLevelFilter;
use gipfl\SimpleDaemon\Daemon;
use Icinga\Module\Vspheredb\Daemon\RpcNamespace\RpcNamespaceProcess;
use Icinga\Module\Vspheredb\Daemon\VsphereDbDaemon;
use Icinga\Module\Vspheredb\Db;
use Throwable;

class DaemonCommand extends Command
{
Expand All @@ -20,6 +23,7 @@ public function runAction()
$this->assertRequiredExtensionsAreLoaded();
$this->assertNoVcenterParam();
$daemon = new Daemon();
$this->eventuallyApplyConfiguredLogLevel();
$daemon->setLogger($this->logger);
$vSphereDb = new VsphereDbDaemon();
$vSphereDb->on(RpcNamespaceProcess::ON_RESTART, function () use ($daemon) {
Expand All @@ -30,6 +34,39 @@ public function runAction()
$this->eventuallyStartMainLoop();
}

protected function eventuallyApplyConfiguredLogLevel(): void
{
// Setting the log level via CLI flags has higher precedence than database config
if ($this->isVerbose || $this->isDebugging) {
return;
}

$db = null;
try {
$db = Db::newConfiguredInstance()->getDbAdapter();
$level = $db->fetchOne(
$db->select()
->from('daemon_config', ['value'])
->where('`key` = ?', 'log_level')
);
if (! is_string($level) || $level === '') {
return;
}

$newFilter = new LogLevelFilter($level);
$this->logger->addFilter($newFilter);
foreach ($this->logger->getFilters() as $filter) {
if ($filter instanceof LogLevelFilter && $filter !== $newFilter) {
$this->logger->removeFilter($filter);
}
}
} catch (Throwable) {
// Keep current log level if DB config is not available yet.
} finally {
$db?->closeConnection();
}
}

protected function assertNoVcenterParam()
{
if ($this->params->get('vCenterId')) {
Expand Down
2 changes: 1 addition & 1 deletion application/controllers/DaemonController.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function indexAction()

protected function prepareLogSettings()
{
$logLevelForm = new LogLevelForm($this->remoteClient(), $this->loop());
$logLevelForm = new LogLevelForm($this->remoteClient(), $this->loop(), $this->db()->getDbAdapter());
$logLevelForm->on($logLevelForm::ON_SUCCESS, function () {
$this->redirectNow($this->url());
});
Expand Down
6 changes: 1 addition & 5 deletions library/Vspheredb/Daemon/RpcNamespace/RpcNamespaceLogger.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,11 @@ public function setLogLevelRequest($level)
$this->logger->notice("Will change the log level to '$level'");
}
$this->logger->addFilter($newFilter);
$remove = [];
foreach ($this->logger->getFilters() as $filter) {
if ($filter instanceof LogLevelFilter && $filter !== $newFilter) {
$remove[] = $filter;
$this->logger->removeFilter($filter);
}
}
foreach ($remove as $filter) {
$this->logger->removeFilter($filter);
}
if ($newFilter->wants('notice', '')) {
$this->logger->notice("Changed log level to '$level'");
}
Expand Down
142 changes: 69 additions & 73 deletions library/Vspheredb/Daemon/VsphereDbDaemon.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ public function start(LoopInterface $loop)
$this->loop = $loop;
$logger = $this->logger;
$this->daemonState = $this->initializeDaemonState();
$this->setInitialDaemonState();
$this->detectProcessInfo();
$this->initializeDbLogger($logger); // TODO: move to sub process. Hint: needs no DB, has a queue
$this->prepareApi($loop, $logger);
Expand Down Expand Up @@ -152,15 +151,10 @@ protected function initializeDaemonState()
}
$this->componentStates = $daemonState->getComponentStates();
});

return $daemonState;
}

protected function setInitialDaemonState()
{
$daemonState = $this->daemonState;
$daemonState->setProcessTitle(self::PROCESS_NAME);
$daemonState->setState(self::STATE_STARTING);

return $daemonState;
}

protected function onComponentChange($component, $formerState, $currentState)
Expand All @@ -174,77 +168,79 @@ protected function onComponentChange($component, $formerState, $currentState)
$this->daemonState->getComponentState($component)
));
}
if ($component === self::COMPONENT_DB) {
if ($formerState === self::STATE_READY) {
$this->stopConfigWatch();
$this->stopComponent(self::COMPONENT_API);
$this->stopComponent(self::COMPONENT_LOCALDB);
} elseif ($currentState === self::STATE_IDLE) {
$this->runConfigWatch();
} elseif ($currentState === self::STATE_READY) {
$this->setLocalDbState(self::STATE_STARTING);
}
if ($currentState === self::STATE_FAILED) {
$this->loop->addTimer(10, function () {
$this->stopDbProcess();
switch ($component) {
case self::COMPONENT_DB:
if ($formerState === self::STATE_READY) {
$this->stopConfigWatch();
if ($this->daemonState->getComponentState(self::COMPONENT_DB) === self::STATE_FAILED) {
$this->initializeDbProcess();
}
});
}
}
if ($component === self::COMPONENT_LOCALDB) {
if ($formerState === self::STATE_READY) {
$this->stopComponent(self::COMPONENT_API);
}
switch ($currentState) {
case self::STATE_STARTING:
$this->reconnectToDb();
break;
case self::STATE_FAILED:
$this->logger->error('Failed. Will try to reconnect to the Database');
$this->eventuallyDisconnectFromDb();
$delay = $this->delayOnFailed;
$this->logger->warning("Failed. Reconnecting in {$delay}s");
$this->loop->addTimer($delay, function () {
if ($this->getLocalDbState() === self::STATE_FAILED) {
$this->setLocalDbState(self::STATE_STARTING);
$this->stopComponent(self::COMPONENT_API);
$this->stopComponent(self::COMPONENT_LOCALDB);
} elseif ($currentState === self::STATE_IDLE) {
$this->runConfigWatch();
} elseif ($currentState === self::STATE_READY) {
$this->setLocalDbState(self::STATE_STARTING);
}
if ($currentState === self::STATE_FAILED) {
$this->loop->addTimer(10, function () {
$this->stopDbProcess();
$this->stopConfigWatch();
if ($this->daemonState->getComponentState(self::COMPONENT_DB) === self::STATE_FAILED) {
$this->initializeDbProcess();
}
});
break;
case self::STATE_READY:
$this->setApiState(self::STATE_STARTING);
break;
case self::STATE_STOPPING:
$this->eventuallyDisconnectFromDb();
}
break;
case self::COMPONENT_LOCALDB:
if ($formerState === self::STATE_READY) {
$this->stopComponent(self::COMPONENT_API);
$this->setLocalDbState(self::STATE_STOPPED);
break;
}
}
if ($component === self::COMPONENT_API) {
switch ($currentState) {
case self::STATE_STARTING:
$this->apiConnectionHandler->run($this->loop);
$this->setApiState(self::STATE_READY);
break;
case self::STATE_READY:
$this->refreshConfiguredServers();
$this->daemonState->setState(self::STATE_READY);
break;
case self::STATE_FAILED:
$this->logger->error('[api] failed');
}
switch ($currentState) {
case self::STATE_STARTING:
$this->reconnectToDb();
break;
case self::STATE_FAILED:
$this->logger->error('Failed. Will try to reconnect to the Database');
$this->eventuallyDisconnectFromDb();
$delay = $this->delayOnFailed;
$this->logger->warning("Failed. Reconnecting in {$delay}s");
$this->loop->addTimer($delay, function () {
if ($this->getLocalDbState() === self::STATE_FAILED) {
$this->setLocalDbState(self::STATE_STARTING);
}
});
break;
case self::STATE_READY:
$this->setApiState(self::STATE_STARTING);
break;
case self::STATE_STOPPING:
$this->eventuallyDisconnectFromDb();
$this->stopComponent(self::COMPONENT_API);
$this->setLocalDbState(self::STATE_STOPPED);
break;
}
break;
case self::COMPONENT_API:
switch ($currentState) {
case self::STATE_STARTING:
$this->apiConnectionHandler->run($this->loop);
$this->setApiState(self::STATE_READY);
break;
case self::STATE_READY:
$this->refreshConfiguredServers();
$this->daemonState->setState(self::STATE_READY);
break;
case self::STATE_FAILED:
$this->logger->error('[api] failed');
// Intentional fall-through:
// no break
case self::STATE_STOPPING:
if ($this->apiConnectionHandler) {
$this->apiConnectionHandler->stop();
}
$this->stopAllApiTasks();
$this->setApiState(self::STATE_STOPPED);
break;
}
case self::STATE_STOPPING:
if ($this->apiConnectionHandler) {
$this->apiConnectionHandler->stop();
}
$this->stopAllApiTasks();
$this->setApiState(self::STATE_STOPPED);
break;
}
break;
}
}

Expand Down
26 changes: 24 additions & 2 deletions library/Vspheredb/Web/Form/LogLevelForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use ipl\I18n\Translation;
use Psr\Log\LogLevel;
use React\EventLoop\LoopInterface;
use Zend_Db_Adapter_Abstract;

use function React\Async\await;

Expand All @@ -22,13 +23,17 @@ class LogLevelForm extends InlineForm
/** @var LoopInterface */
protected $loop;

/** @var Zend_Db_Adapter_Abstract */
protected Zend_Db_Adapter_Abstract $db;

/** @var boolean */
protected $talkedToSocket;

public function __construct(RemoteClient $client, LoopInterface $loop)
public function __construct(RemoteClient $client, LoopInterface $loop, Zend_Db_Adapter_Abstract $db)
{
$this->client = $client;
$this->loop = $loop;
$this->db = $db;
}

public function talkedToSocket()
Expand Down Expand Up @@ -63,7 +68,24 @@ protected function assemble()

protected function onSuccess()
{
await($this->client->request('logger.setLogLevel', ['level' => $this->getValue('log_level')]));
$logLevel = $this->getValue('log_level');
await(
$this->client->request('logger.setLogLevel', ['level' => $logLevel])
->then(function () use ($logLevel) {
$query = $this->db->select()
->from('daemon_config', ['key'])
->where('`key` = ?', 'log_level');
if ($this->db->query($query)->rowCount()) {
$this->db->update(
'daemon_config',
['value' => $logLevel],
$this->db->quoteInto('`key` = ?', 'log_level')
);
} else {
$this->db->insert('daemon_config', ['`key`' => 'log_level', 'value' => $logLevel]);
}
})
);
}

protected function listLogLevels()
Expand Down
8 changes: 8 additions & 0 deletions schema/mysql-migrations/upgrade_64.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CREATE TABLE daemon_config (
`key` ENUM('log_level') PRIMARY KEY,
value VARCHAR(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_bin;

INSERT INTO vspheredb_schema_migration
(schema_version, migration_time)
VALUES (64, NOW());
7 changes: 6 additions & 1 deletion schema/mysql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -995,6 +995,11 @@ CREATE TABLE tagging_object_tag (
-- vm_id BIGINT(20) UNSIGNED AUTO_INCREMENT NOT NULL,
-- );

CREATE TABLE daemon_config (
`key` ENUM('log_level') PRIMARY KEY,
value VARCHAR(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_bin;

INSERT INTO vspheredb_schema_migration
(schema_version, migration_time)
VALUES (63, NOW());
VALUES (64, NOW());
Loading