icingaweb2-module-director/application/controllers/ConfigController.php

544 lines
18 KiB
PHP
Raw Normal View History

<?php
2015-10-20 22:34:04 +02:00
namespace Icinga\Module\Director\Controllers;
use gipfl\Diff\HtmlRenderer\SideBySideDiff;
use gipfl\Diff\PhpDiff;
2020-10-26 18:50:13 +01:00
use gipfl\Web\Widget\Hint;
use Icinga\Data\Filter\Filter;
use Icinga\Exception\IcingaException;
use Icinga\Exception\NotFoundError;
use Icinga\Module\Director\Auth\Permission;
use Icinga\Module\Director\Db\Branch\Branch;
use Icinga\Module\Director\Deployment\DeploymentStatus;
use Icinga\Module\Director\Forms\DeployConfigForm;
use Icinga\Module\Director\Forms\SettingsForm;
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
use Icinga\Module\Director\Objects\DirectorDeploymentLog;
use Icinga\Module\Director\Settings;
2021-08-16 11:43:09 +02:00
use Icinga\Module\Director\Web\Controller\BranchHelper;
use Icinga\Module\Director\Web\Table\ActivityLogTable;
2021-08-16 11:43:09 +02:00
use Icinga\Module\Director\Web\Table\BranchActivityTable;
use Icinga\Module\Director\Web\Table\ConfigFileDiffTable;
use Icinga\Module\Director\Web\Table\DeploymentLogTable;
use Icinga\Module\Director\Web\Table\GeneratedConfigFileTable;
use Icinga\Module\Director\Web\Controller\ActionController;
use Icinga\Module\Director\Web\Tabs\InfraTabs;
use Icinga\Module\Director\Web\Widget\ActivityLogInfo;
use Icinga\Module\Director\Web\Widget\DeployedConfigInfoHeader;
use Icinga\Module\Director\Web\Widget\ShowConfigFile;
2015-09-30 08:40:09 +02:00
use Icinga\Web\Notification;
use Exception;
use RuntimeException;
use ipl\Html\Html;
use ipl\Html\HtmlString;
use gipfl\IcingaWeb2\Icon;
use gipfl\IcingaWeb2\Link;
use gipfl\IcingaWeb2\Url;
2015-10-20 22:34:04 +02:00
class ConfigController extends ActionController
{
2021-08-16 11:43:09 +02:00
use BranchHelper;
protected $isApified = true;
protected function checkDirectorPermissions()
{
}
/**
* @throws \Icinga\Security\SecurityException
*/
public function deploymentsAction()
{
if ($this->sendNotFoundForRestApi()) {
return;
}
$this->assertPermission('director/deploy');
$this->addTitle($this->translate('Deployments'));
try {
if (DirectorDeploymentLog::hasUncollected($this->db())) {
$this->setAutorefreshInterval(2);
} else {
$this->setAutorefreshInterval(20);
}
} catch (Exception $e) {
2020-10-26 18:50:13 +01:00
$this->content()->prepend(Hint::warning($e->getMessage()));
// No problem, Icinga might be reloading
}
if (! $this->getBranch()->isBranch()) {
// TODO: a form!
$this->actions()->add(Link::create(
$this->translate('Render config'),
'director/config/store',
null,
['class' => 'icon-wrench']
));
}
$this->tabs(new InfraTabs($this->Auth()))->activate('deploymentlog');
$table = new DeploymentLogTable($this->db());
try {
// Move elsewhere
$table->setActiveStageName(
$this->api()->getActiveStageName()
);
} catch (Exception $e) {
// Don't care
}
$table->renderTo($this);
}
/**
* @throws NotFoundError
* @throws \Icinga\Module\Director\Exception\DuplicateKeyException
* @throws \Icinga\Security\SecurityException
*/
2015-09-30 08:40:09 +02:00
public function deployAction()
{
$request = $this->getRequest();
if (! $request->isApiRequest()) {
throw new NotFoundError('Not found');
}
if (! $request->isPost()) {
throw new RuntimeException(sprintf(
'Unsupported method: %s',
$request->getMethod()
));
}
$this->assertPermission('director/deploy');
2016-02-19 12:53:47 +01:00
// TODO: require POST
2015-09-30 08:40:09 +02:00
$checksum = $this->params->get('checksum');
if ($checksum) {
$config = IcingaConfig::load(hex2bin($checksum), $this->db());
} else {
$config = IcingaConfig::generate($this->db());
$checksum = $config->getHexChecksum();
}
try {
$this->api()->wipeInactiveStages($this->db());
} catch (Exception $e) {
$this->deploymentFailed($checksum, $e->getMessage());
}
2015-09-30 08:40:09 +02:00
if ($this->api()->dumpConfig($config, $this->db())) {
$this->deploymentSucceeded($checksum);
2015-09-30 08:40:09 +02:00
} else {
$this->deploymentFailed($checksum);
}
}
public function deploymentStatusAction()
{
if ($this->sendNotFoundUnlessRestApi()) {
return;
}
2020-10-07 15:04:08 +02:00
$db = $this->db();
$api = $this->api();
$status = new DeploymentStatus($db, $api);
2020-10-07 16:53:32 +02:00
$result = $status->getDeploymentStatus($this->params->get('configs'), $this->params->get('activities'));
$this->sendJson($this->getResponse(), (object) $result);
}
/**
* @throws \Icinga\Security\SecurityException
*/
public function activitiesAction()
{
if ($this->sendNotFoundForRestApi()) {
return;
}
$this->assertPermission('director/audit');
$this->showOptionalBranchActivity();
$this->setAutorefreshInterval(10);
$this->tabs(new InfraTabs($this->Auth()))->activate('activitylog');
$this->addTitle($this->translate('Activity Log'));
$lastDeployedId = $this->db()->getLastDeploymentActivityLogId();
$table = new ActivityLogTable($this->db());
$table->setLastDeployedId($lastDeployedId);
if ($idRangeEx = $this->url()->getParam('idRangeEx')) {
$table->applyFilter(Filter::fromQueryString($idRangeEx));
}
$filter = Filter::fromQueryString(
$this->url()->without(['page', 'limit', 'q', 'idRangeEx'])->getQueryString()
);
$table->applyFilter($filter);
if ($this->url()->hasParam('author')) {
$this->actions()->add(Link::create(
$this->translate('All changes'),
$this->url()
->without(['author', 'page']),
null,
['class' => 'icon-users', 'data-base-target' => '_self']
));
} else {
$this->actions()->add(Link::create(
$this->translate('My changes'),
$this->url()
->with('author', $this->Auth()->getUser()->getUsername())
->without('page'),
null,
['class' => 'icon-user', 'data-base-target' => '_self']
));
}
if ($this->hasPermission(Permission::DEPLOY) && ! $this->getBranch()->isBranch()) {
if ($this->db()->hasDeploymentEndpoint()) {
$this->actions()->add(DeployConfigForm::load()
->setDb($this->db())
->setApi($this->api())
->handleRequest());
}
}
$table->renderTo($this);
}
/**
* @throws IcingaException
* @throws \Icinga\Exception\Http\HttpNotFoundException
* @throws \Icinga\Exception\ProgrammingError
*/
public function activityAction()
{
if ($this->sendNotFoundForRestApi()) {
return;
}
$this->assertPermission('director/showconfig');
$p = $this->params;
$info = new ActivityLogInfo(
$this->db(),
$p->get('type'),
$p->get('name')
);
$info->setChecksum($p->get('checksum'))
->setId($p->get('id'));
$this->tabs($info->getTabs($this->url()));
$info->showTab($this->params->get('show'));
$this->addTitle($info->getTitle());
$this->controls()->prepend($info->getPagination($this->url()));
$this->content()->add($info);
}
/**
* @throws \Icinga\Security\SecurityException
*/
public function settingsAction()
{
if ($this->sendNotFoundForRestApi()) {
return;
}
$this->assertPermission('director/admin');
$this->addSingleTab($this->translate('Settings'))
->addTitle($this->translate('Global Director Settings'));
$this->content()->add(
SettingsForm::load()
->setSettings(new Settings($this->db()))
->handleRequest()
);
}
/**
* Show all files for a given config
*
* @throws \Icinga\Exception\MissingParameterException
* @throws \Icinga\Security\SecurityException
*/
public function filesAction()
{
if ($this->sendNotFoundForRestApi()) {
return;
}
$this->assertPermission('director/showconfig');
$config = IcingaConfig::load(
hex2bin($this->params->getRequired('checksum')),
$this->db()
);
$deploymentId = $this->params->get('deployment_id');
$tabs = $this->tabs();
if ($deploymentId) {
$tabs->add('deployment', [
'label' => $this->translate('Deployment'),
'url' => 'director/deployment',
'urlParams' => ['id' => $deploymentId]
]);
}
$tabs->add('config', [
'label' => $this->translate('Config'),
'url' => $this->url(),
])->activate('config');
$this->addTitle($this->translate('Generated config'));
$this->content()->add(new DeployedConfigInfoHeader(
$config,
$this->db(),
$this->api(),
$this->getBranch(),
$deploymentId
));
GeneratedConfigFileTable::load($config, $this->db())
->setActiveFilename($this->params->get('active_file'))
->setDeploymentId($deploymentId)
->renderTo($this);
}
/**
* Show a single file
*
* @throws \Icinga\Exception\MissingParameterException
* @throws \Icinga\Security\SecurityException
*/
2015-12-17 10:54:38 +01:00
public function fileAction()
{
if ($this->sendNotFoundForRestApi()) {
return;
}
$this->assertPermission('director/showconfig');
$filename = $this->params->getRequired('file_path');
$this->configTabs()->add('file', array(
'label' => $this->translate('Rendered file'),
'url' => $this->url(),
2016-03-23 01:31:33 +01:00
))->activate('file');
$params = $this->getConfigTabParams();
if ('deployment' === $this->params->get('backTo')) {
$this->addBackLink('director/deployment', ['id' => $params['deployment_id']]);
2016-07-28 09:11:09 +02:00
} else {
$params['active_file'] = $filename;
$this->addBackLink('director/config/files', $params);
2016-07-28 09:11:09 +02:00
}
2016-03-23 01:31:33 +01:00
$config = IcingaConfig::load(hex2bin($this->params->get('config_checksum')), $this->db());
$this->addTitle($this->translate('Config file "%s"'), $filename);
$this->content()->add(new ShowConfigFile(
$config->getFile($filename),
$this->params->get('highlight'),
$this->params->get('highlightSeverity')
));
}
2015-06-17 18:57:51 +02:00
/**
* TODO: Check if this can be removed
*
* @throws \Icinga\Security\SecurityException
*/
2015-06-17 18:57:51 +02:00
public function storeAction()
{
$this->assertPermission('director/deploy');
try {
$config = IcingaConfig::generate($this->db());
} catch (Exception $e) {
Notification::error($e->getMessage());
$this->redirectNow('director/config/deployments');
}
2015-06-29 10:13:39 +02:00
$this->redirectNow(
2016-02-26 12:42:21 +01:00
Url::fromPath(
'director/config/files',
2016-02-26 12:42:21 +01:00
array('checksum' => $config->getHexChecksum())
)
2015-06-29 10:13:39 +02:00
);
2015-06-17 18:57:51 +02:00
}
/**
* @throws \Icinga\Security\SecurityException
*/
public function diffAction()
{
if ($this->sendNotFoundForRestApi()) {
return;
}
$this->assertPermission('director/showconfig');
$db = $this->db();
$this->addTitle($this->translate('Config diff'));
$this->addSingleTab($this->translate('Config diff'));
$leftSum = $this->params->get('left');
$rightSum = $this->params->get('right');
$configs = $db->enumDeployedConfigs();
foreach (array($leftSum, $rightSum) as $sum) {
if ($sum && ! array_key_exists($sum, $configs)) {
$configs[$sum] = substr($sum, 0, 7);
}
}
$baseUrl = $this->url()->without(['left', 'right']);
2024-01-11 13:10:17 +01:00
$this->content()->add(
Html::tag('form', ['action' => (string) $baseUrl, 'method' => 'GET', 'class' => 'director-form'], [
new HtmlString($this->view->formSelect(
'left',
$leftSum,
['class' => ['autosubmit', 'config-diff']],
[null => $this->translate('- please choose -')] + $configs
)),
Link::create(
Icon::create('flapping'),
$baseUrl,
['left' => $rightSum, 'right' => $leftSum]
),
new HtmlString($this->view->formSelect(
'right',
$rightSum,
['class' => ['autosubmit', 'config-diff']],
[null => $this->translate('- please choose -')] + $configs
)),
])
);
2022-01-10 11:44:33 +01:00
if ($rightSum === null || $leftSum === null || ! strlen($rightSum) || ! strlen($leftSum)) {
return;
}
ConfigFileDiffTable::load($leftSum, $rightSum, $this->db())->renderTo($this);
}
/**
* @throws IcingaException
* @throws \Icinga\Exception\MissingParameterException
*/
public function filediffAction()
{
if ($this->sendNotFoundForRestApi()) {
return;
}
$this->assertPermission('director/showconfig');
$p = $this->params;
$db = $this->db();
$leftSum = $p->getRequired('left');
$rightSum = $p->getRequired('right');
$filename = $p->getRequired('file_path');
$left = IcingaConfig::load(hex2bin($leftSum), $db);
$right = IcingaConfig::load(hex2bin($rightSum), $db);
$this
->addTitle($this->translate('Config file "%s"'), $filename)
->addSingleTab($this->translate('Diff'))
->content()->add(new SideBySideDiff(new PhpDiff(
$left->getFile($filename),
$right->getFile($filename)
)));
}
protected function showOptionalBranchActivity()
{
if ($this->url()->hasParam('idRangeEx')) {
return;
}
$branch = $this->getBranch();
if ($branch->isBranch() && (int) $this->params->get('page', '1') === 1) {
$table = new BranchActivityTable($branch->getUuid(), $this->db());
if (count($table) > 0) {
$this->content()->add(Hint::info(Html::sprintf($this->translate(
'The following modifications are visible in this %s only...'
), Branch::requireHook()->linkToBranch(
$branch,
$this->Auth(),
$this->translate('configuration branch')
))));
$this->content()->add($table);
$this->content()->add(Html::tag('br'));
$this->content()->add(Hint::ok($this->translate(
'...and the modifications below are already in the main branch:'
)));
$this->content()->add(Html::tag('br'));
}
}
}
/**
* @param $checksum
*/
protected function deploymentSucceeded($checksum)
{
if ($this->getRequest()->isApiRequest()) {
$this->sendJson($this->getResponse(), (object) array('checksum' => $checksum));
return;
} else {
$url = Url::fromPath('director/config/deployments');
Notification::success(
$this->translate('Config has been submitted, validation is going on')
);
$this->redirectNow($url);
}
}
/**
* @param $checksum
* @param null $error
*/
protected function deploymentFailed($checksum, $error = null)
{
2024-10-22 14:31:14 +02:00
$extra = $error ? ': ' . $error : '';
if ($this->getRequest()->isApiRequest()) {
$this->sendJsonError($this->getResponse(), 'Config deployment failed' . $extra);
return;
} else {
$url = Url::fromPath('director/config/files', array('checksum' => $checksum));
Notification::error(
$this->translate('Config deployment failed') . $extra
);
$this->redirectNow($url);
}
}
/**
* @return \gipfl\IcingaWeb2\Widget\Tabs
*/
2016-03-23 01:31:33 +01:00
protected function configTabs()
{
$tabs = $this->tabs();
2016-03-23 01:31:33 +01:00
2024-10-22 14:31:14 +02:00
if (
$this->hasPermission(Permission::DEPLOY)
&& $deploymentId = $this->params->get('deployment_id')
) {
$tabs->add('deployment', [
2016-03-23 01:31:33 +01:00
'label' => $this->translate('Deployment'),
'url' => 'director/deployment',
'urlParams' => ['id' => $deploymentId]
]);
2016-03-23 01:31:33 +01:00
}
if ($this->hasPermission(Permission::SHOW_CONFIG)) {
$tabs->add('config', [
'label' => $this->translate('Config'),
'url' => 'director/config/files',
'urlParams' => $this->getConfigTabParams()
]);
}
2016-03-23 01:31:33 +01:00
return $tabs;
}
protected function getConfigTabParams()
{
$params = [
2016-03-23 01:31:33 +01:00
'checksum' => $this->params->get(
'config_checksum',
$this->params->get('checksum')
)
];
2016-03-23 01:31:33 +01:00
if ($deploymentId = $this->params->get('deployment_id')) {
$params['deployment_id'] = $deploymentId;
}
return $params;
}
}