491 lines
15 KiB
PHP
491 lines
15 KiB
PHP
<?php
|
|
|
|
namespace Icinga\Module\Director\Controllers;
|
|
|
|
use Icinga\Data\Filter\Filter;
|
|
use Icinga\Exception\IcingaException;
|
|
use Icinga\Exception\NotFoundError;
|
|
use Icinga\Module\Director\ConfigDiff;
|
|
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;
|
|
use Icinga\Module\Director\Web\Table\ActivityLogTable;
|
|
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\Util;
|
|
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;
|
|
use Icinga\Web\Notification;
|
|
use Exception;
|
|
use RuntimeException;
|
|
use dipl\Html\Html;
|
|
use dipl\Html\HtmlString;
|
|
use dipl\Html\Icon;
|
|
use dipl\Html\Link;
|
|
use dipl\Web\Url;
|
|
|
|
class ConfigController extends ActionController
|
|
{
|
|
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(3);
|
|
$this->api()->collectLogFiles($this->db());
|
|
} else {
|
|
$this->setAutorefreshInterval(20);
|
|
}
|
|
} catch (Exception $e) {
|
|
$this->content()->prepend(
|
|
Html::tag('p', ['class' => 'warning'], $e->getMessage())
|
|
);
|
|
// No problem, Icinga might be reloading
|
|
}
|
|
|
|
// 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
|
|
*/
|
|
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');
|
|
|
|
// TODO: require POST
|
|
$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());
|
|
}
|
|
|
|
if ($this->api()->dumpConfig($config, $this->db())) {
|
|
$this->deploymentSucceeded($checksum);
|
|
} else {
|
|
$this->deploymentFailed($checksum);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @throws \Icinga\Security\SecurityException
|
|
*/
|
|
public function activitiesAction()
|
|
{
|
|
if ($this->sendNotFoundForRestApi()) {
|
|
return;
|
|
}
|
|
$this->assertPermission('director/audit');
|
|
|
|
$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);
|
|
$filter = Filter::fromQueryString(
|
|
$this->url()->without(['page', 'limit', 'q'])->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('director/deploy')) {
|
|
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(),
|
|
$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
|
|
*/
|
|
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(),
|
|
))->activate('file');
|
|
|
|
$params = $this->getConfigTabParams();
|
|
if ('deployment' === $this->params->get('backTo')) {
|
|
$this->addBackLink('director/deployment', ['id' => $params['deployment_id']]);
|
|
} else {
|
|
$params['active_file'] = $filename;
|
|
$this->addBackLink('director/config/files', $params);
|
|
}
|
|
|
|
$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')
|
|
));
|
|
}
|
|
|
|
/**
|
|
* TODO: Check if this can be removed
|
|
*
|
|
* @throws \Icinga\Security\SecurityException
|
|
*/
|
|
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');
|
|
}
|
|
$this->redirectNow(
|
|
Url::fromPath(
|
|
'director/config/files',
|
|
array('checksum' => $config->getHexChecksum())
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @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 (! array_key_exists($sum, $configs)) {
|
|
$configs[$sum] = substr($sum, 0, 7);
|
|
}
|
|
}
|
|
|
|
$baseUrl = $this->url()->without(['left', 'right']);
|
|
$this->content()->add(Html::tag('form', ['action' => (string) $baseUrl, 'method' => 'GET'], [
|
|
new HtmlString($this->view->formSelect(
|
|
'left',
|
|
$leftSum,
|
|
['class' => 'autosubmit', 'style' => 'width: 37%'],
|
|
[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', 'style' => 'width: 37%'],
|
|
[null => $this->translate('- please choose -')] + $configs
|
|
)),
|
|
]));
|
|
|
|
if (! 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(ConfigDiff::create(
|
|
$left->getFile($filename),
|
|
$right->getFile($filename)
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @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)
|
|
{
|
|
$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 \dipl\Web\Widget\Tabs
|
|
*/
|
|
protected function configTabs()
|
|
{
|
|
$tabs = $this->tabs();
|
|
|
|
if ($this->hasPermission('director/deploy')
|
|
&& $deploymentId = $this->params->get('deployment_id')
|
|
) {
|
|
$tabs->add('deployment', [
|
|
'label' => $this->translate('Deployment'),
|
|
'url' => 'director/deployment',
|
|
'urlParams' => ['id' => $deploymentId]
|
|
]);
|
|
}
|
|
|
|
if ($this->hasPermission('director/showconfig')) {
|
|
$tabs->add('config', [
|
|
'label' => $this->translate('Config'),
|
|
'url' => 'director/config/files',
|
|
'urlParams' => $this->getConfigTabParams()
|
|
]);
|
|
}
|
|
|
|
return $tabs;
|
|
}
|
|
|
|
protected function getConfigTabParams()
|
|
{
|
|
$params = [
|
|
'checksum' => $this->params->get(
|
|
'config_checksum',
|
|
$this->params->get('checksum')
|
|
)
|
|
];
|
|
|
|
if ($deploymentId = $this->params->get('deployment_id')) {
|
|
$params['deployment_id'] = $deploymentId;
|
|
}
|
|
|
|
return $params;
|
|
}
|
|
}
|