summaryrefslogtreecommitdiffstats
path: root/library/Icingadb/Setup
diff options
context:
space:
mode:
Diffstat (limited to 'library/Icingadb/Setup')
-rw-r--r--library/Icingadb/Setup/ApiTransportPage.php128
-rw-r--r--library/Icingadb/Setup/ApiTransportStep.php102
-rw-r--r--library/Icingadb/Setup/DbResourcePage.php145
-rw-r--r--library/Icingadb/Setup/DbResourceStep.php148
-rw-r--r--library/Icingadb/Setup/IcingaDbWizard.php89
-rw-r--r--library/Icingadb/Setup/RedisPage.php68
-rw-r--r--library/Icingadb/Setup/RedisStep.php205
-rw-r--r--library/Icingadb/Setup/WelcomePage.php56
8 files changed, 941 insertions, 0 deletions
diff --git a/library/Icingadb/Setup/ApiTransportPage.php b/library/Icingadb/Setup/ApiTransportPage.php
new file mode 100644
index 0000000..e727e99
--- /dev/null
+++ b/library/Icingadb/Setup/ApiTransportPage.php
@@ -0,0 +1,128 @@
+<?php
+
+/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Icingadb\Setup;
+
+use Icinga\Data\ConfigObject;
+use Icinga\Module\Icingadb\Command\Transport\CommandTransport;
+use Icinga\Module\Icingadb\Command\Transport\CommandTransportException;
+use Icinga\Web\Form;
+
+class ApiTransportPage extends Form
+{
+ public function init()
+ {
+ $this->setName('setup_icingadb_api_transport');
+ $this->setTitle(t('Icinga 2 API'));
+ $this->addDescription(t(
+ 'Please fill out the connection details to the Icinga 2 API.'
+ ));
+ $this->setValidatePartial(true);
+ }
+
+ public function createElements(array $formData)
+ {
+ if (isset($formData['skip_validation']) && $formData['skip_validation']) {
+ // In case another error occured and the checkbox was displayed before
+ $this->addSkipValidationCheckbox();
+ } else {
+ $this->addElement('hidden', 'skip_validation', ['value' => 0]);
+ }
+
+ $this->addElement('hidden', 'transport', [
+ 'required' => true,
+ 'disabled' => true,
+ 'value' => 'api'
+ ]);
+ $this->addElement('hidden', 'name', [
+ 'required' => true,
+ 'disabled' => true,
+ 'value' => 'icinga2'
+ ]);
+ $this->addElement('text', 'host', [
+ 'required' => true,
+ 'label' => t('Host'),
+ 'description' => t('Hostname or address of the Icinga master')
+ ]);
+ $this->addElement('number', 'port', [
+ 'required' => true,
+ 'label' => t('Port'),
+ 'value' => 5665,
+ 'min' => 1,
+ 'max' => 65536
+ ]);
+ $this->addElement('text', 'username', [
+ 'required' => true,
+ 'label' => t('API Username'),
+ 'description' => t('User to authenticate with using HTTP Basic Auth')
+ ]);
+ $this->addElement('password', 'password', [
+ 'required' => true,
+ 'renderPassword' => true,
+ 'label' => t('API Password'),
+ 'autocomplete' => 'new-password'
+ ]);
+ }
+
+ public function isValid($formData)
+ {
+ if (! parent::isValid($formData)) {
+ return false;
+ }
+
+ if (! isset($formData['skip_validation']) || !$formData['skip_validation']) {
+ if (! $this->validateConfiguration()) {
+ $this->addSkipValidationCheckbox();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function isValidPartial(array $formData)
+ {
+ if (isset($formData['backend_validation']) && parent::isValid($formData)) {
+ if (! $this->validateConfiguration()) {
+ return false;
+ }
+
+ $this->info(t('The configuration has been successfully validated.'));
+ } elseif (! isset($formData['backend_validation'])) {
+ // This is usually done by isValid(Partial), but as we're not calling any of these...
+ $this->populate($formData);
+ }
+
+ return true;
+ }
+
+ protected function validateConfiguration(): bool
+ {
+ try {
+ CommandTransport::createTransport(new ConfigObject($this->getValues()))->probe();
+ } catch (CommandTransportException $e) {
+ $this->error(sprintf(
+ t('Failed to successfully validate the configuration: %s'),
+ $e->getMessage()
+ ));
+
+ return false;
+ }
+
+ return true;
+ }
+
+ protected function addSkipValidationCheckbox()
+ {
+ $this->addElement(
+ 'checkbox',
+ 'skip_validation',
+ [
+ 'ignore' => true,
+ 'label' => t('Skip Validation'),
+ 'description' => t('Check this to not to validate the configuration')
+ ]
+ );
+ }
+}
diff --git a/library/Icingadb/Setup/ApiTransportStep.php b/library/Icingadb/Setup/ApiTransportStep.php
new file mode 100644
index 0000000..1e2e905
--- /dev/null
+++ b/library/Icingadb/Setup/ApiTransportStep.php
@@ -0,0 +1,102 @@
+<?php
+
+/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Icingadb\Setup;
+
+use Exception;
+use Icinga\Application\Config;
+use Icinga\Exception\IcingaException;
+use Icinga\Module\Setup\Step;
+use ipl\Html\Attributes;
+use ipl\Html\HtmlDocument;
+use ipl\Html\HtmlElement;
+use ipl\Html\Table;
+use ipl\Html\Text;
+
+class ApiTransportStep extends Step
+{
+ /** @var array */
+ protected $data;
+
+ /** @var Exception */
+ protected $error;
+
+ public function __construct(array $data)
+ {
+ $this->data = $data;
+ }
+
+ public function apply()
+ {
+ $transportConfig = $this->data;
+ $transportName = $transportConfig['name'];
+ unset($transportConfig['name']);
+
+ try {
+ $config = Config::module('icingadb', 'commandtransports', true);
+ $config->setSection($transportName, $transportConfig);
+ $config->saveIni();
+ } catch (Exception $e) {
+ $this->error = $e;
+ return false;
+ }
+
+ return true;
+ }
+
+ public function getSummary()
+ {
+ $description = new HtmlElement('p', null, Text::create(mt(
+ 'icingadb',
+ 'The Icinga 2 API will be accessed using the following connection details:'
+ )));
+
+ $apiOptions = new Table();
+ $apiOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Host'))),
+ $this->data['host']
+ ]));
+ $apiOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Port'))),
+ $this->data['port']
+ ]));
+ $apiOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Username'))),
+ $this->data['username']
+ ]));
+ $apiOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Password'))),
+ str_repeat('*', strlen($this->data['password']))
+ ]));
+
+ $topic = new HtmlElement('div', Attributes::create(['class' => 'topic']));
+ $topic->addHtml($description, $apiOptions);
+
+ $summary = new HtmlDocument();
+ $summary->addHtml(
+ new HtmlElement('h2', null, Text::create(mt('icingadb', 'Icinga 2 API'))),
+ $topic
+ );
+
+ return $summary->render();
+ }
+
+ public function getReport()
+ {
+ if ($this->error === null) {
+ return [sprintf(
+ mt('icingadb', 'Commandtransport configuration update successful: %s'),
+ Config::module('icingadb', 'commandtransports')->getConfigFile()
+ )];
+ } else {
+ return [
+ sprintf(
+ mt('icingadb', 'Commandtransport configuration update failed: %s'),
+ Config::module('icingadb', 'commandtransports')->getConfigFile()
+ ),
+ sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->error))
+ ];
+ }
+ }
+}
diff --git a/library/Icingadb/Setup/DbResourcePage.php b/library/Icingadb/Setup/DbResourcePage.php
new file mode 100644
index 0000000..cc99dcc
--- /dev/null
+++ b/library/Icingadb/Setup/DbResourcePage.php
@@ -0,0 +1,145 @@
+<?php
+
+/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Icingadb\Setup;
+
+use Icinga\Forms\Config\ResourceConfigForm;
+use Icinga\Forms\Config\Resource\DbResourceForm;
+use Icinga\Web\Form;
+
+class DbResourcePage extends Form
+{
+ public function init()
+ {
+ $this->setName('setup_icingadb_resource');
+ $this->setTitle(t('Icinga DB Resource'));
+ $this->addDescription(t(
+ 'Please fill out the connection details below to access Icinga DB.'
+ ));
+ $this->setValidatePartial(true);
+ }
+
+ public function createElements(array $formData)
+ {
+ $this->addElement(
+ 'hidden',
+ 'type',
+ [
+ 'required' => true,
+ 'disabled' => true,
+ 'value' => 'db'
+ ]
+ );
+
+ if (isset($formData['skip_validation']) && $formData['skip_validation']) {
+ // In case another error occured and the checkbox was displayed before
+ $this->addSkipValidationCheckbox();
+ } else {
+ $this->addElement('hidden', 'skip_validation', ['value' => 0]);
+ }
+
+ $dbResourceForm = new DbResourceForm();
+ $this->addElements($dbResourceForm->createElements($formData)->getElements());
+ $this->getElement('name')->setValue('icingadb');
+ $this->getElement('db')->setMultiOptions([
+ 'mysql' => 'MySQL',
+ 'pgsql' => 'PostgreSQL'
+ ]);
+
+ $this->removeElement('name');
+ $this->addElement(
+ 'hidden',
+ 'name',
+ [
+ 'required' => true,
+ 'disabled' => true,
+ 'value' => 'icingadb'
+ ]
+ );
+
+ if (! isset($formData['db']) || $formData['db'] === 'mysql') {
+ $this->getElement('charset')->setValue('utf8mb4');
+ }
+ }
+
+ public function isValid($formData)
+ {
+ if (! parent::isValid($formData)) {
+ return false;
+ }
+
+ if (! isset($formData['skip_validation']) || !$formData['skip_validation']) {
+ if (! $this->validateConfiguration()) {
+ $this->addSkipValidationCheckbox();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function isValidPartial(array $formData)
+ {
+ if (isset($formData['backend_validation']) && parent::isValid($formData)) {
+ if (! $this->validateConfiguration(true)) {
+ return false;
+ }
+
+ $this->info(t('The configuration has been successfully validated.'));
+ } elseif (! isset($formData['backend_validation'])) {
+ // This is usually done by isValid(Partial), but as we're not calling any of these...
+ $this->populate($formData);
+ }
+
+ return true;
+ }
+
+ protected function validateConfiguration(bool $showLog = false): bool
+ {
+ $inspection = ResourceConfigForm::inspectResource($this);
+ if ($inspection !== null) {
+ if ($showLog) {
+ $join = function ($e) use (&$join) {
+ return is_string($e) ? $e : join("\n", array_map($join, $e));
+ };
+ $this->addElement(
+ 'note',
+ 'inspection_output',
+ [
+ 'order' => 0,
+ 'value' => '<strong>' . t('Validation Log') . "</strong>\n\n"
+ . join("\n", array_map($join, $inspection->toArray())),
+ 'decorators' => [
+ 'ViewHelper',
+ ['HtmlTag', ['tag' => 'pre', 'class' => 'log-output']],
+ ]
+ ]
+ );
+ }
+
+ if ($inspection->hasError()) {
+ $this->error(sprintf(
+ t('Failed to successfully validate the configuration: %s'),
+ $inspection->getError()
+ ));
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected function addSkipValidationCheckbox()
+ {
+ $this->addElement(
+ 'checkbox',
+ 'skip_validation',
+ [
+ 'ignore' => true,
+ 'label' => t('Skip Validation'),
+ 'description' => t('Check this to not to validate the configuration')
+ ]
+ );
+ }
+}
diff --git a/library/Icingadb/Setup/DbResourceStep.php b/library/Icingadb/Setup/DbResourceStep.php
new file mode 100644
index 0000000..970d367
--- /dev/null
+++ b/library/Icingadb/Setup/DbResourceStep.php
@@ -0,0 +1,148 @@
+<?php
+
+/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Icingadb\Setup;
+
+use Exception;
+use Icinga\Application\Config;
+use Icinga\Exception\IcingaException;
+use Icinga\Module\Setup\Step;
+use ipl\Html\Attributes;
+use ipl\Html\HtmlDocument;
+use ipl\Html\HtmlElement;
+use ipl\Html\Table;
+use ipl\Html\Text;
+
+class DbResourceStep extends Step
+{
+ /** @var array */
+ protected $data;
+
+ /** @var Exception */
+ protected $error;
+
+ public function __construct(array $data)
+ {
+ $this->data = $data;
+ }
+
+ public function apply()
+ {
+ $resourceConfig = $this->data;
+ $resourceName = $resourceConfig['name'];
+ unset($resourceConfig['name']);
+
+ try {
+ $config = Config::app('resources', true);
+ $config->setSection($resourceName, $resourceConfig);
+ $config->saveIni();
+ } catch (Exception $e) {
+ $this->error = $e;
+ return false;
+ }
+
+ try {
+ $config = Config::module('icingadb', 'config', true);
+ $config->setSection('icingadb', ['resource' => $resourceName]);
+ $config->saveIni();
+ } catch (Exception $e) {
+ $this->error = $e;
+ return false;
+ }
+
+ return true;
+ }
+
+ public function getSummary()
+ {
+ $description = new HtmlElement('p', null, Text::create(mt(
+ 'icingadb',
+ 'Icinga DB will be accessed using the following connection details:'
+ )));
+
+ $resourceOptions = new Table();
+ $resourceOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Host'))),
+ $this->data['host']
+ ]));
+ $resourceOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Port'))),
+ $this->data['port'] ?: ($this->data['db'] === 'mysql' ? 3306 : 5432)
+ ]));
+ $resourceOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Database'))),
+ $this->data['dbname']
+ ]));
+ $resourceOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Username'))),
+ $this->data['username']
+ ]));
+ $resourceOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Password'))),
+ str_repeat('*', strlen($this->data['password']))
+ ]));
+ $resourceOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Charset'))),
+ $this->data['charset']
+ ]));
+
+ if (isset($this->data['use_ssl']) && $this->data['use_ssl']) {
+ $resourceOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('SSL Do Not Verify Server Certificate'))),
+ isset($this->data['ssl_do_not_verify_server_cert']) && $this->data['ssl_do_not_verify_server_cert']
+ ? t('Yes')
+ : t('No')
+ ]));
+ $resourceOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('SSL Key'))),
+ $this->data['ssl_key'] ?: mt('icingadb', 'None', 'non-existence of a value')
+ ]));
+ $resourceOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('SSL Certificate'))),
+ $this->data['ssl_cert'] ?: mt('icingadb', 'None', 'non-existence of a value')
+ ]));
+ $resourceOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('SSL CA'))),
+ $this->data['ssl_ca'] ?: mt('icingadb', 'None', 'non-existence of a value')
+ ]));
+ $resourceOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('The CA certificate file path'))),
+ $this->data['ssl_capath'] ?: mt('icingadb', 'None', 'non-existence of a value')
+ ]));
+ $resourceOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('SSL CA Path'))),
+ $this->data['ssl_cipher'] ?: mt('icingadb', 'None', 'non-existence of a value')
+ ]));
+ }
+
+ $topic = new HtmlElement('div', Attributes::create(['class' => 'topic']));
+ $topic->addHtml($description, $resourceOptions);
+
+ $summary = new HtmlDocument();
+ $summary->addHtml(
+ new HtmlElement('h2', null, Text::create(mt('icingadb', 'Icinga DB Resource'))),
+ $topic
+ );
+
+ return $summary->render();
+ }
+
+ public function getReport()
+ {
+ if ($this->error === null) {
+ return [sprintf(
+ mt('icingadb', 'Resource configuration update successful: %s'),
+ Config::resolvePath('resources.ini')
+ )];
+ } else {
+ return [
+ sprintf(
+ mt('icingadb', 'Resource configuration update failed: %s'),
+ Config::resolvePath('resources.ini')
+ ),
+ sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->error))
+ ];
+ }
+ }
+}
diff --git a/library/Icingadb/Setup/IcingaDbWizard.php b/library/Icingadb/Setup/IcingaDbWizard.php
new file mode 100644
index 0000000..f99f240
--- /dev/null
+++ b/library/Icingadb/Setup/IcingaDbWizard.php
@@ -0,0 +1,89 @@
+<?php
+
+/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Icingadb\Setup;
+
+use Icinga\Application\Icinga;
+use Icinga\Module\Setup\Forms\SummaryPage;
+use Icinga\Module\Setup\Requirement\PhpModuleRequirement;
+use Icinga\Module\Setup\Requirement\WebLibraryRequirement;
+use Icinga\Module\Setup\RequirementSet;
+use Icinga\Module\Setup\Setup;
+use Icinga\Module\Setup\SetupWizard;
+use Icinga\Web\Form;
+use Icinga\Web\Request;
+use Icinga\Web\Wizard;
+
+class IcingaDbWizard extends Wizard implements SetupWizard
+{
+ protected function init()
+ {
+ $this->addPage(new WelcomePage());
+ $this->addPage(new DbResourcePage());
+ $this->addPage(new RedisPage());
+ $this->addPage(new ApiTransportPage());
+ $this->addPage(new SummaryPage(['name' => 'setup_icingadb_summary']));
+ }
+
+ public function setupPage(Form $page, Request $request)
+ {
+ if ($page->getName() === 'setup_icingadb_summary') {
+ $page->setSummary($this->getSetup()->getSummary());
+ $page->setSubjectTitle('Icinga DB Web');
+ }
+ }
+
+ public function getSetup()
+ {
+ $pageData = $this->getPageData();
+ $setup = new Setup();
+
+ $setup->addStep(new DbResourceStep($pageData['setup_icingadb_resource']));
+ $setup->addStep(new RedisStep($pageData['setup_icingadb_redis']));
+ $setup->addStep(new ApiTransportStep($pageData['setup_icingadb_api_transport']));
+
+ return $setup;
+ }
+
+ public function getRequirements()
+ {
+ $set = new RequirementSet();
+
+ $requiredVersions = Icinga::app()->getModuleManager()->getModule('icingadb')->getRequiredLibraries();
+
+ $set->add(new WebLibraryRequirement([
+ 'condition' => ['icinga-php-library', '', $requiredVersions['icinga-php-library']],
+ 'alias' => 'Icinga PHP library',
+ 'description' => t('The Icinga PHP library (IPL) is required for Icinga DB Web')
+ ]));
+
+ $set->add(new WebLibraryRequirement([
+ 'condition' => ['icinga-php-thirdparty', '', $requiredVersions['icinga-php-thirdparty']],
+ 'alias' => 'Icinga PHP Thirdparty',
+ 'description' => t('The Icinga PHP Thirdparty library is required for Icinga DB Web')
+ ]));
+
+ $set->add(new PhpModuleRequirement([
+ 'condition' => 'libxml',
+ 'alias' => 'libxml',
+ 'description' => t('For check plugins that output HTML the libxml extension is required')
+ ]));
+
+ $set->add(new PhpModuleRequirement([
+ 'condition' => 'dom',
+ 'alias' => 'dom',
+ 'description' => t('For check plugins that output HTML the dom extension is required')
+ ]));
+
+ $set->add(new PhpModuleRequirement([
+ 'condition' => 'curl',
+ 'alias' => 'cURL',
+ 'description' => t(
+ 'To send external commands over Icinga 2\'s API, the cURL module for PHP is required.'
+ )
+ ]));
+
+ return $set;
+ }
+}
diff --git a/library/Icingadb/Setup/RedisPage.php b/library/Icingadb/Setup/RedisPage.php
new file mode 100644
index 0000000..3c0a741
--- /dev/null
+++ b/library/Icingadb/Setup/RedisPage.php
@@ -0,0 +1,68 @@
+<?php
+
+/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Icingadb\Setup;
+
+use Icinga\Module\Icingadb\Forms\RedisConfigForm;
+use Icinga\Web\Form;
+
+class RedisPage extends Form
+{
+ public function init()
+ {
+ $this->setName('setup_icingadb_redis');
+ $this->setTitle(t('Icinga DB Redis'));
+ $this->addDescription(t(
+ 'Please fill out the connection details to access the Icinga DB Redis.'
+ ));
+ $this->setValidatePartial(true);
+ }
+
+ public function createElements(array $formData)
+ {
+ $redisConfigForm = new RedisConfigForm();
+ $redisConfigForm->createElements($formData);
+ if (isset($formData['redis_tls']) && $formData['redis_tls']) {
+ $redisConfigForm->getElement('redis_ca_pem')->setIgnore(false);
+ $redisConfigForm->getElement('redis_cert_pem')->setIgnore(false);
+ $redisConfigForm->getElement('redis_key_pem')->setIgnore(false);
+ }
+
+ $this->addElements($redisConfigForm->getElements());
+ $this->addDisplayGroups($redisConfigForm->getDisplayGroups());
+ }
+
+ public function isValid($formData)
+ {
+ if (! parent::isValid($formData)) {
+ return false;
+ }
+
+ if (($el = $this->getElement('skip_validation')) === null || ! $el->isChecked()) {
+ if (! RedisConfigForm::checkRedis($this)) {
+ if ($el === null) {
+ RedisConfigForm::addSkipValidationCheckbox($this);
+ RedisConfigForm::addInsecureCheckboxIfTls($this);
+ }
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function isValidPartial(array $formData)
+ {
+ if (! parent::isValidPartial($formData)) {
+ return false;
+ }
+
+ if (isset($formData['backend_validation'])) {
+ return RedisConfigForm::checkRedis($this);
+ }
+
+ return true;
+ }
+}
diff --git a/library/Icingadb/Setup/RedisStep.php b/library/Icingadb/Setup/RedisStep.php
new file mode 100644
index 0000000..97e50e0
--- /dev/null
+++ b/library/Icingadb/Setup/RedisStep.php
@@ -0,0 +1,205 @@
+<?php
+
+/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Icingadb\Setup;
+
+use Exception;
+use Icinga\Application\Config;
+use Icinga\Application\Icinga;
+use Icinga\Exception\IcingaException;
+use Icinga\Exception\NotWritableError;
+use Icinga\File\Storage\LocalFileStorage;
+use Icinga\Module\Setup\Step;
+use ipl\Html\Attributes;
+use ipl\Html\HtmlDocument;
+use ipl\Html\HtmlElement;
+use ipl\Html\Table;
+use ipl\Html\Text;
+
+class RedisStep extends Step
+{
+ /** @var array */
+ protected $data;
+
+ /** @var Exception */
+ protected $error;
+
+ public function __construct(array $data)
+ {
+ $this->data = $data;
+ }
+
+ public function apply()
+ {
+ $moduleConfig = [
+ 'redis' => [
+ 'tls' => 0
+ ]
+ ];
+ $redisConfig = [
+ 'redis1' => [
+ 'host' => $this->data['redis1_host'],
+ 'port' => $this->data['redis1_port'] ?: null,
+ 'password' => $this->data['redis1_password'] ?: null
+ ]
+ ];
+ if (isset($this->data['redis2_host']) && $this->data['redis2_host']) {
+ $redisConfig['redis2'] = [
+ 'host' => $this->data['redis2_host'],
+ 'port' => $this->data['redis2_port'] ?: null,
+ 'password' => $this->data['redis2_password'] ?: null
+ ];
+ }
+
+ if (isset($this->data['redis_tls']) && $this->data['redis_tls']) {
+ $moduleConfig['redis']['tls'] = 1;
+ if (isset($this->data['redis_insecure']) && $this->data['redis_insecure']) {
+ $moduleConfig['redis']['insecure'] = 1;
+ }
+
+ $storage = new LocalFileStorage(Icinga::app()->getStorageDir(
+ join(DIRECTORY_SEPARATOR, ['modules', 'icingadb', 'redis'])
+ ));
+ foreach (['ca', 'cert', 'key'] as $name) {
+ $textareaName = 'redis_' . $name . '_pem';
+ if (isset($this->data[$textareaName]) && $this->data[$textareaName]) {
+ $pem = $this->data[$textareaName];
+ $pemFile = md5($pem) . '-' . $name . '.pem';
+ if (! $storage->has($pemFile)) {
+ try {
+ $storage->create($pemFile, $pem);
+ } catch (NotWritableError $e) {
+ $this->error = $e;
+ return false;
+ }
+ }
+
+ $moduleConfig['redis'][$name] = $storage->resolvePath($pemFile);
+ }
+ }
+ }
+
+ try {
+ $config = Config::module('icingadb', 'config', true);
+ foreach ($moduleConfig as $section => $options) {
+ $config->setSection($section, $options);
+ }
+
+ $config->saveIni();
+
+ $config = Config::module('icingadb', 'redis', true);
+ foreach ($redisConfig as $section => $options) {
+ $config->setSection($section, $options);
+ }
+
+ $config->saveIni();
+ } catch (Exception $e) {
+ $this->error = $e;
+ return false;
+ }
+
+ return true;
+ }
+
+ public function getSummary()
+ {
+ $topic = new HtmlElement('div', Attributes::create(['class' => 'topic']));
+ $topic->addHtml(new HtmlElement('p', null, Text::create(mt(
+ 'icingadb',
+ 'The Icinga DB Redis will be accessed using the following connection details:'
+ ))));
+
+ $primaryOptions = new Table();
+ $primaryOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Host'))),
+ $this->data['redis1_host']
+ ]));
+ $primaryOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Port'))),
+ $this->data['redis1_port'] ?: 6380
+ ]));
+ $primaryOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Password'))),
+ $this->data['redis1_password'] ?: mt('icingadb', 'None', 'non-existence of a value')
+ ]));
+
+ if (isset($this->data['redis2_host']) && $this->data['redis2_host']) {
+ $topic->addHtml(
+ new HtmlElement('h3', null, Text::create(mt('icingadb', 'Primary'))),
+ $primaryOptions
+ );
+
+ $secondaryOptions = new Table();
+ $secondaryOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Host'))),
+ $this->data['redis2_host']
+ ]));
+ $secondaryOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Port'))),
+ $this->data['redis2_port'] ?: 6380
+ ]));
+ $secondaryOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create(t('Password'))),
+ $this->data['redis2_password'] ?: mt('icingadb', 'None', 'non-existence of a value')
+ ]));
+
+ $topic->addHtml(
+ new HtmlElement('h3', null, Text::create(mt('icingadb', 'Secondary'))),
+ $secondaryOptions
+ );
+ } else {
+ $topic->addHtml($primaryOptions);
+ }
+
+ $tlsOptions = new Table();
+ $topic->addHtml($tlsOptions);
+ if (isset($this->data['redis_tls']) && $this->data['redis_tls']) {
+ if (isset($this->data['redis_cert_pem']) && $this->data['redis_cert_pem']) {
+ $tlsOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create('TLS')),
+ Text::create(
+ t('Icinga DB Web will authenticate against Redis with a client'
+ . ' certificate and private key over a secured connection')
+ )
+ ]));
+ } else {
+ $tlsOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create('TLS')),
+ Text::create(t('Icinga DB Web will use secured Redis connections'))
+ ]));
+ }
+ } else {
+ $tlsOptions->addHtml(Table::row([
+ new HtmlElement('strong', null, Text::create('TLS')),
+ Text::create(t('No'))
+ ]));
+ }
+
+ $summary = new HtmlDocument();
+ $summary->addHtml(
+ new HtmlElement('h2', null, Text::create(mt('icingadb', 'Icinga DB Redis'))),
+ $topic
+ );
+
+ return $summary->render();
+ }
+
+ public function getReport()
+ {
+ if ($this->error === null) {
+ return [sprintf(
+ mt('icingadb', 'Module configuration update successful: %s'),
+ Config::module('icingab')->getConfigFile()
+ )];
+ } else {
+ return [
+ sprintf(
+ mt('icingadb', 'Module configuration update failed: %s'),
+ Config::module('icingab')->getConfigFile()
+ ),
+ sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->error))
+ ];
+ }
+ }
+}
diff --git a/library/Icingadb/Setup/WelcomePage.php b/library/Icingadb/Setup/WelcomePage.php
new file mode 100644
index 0000000..9f97c7d
--- /dev/null
+++ b/library/Icingadb/Setup/WelcomePage.php
@@ -0,0 +1,56 @@
+<?php
+
+/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Icingadb\Setup;
+
+use Icinga\Web\Form;
+
+class WelcomePage extends Form
+{
+ public function init()
+ {
+ $this->setName('setup_icingadb_welcome');
+ }
+
+ public function createElements(array $formData)
+ {
+ $this->addElement(
+ 'note',
+ 'welcome',
+ array(
+ 'value' => t(
+ 'Welcome to the configuration of Icinga DB Web!'
+ ),
+ 'decorators' => array(
+ 'ViewHelper',
+ array('HtmlTag', array('tag' => 'h2'))
+ )
+ )
+ );
+
+ $this->addElement(
+ 'note',
+ 'description_1',
+ array(
+ 'value' => '<p>' . t(
+ 'Icinga DB Web is the UI for Icinga DB and provides'
+ . ' a graphical interface to your monitoring environment.'
+ ) . '</p>',
+ 'decorators' => array('ViewHelper')
+ )
+ );
+
+ $this->addElement(
+ 'note',
+ 'description_2',
+ array(
+ 'value' => '<p>' . t(
+ 'The wizard will guide you through the configuration to'
+ . ' establish a connection with Icinga DB and Icinga 2.'
+ ) . '</p>',
+ 'decorators' => array('ViewHelper')
+ )
+ );
+ }
+}