Merge pull request #161 from Icinga/feature/native-command-implementation

Native command implementation
This commit is contained in:
Johannes Meyer 2021-03-09 09:16:14 +01:00 committed by GitHub
commit fa2b9d5847
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 4108 additions and 683 deletions

View file

@ -0,0 +1,147 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Controllers;
use GuzzleHttp\Psr7\ServerRequest;
use Icinga\Application\Config;
use Icinga\Data\Filter\Filter;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Module\Icingadb\Command\Transport\CommandTransportConfig;
use Icinga\Module\Icingadb\Forms\ApiTransportForm;
use Icinga\Module\Icingadb\Widget\ItemList\CommandTransportList;
use ipl\Html\HtmlString;
use ipl\Web\Widget\ButtonLink;
class CommandTransportController extends ConfigController
{
public function init()
{
$this->assertPermission('config/modules');
}
public function indexAction()
{
$list = new CommandTransportList((new CommandTransportConfig())->select());
$this->addControl(
(new ButtonLink(
t('Create Command Transport'),
'icingadb/command-transport/add',
'plus'
))->setBaseTarget('_next')
);
$this->addContent($list);
$this->mergeTabs($this->Module()->getConfigTabs());
$this->getTabs()->disableLegacyExtensions();
$this->view->title = $this->getTabs()
->activate('command-transports')
->getActiveTab()
->getLabel();
}
public function showAction()
{
$transportName = $this->params->getRequired('name');
$transportConfig = (new CommandTransportConfig())
->select()
->where('name', $transportName)
->fetchRow();
if ($transportConfig === false) {
$this->httpNotFound(t('Unknown transport'));
}
$form = new ApiTransportForm();
$form->populate((array) $transportConfig);
$form->on(ApiTransportForm::ON_SUCCESS, function (ApiTransportForm $form) use ($transportName) {
(new CommandTransportConfig())->update(
'transport',
$form->getValues(),
Filter::where('name', $transportName)
);
$this->redirectNow('icingadb/command-transport');
});
$form->handleRequest(ServerRequest::fromGlobals());
$this->addContent($form);
$this->setTitle(sprintf($this->translate('Command Transport: %s'), $transportName));
$this->getTabs()->disableLegacyExtensions();
}
public function addAction()
{
$form = new ApiTransportForm();
$form->on(ApiTransportForm::ON_SUCCESS, function (ApiTransportForm $form) {
(new CommandTransportConfig())->insert('transport', $form->getValues());
$this->redirectNow('icingadb/command-transport');
});
$form->handleRequest(ServerRequest::fromGlobals());
$this->addContent($form);
$this->setTitle($this->translate('Add Command Transport'));
$this->getTabs()->disableLegacyExtensions();
}
public function removeAction()
{
$transportName = $this->params->getRequired('name');
$form = new ConfirmRemovalForm();
$form->setAttrib('style', 'text-align:center;');
$form->setOnSuccess(function () use ($transportName) {
(new CommandTransportConfig())->delete(
'transport',
Filter::where('name', $transportName)
);
$this->redirectNow('icingadb/command-transport');
});
$form->handleRequest();
$this->addContent(HtmlString::create($form->render()));
$this->setTitle($this->translate('Remove Command Transport: %s'), $transportName);
$this->getTabs()->disableLegacyExtensions();
}
public function sortAction()
{
$transportName = $this->params->getRequired('name');
$newPosition = (int) $this->params->getRequired('pos');
$config = $this->Config('commandtransports');
if (! $config->hasSection($transportName)) {
$this->httpNotFound(t('Unknown transport'));
}
if ($newPosition < 0 || $newPosition > $config->count()) {
$this->httpBadRequest(t('Position out of bounds'));
}
$transports = $config->getConfigObject()->toArray();
$transportNames = array_keys($transports);
array_splice($transportNames, array_search($transportName, $transportNames, true), 1);
array_splice($transportNames, $newPosition, 0, [$transportName]);
$sortedTransports = [];
foreach ($transportNames as $name) {
$sortedTransports[$name] = $transports[$name];
}
$newConfig = Config::fromArray($sortedTransports);
$newConfig->saveIni($config->getConfigFile());
$this->redirectNow('icingadb/command-transport');
}
}

View file

@ -6,19 +6,16 @@ namespace Icinga\Module\Icingadb\Controllers;
use GuzzleHttp\Psr7\ServerRequest;
use Icinga\Module\Icingadb\Common\Links;
use Icinga\Module\Icingadb\Forms\Command\Object\DeleteCommentForm;
use Icinga\Module\Icingadb\Model\Comment;
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
use Icinga\Module\Icingadb\Web\Controller;
use Icinga\Module\Icingadb\Widget\ContinueWith;
use Icinga\Module\Icingadb\Widget\ItemList\CommentList;
use Icinga\Module\Icingadb\Widget\ShowMore;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentsCommandForm;
use ipl\Html\HtmlDocument;
use ipl\Html\HtmlString;
use ipl\Web\Filter\QueryString;
use ipl\Web\Url;
use ipl\Web\Widget\ActionLink;
use ipl\Web\Widget\Icon;
class CommentsController extends Controller
{
@ -109,6 +106,8 @@ class CommentsController extends Controller
public function deleteAction()
{
// TODO: Check permission
$this->setTitle(t('Remove Comments'));
$db = $this->getDb();
@ -124,40 +123,19 @@ class CommentsController extends Controller
$this->filter($comments);
$deleteCommentsForm = (new DeleteCommentsCommandForm())
->addDescription(sprintf(
t('Confirm removal of %d comments.'),
$comments->count()
))
->setComments($comments)
->setRedirectUrl(Links::comments())
->create();
$form = (new DeleteCommentForm())
->setObjects($comments)
->setRedirectUrl(Links::comments()->getAbsoluteUrl())
->on(DeleteCommentForm::ON_SUCCESS, function ($form) {
// This forces the column to reload nearly instantly after the redirect
// and ensures the effect of the command is visible to the user asap
$this->getResponse()->setAutoRefreshInterval(1);
$deleteCommentsForm->removeElement('btn_submit');
$this->redirectNow($form->getRedirectUrl());
})
->handleRequest(ServerRequest::fromGlobals());
$deleteCommentsForm->addElement(
'button',
'btn_submit',
[
'class' => 'cancel-button spinner',
'decorators' => [
'ViewHelper',
['HtmlTag', ['tag' => 'div', 'class' => 'control-group form-controls']]
],
'escape' => false,
'ignore' => true,
'label' => (new HtmlDocument())
->add([new Icon('trash'), t('Remove Comments')])
->setSeparator(' ')
->render(),
'title' => t('Remove comments'),
'type' => 'submit'
]
);
$deleteCommentsForm->handleRequest();
$this->addContent(HtmlString::create($deleteCommentsForm->render()));
$this->addContent($form);
}
public function detailsAction()
@ -191,15 +169,16 @@ class CommentsController extends Controller
sprintf(t('Show all %d comments'), $comments->count())
));
$this->addContent((new ActionLink(
sprintf(t('Remove %d comments'), $comments->count()),
Links::commentsDelete()->setQueryString(QueryString::render($this->getFilter())),
'trash',
[
'data-icinga-modal' => true,
'data-no-icinga-ajax' => true
]
))->setAttribute('class', 'cancel-button'));
// TODO: Check permission
$this->addContent(
(new DeleteCommentForm())
->setObjects($comments)
->setAction(
Links::commentsDelete()
->setQueryString(QueryString::render($this->getFilter()))
->getAbsoluteUrl()
)
);
}
public function completeAction()

View file

@ -4,22 +4,15 @@
namespace Icinga\Module\Icingadb\Controllers;
use Exception;
use Icinga\Application\Config;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Module\Icingadb\Forms\DatabaseConfigForm;
use Icinga\Module\Icingadb\Forms\RedisConfigForm;
use Icinga\Module\Icingadb\Web\Controller;
use Icinga\Module\Monitoring\Forms\Config\SecurityConfigForm;
use Icinga\Module\Monitoring\Forms\Config\TransportConfigForm;
use Icinga\Module\Monitoring\Forms\Config\TransportReorderForm;
use Icinga\Web\Form;
use Icinga\Web\Notification;
use Icinga\Web\Widget\Tab;
use Icinga\Web\Widget\Tabs;
use ipl\Html\HtmlString;
use ipl\Web\Widget\ButtonLink;
class ConfigController extends Controller
{
@ -30,142 +23,6 @@ class ConfigController extends Controller
// parent::init();
// }
public function commandTransportsAction()
{
$form = new TransportReorderForm();
$form->handleRequest();
$this->mergeTabs($this->Module()->getConfigTabs()->activate('command-transports'));
$this->addControl(
(new ButtonLink(
t('Create Command Transport'),
'icingadb/config/create-command-transport',
'plus'
))->setBaseTarget('_next')
);
$this->addFormToContent($form);
}
public function createCommandTransportAction()
{
$this->setTitle(t('Create Command Transport'));
$form = (new TransportConfigForm())
->setIniConfig(Config::module('monitoring', 'commandtransports'))
->setRedirectUrl('icingadb/config/command-transports');
$form->setOnSuccess(function (TransportConfigForm $form) {
try {
$form->add($form::transformEmptyValuesToNull($form->getValues()));
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($form->save()) {
Notification::success(t('Command transport successfully created'));
return true;
}
return false;
});
$form->handleRequest();
$this->addFormToContent($form);
}
public function deleteCommandTransportAction()
{
$this->setTitle(t('Delete Command Transport'));
$transportName = $this->params->getRequired('transport');
$transportConfigForm = (new TransportConfigForm())
->setIniConfig(Config::module('monitoring', 'commandtransports'));
$confirmRemovalForm = (new ConfirmRemovalForm())
->setRedirectUrl('icingadb/config/command-transports');
$confirmRemovalForm->setOnSuccess(
function (ConfirmRemovalForm $form) use ($transportName, $transportConfigForm) {
try {
$transportConfigForm->delete($transportName);
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($transportConfigForm->save()) {
Notification::success(sprintf(
t('Command transport "%s" successfully removed'),
$transportName
));
return true;
}
return false;
}
);
$confirmRemovalForm->handleRequest();
$this->addFormToContent($confirmRemovalForm);
}
public function updateCommandTransportAction()
{
$this->setTitle(t('Update Command Transport'));
$transportName = $this->params->getRequired('transport');
$form = (new TransportConfigForm())
->setIniConfig(Config::module('monitoring', 'commandtransports'))
->setRedirectUrl('icingadb/config/command-transports');
$form->setOnSuccess(function (TransportConfigForm $form) use ($transportName) {
try {
$form->edit($transportName, array_map(
function ($v) {
return $v !== '' ? $v : null;
},
$form->getValues()
));
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($form->save()) {
Notification::success(sprintf(
t('Command transport "%s" successfully updated'),
$transportName
));
return true;
}
return false;
});
try {
$form->load($transportName);
$form->handleRequest();
} catch (NotFoundError $_) {
$this->httpNotFound(sprintf(t('Command transport "%s" not found'), $transportName));
}
$this->addFormToContent($form);
}
public function databaseAction()
{
$form = (new DatabaseConfigForm())

View file

@ -6,6 +6,7 @@ namespace Icinga\Module\Icingadb\Controllers;
use GuzzleHttp\Psr7\ServerRequest;
use Icinga\Module\Icingadb\Common\Links;
use Icinga\Module\Icingadb\Forms\Command\Object\DeleteDowntimeForm;
use Icinga\Module\Icingadb\Model\Downtime;
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
use Icinga\Module\Icingadb\Web\Controller;
@ -114,6 +115,8 @@ class DowntimesController extends Controller
public function deleteAction()
{
// TODO: Check permission
$this->setTitle(t('Cancel Downtimes'));
$db = $this->getDb();
@ -129,40 +132,19 @@ class DowntimesController extends Controller
$this->filter($downtimes);
$cancelDowntimesForm = (new DeleteDowntimesCommandForm())
->addDescription(sprintf(
t('Confirm cancellation of %d downtimes.'),
$downtimes->count()
))
->setDowntimes($downtimes)
->setRedirectUrl(Links::downtimes())
->create();
$form = (new DeleteDowntimeForm())
->setObjects($downtimes)
->setRedirectUrl(Links::downtimes()->getAbsoluteUrl())
->on(DeleteDowntimeForm::ON_SUCCESS, function ($form) {
// This forces the column to reload nearly instantly after the redirect
// and ensures the effect of the command is visible to the user asap
$this->getResponse()->setAutoRefreshInterval(1);
$cancelDowntimesForm->removeElement('btn_submit');
$this->redirectNow($form->getRedirectUrl());
})
->handleRequest(ServerRequest::fromGlobals());
$cancelDowntimesForm->addElement(
'button',
'btn_submit',
[
'class' => 'cancel-button spinner',
'decorators' => [
'ViewHelper',
['HtmlTag', ['tag' => 'div', 'class' => 'control-group form-controls']]
],
'escape' => false,
'ignore' => true,
'label' => (new HtmlDocument())
->add([new Icon('trash'), t('Cancel Downtimes')])
->setSeparator(' ')
->render(),
'title' => t('Cancel downtimes'),
'type' => 'submit'
]
);
$cancelDowntimesForm->handleRequest();
$this->addContent(HtmlString::create($cancelDowntimesForm->render()));
$this->addContent($form);
}
public function detailsAction()
@ -196,15 +178,16 @@ class DowntimesController extends Controller
sprintf(t('Show all %d downtimes'), $downtimes->count())
));
$this->addContent((new ActionLink(
sprintf(t('Cancel %d downtimes'), $downtimes->count()),
Links::downtimesDelete()->setQueryString(QueryString::render($this->getFilter())),
'trash',
[
'data-icinga-modal' => true,
'data-no-icinga-ajax' => true
]
))->setAttribute('class', 'cancel-button'));
// TODO: Check permission
$this->addContent(
(new DeleteDowntimeForm())
->setObjects($downtimes)
->setAction(
Links::downtimesDelete()
->setQueryString(QueryString::render($this->getFilter()))
->getAbsoluteUrl()
)
);
}
public function completeAction()

View file

@ -4,16 +4,18 @@
namespace Icinga\Module\Icingadb\Controllers;
use Icinga\Module\Icingadb\Compat\CompatBackend;
use GuzzleHttp\Psr7\ServerRequest;
use Icinga\Module\Icingadb\Command\Instance\ToggleInstanceFeatureCommand;
use Icinga\Module\Icingadb\Common\Links;
use Icinga\Module\Icingadb\Forms\Command\Instance\ToggleInstanceFeaturesForm;
use Icinga\Module\Icingadb\Model\HoststateSummary;
use Icinga\Module\Icingadb\Model\Instance;
use Icinga\Module\Icingadb\Model\ServicestateSummary;
use Icinga\Module\Icingadb\Web\Controller;
use Icinga\Module\Icingadb\Widget\Health;
use Icinga\Module\Icingadb\Widget\VerticalKeyValue;
use Icinga\Module\Monitoring\Forms\Command\Instance\ToggleInstanceFeaturesCommandForm;
use ipl\Html\Html;
use ipl\Html\HtmlString;
use ipl\Web\Url;
class HealthController extends Controller
{
@ -83,22 +85,27 @@ class HealthController extends Controller
['class' => 'instance-commands'],
Html::tag('h2', t('Feature Commands'))
);
$programStatus = (object) [
'active_host_checks_enabled' => $instance->icinga2_active_host_checks_enabled,
'active_service_checks_enabled' => $instance->icinga2_active_service_checks_enabled,
'event_handlers_enabled' => $instance->icinga2_event_handlers_enabled,
'flap_detection_enabled' => $instance->icinga2_flap_detection_enabled,
'notifications_enabled' => $instance->icinga2_notifications_enabled,
'process_performance_data' => $instance->icinga2_performance_data_enabled,
'program_version' => $instance->icinga2_version
];
$toggleInstanceFeaturesCommandForm = new ToggleInstanceFeaturesCommandForm();
$toggleInstanceFeaturesCommandForm
->setBackend(new CompatBackend())
->setStatus($programStatus)
->load($programStatus)
->handleRequest();
$featureCommands->add(HtmlString::create($toggleInstanceFeaturesCommandForm->render()));
$toggleInstanceFeaturesCommandForm = new ToggleInstanceFeaturesForm([
ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS =>
$instance->icinga2_active_host_checks_enabled,
ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS =>
$instance->icinga2_active_service_checks_enabled,
ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS =>
$instance->icinga2_event_handlers_enabled,
ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION =>
$instance->icinga2_flap_detection_enabled,
ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS =>
$instance->icinga2_notifications_enabled,
ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA =>
$instance->icinga2_performance_data_enabled
]);
$toggleInstanceFeaturesCommandForm->setObjects([$instance]);
$toggleInstanceFeaturesCommandForm->on(ToggleInstanceFeaturesForm::ON_SUCCESS, function () {
$this->redirectNow(Url::fromPath('icingadb/health')->getAbsoluteUrl());
});
$toggleInstanceFeaturesCommandForm->handleRequest(ServerRequest::fromGlobals());
$featureCommands->add($toggleInstanceFeaturesCommandForm);
$this->addContent($featureCommands);
$this->setAutorefreshInterval(30);

View file

@ -7,9 +7,9 @@ namespace Icinga\Module\Icingadb\Controllers;
use GuzzleHttp\Psr7\ServerRequest;
use Icinga\Module\Icingadb\Common\CommandActions;
use Icinga\Module\Icingadb\Common\Links;
use Icinga\Module\Icingadb\Compat\FeatureStatus;
use Icinga\Module\Icingadb\Model\Host;
use Icinga\Module\Icingadb\Model\HoststateSummary;
use Icinga\Module\Icingadb\Util\FeatureStatus;
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
use Icinga\Module\Icingadb\Web\Controller;
use Icinga\Module\Icingadb\Widget\ContinueWith;

View file

@ -7,9 +7,9 @@ namespace Icinga\Module\Icingadb\Controllers;
use GuzzleHttp\Psr7\ServerRequest;
use Icinga\Module\Icingadb\Common\CommandActions;
use Icinga\Module\Icingadb\Common\Links;
use Icinga\Module\Icingadb\Compat\FeatureStatus;
use Icinga\Module\Icingadb\Model\Service;
use Icinga\Module\Icingadb\Model\ServicestateSummary;
use Icinga\Module\Icingadb\Util\FeatureStatus;
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
use Icinga\Module\Icingadb\Web\Controller;
use Icinga\Module\Icingadb\Widget\ContinueWith;

View file

@ -0,0 +1,102 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms;
use Icinga\Data\ConfigObject;
use Icinga\Module\Icingadb\Command\Transport\CommandTransport;
use Icinga\Module\Icingadb\Command\Transport\CommandTransportException;
use Icinga\Web\Session;
use ipl\Web\Common\CsrfCounterMeasure;
use ipl\Web\Compat\CompatForm;
class ApiTransportForm extends CompatForm
{
use CsrfCounterMeasure;
protected function assemble()
{
// TODO: Use a validator to check if a name is not already in use
$this->addElement('text', 'name', [
'required' => true,
'label' => t('Transport Name')
]);
$this->addElement('hidden', 'transport', [
'value' => 'api'
]);
$this->addElement('text', 'host', [
'required' => true,
'id' => 'api_transport_host',
'label' => t('Host'),
'description' => t('Hostname or address of the Icinga master')
]);
// TODO: Don't rely only on browser validation
$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')
]);
// TODO: Use a password element
$this->addElement('text', 'password', [
'required' => true,
'label' => t('API Password')
]);
$this->addElement('submit', 'btn_submit', [
'label' => t('Save')
]);
$this->addElement($this->createCsrfCounterMeasure(Session::getSession()->getId()));
}
public function validate()
{
parent::validate();
if (! $this->isValid) {
return $this;
}
if ($this->getPopulatedValue('force_creation') === 'n') {
return $this;
}
try {
CommandTransport::createTransport(new ConfigObject($this->getValues()))->probe();
} catch (CommandTransportException $e) {
$this->addMessage(
sprintf(t('Failed to successfully validate the configuration: %s'), $e->getMessage())
);
$forceCheckbox = $this->createElement(
'checkbox',
'force_creation',
[
'ignore' => true,
'label' => t('Force Changes'),
'description' => t('Check this box to enforce changes without connectivity validation')
]
);
$this->registerElement($forceCheckbox);
$this->decorate($forceCheckbox);
$this->prepend($forceCheckbox);
$this->isValid = false;
}
return $this;
}
}

View file

@ -0,0 +1,125 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms\Command;
use Exception;
use Icinga\Application\Logger;
use Icinga\Module\Icingadb\Command\IcingaCommand;
use Icinga\Module\Icingadb\Command\Transport\CommandTransport;
use Icinga\Web\Notification;
use Icinga\Web\Session;
use ipl\Html\Form;
use ipl\Orm\Model;
use ipl\Web\Common\CsrfCounterMeasure;
abstract class CommandForm extends Form
{
use CsrfCounterMeasure;
protected $defaultAttributes = ['class' => 'icinga-form icinga-controls'];
/** @var mixed */
protected $objects;
/**
* Set the objects to issue the command for
*
* @param mixed $objects A traversable that is also countable
*
* @return $this
*/
public function setObjects($objects)
{
$this->objects = $objects;
return $this;
}
/**
* Get the objects to issue the command for
*
* @return mixed
*/
public function getObjects()
{
return $this->objects;
}
/**
* Create and add form elements representing the command's options
*
* @return void
*/
abstract protected function assembleElements();
/**
* Create and add a submit button to the form
*
* @return void
*/
abstract protected function assembleSubmitButton();
/**
* Get the command to issue for the given object
*
* @param Model $object
*
* @return IcingaCommand|IcingaCommand[]|null NULL in case no command should be issued for the object
*/
abstract protected function getCommand(Model $object);
protected function assemble()
{
$this->assembleElements();
$this->assembleSubmitButton();
$this->addElement($this->createCsrfCounterMeasure(Session::getSession()->getId()));
}
protected function onSuccess()
{
$errors = [];
foreach ($this->getObjects() as $object) {
$commands = $this->getCommand($object);
if ($commands === null) {
continue;
}
if ($commands instanceof IcingaCommand) {
$commands = [$commands];
}
foreach ($commands as $command) {
try {
$this->sendCommand($command);
} catch (Exception $e) {
Logger::error($e->getMessage());
$errors[] = $e->getMessage();
}
}
}
if (! empty($errors)) {
if (count($errors) > 1) {
Notification::warning(
t('Some commands were not transmitted. Please check the log. The first error follows.')
);
}
Notification::error($errors[0]);
}
}
/**
* Transmit the given command
*
* @param IcingaCommand $command
*
* @return void
*/
protected function sendCommand(IcingaCommand $command)
{
(new CommandTransport())->send($command);
}
}

View file

@ -0,0 +1,82 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms\Command\Instance;
use Icinga\Module\Icingadb\Command\Instance\ToggleInstanceFeatureCommand;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use ipl\Orm\Model;
use ipl\Web\FormDecorator\IcingaFormDecorator;
class ToggleInstanceFeaturesForm extends CommandForm
{
use Auth;
protected $features;
protected $featureStatus;
public function __construct($featureStatus)
{
$this->featureStatus = $featureStatus;
$this->features = [
ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS =>
t('Active Host Checks'),
ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS =>
t('Active Service Checks'),
ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS =>
t('Event Handlers'),
ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION =>
t('Flap Detection'),
ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS =>
t('Notifications'),
ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA =>
t('Performance Data')
];
$this->getAttributes()->add('class', 'instance-features');
}
protected function assembleElements()
{
$disabled = ! $this->getAuth()->hasPermission('monitoring/command/feature/instance');
$decorator = new IcingaFormDecorator();
foreach ($this->features as $feature => $label) {
$this->addElement(
'checkbox',
$feature,
[
'class' => 'autosubmit',
'label' => $label,
'disabled' => $disabled,
'value' => (bool) $this->featureStatus[$feature]
]
);
$decorator->decorate($this->getElement($feature));
}
}
protected function assembleSubmitButton()
{
}
protected function getCommand(Model $object)
{
foreach ($this->features as $feature => $spec) {
$featureState = $this->getElement($feature)->isChecked();
if ((int) $featureState === (int) $this->featureStatus[$feature]) {
continue;
}
$command = new ToggleInstanceFeatureCommand();
$command->setFeature($feature);
$command->setEnabled((int) $featureState);
yield $command;
}
}
}

View file

@ -0,0 +1,159 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms\Command\Object;
use DateInterval;
use DateTime;
use Icinga\Application\Config;
use Icinga\Module\Icingadb\Command\Object\AcknowledgeProblemCommand;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use ipl\Html\HtmlElement;
use ipl\Orm\Model;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use ipl\Web\Widget\Icon;
class AcknowledgeProblemForm extends CommandForm
{
use Auth;
protected function assembleElements()
{
$this->add(new HtmlElement('div', ['class' => 'form-description'], [
new Icon('info-circle', ['class' => 'form-description-icon']),
new HtmlElement('ul', null, [
new HtmlElement('li', null, t(
'This command is used to acknowledge host or service problems. When a problem is acknowledged,'
. ' future notifications about problems are temporarily disabled until the host or service'
. ' recovers.'
))
])
]));
$config = Config::module('icingadb');
$decorator = new IcingaFormDecorator();
$this->addElement(
'textarea',
'comment',
[
'required' => true,
'label' => t('Comment'),
'description' => t(
'If you work with other administrators, you may find it useful to share information about'
. ' the host or service that is having problems. Make sure you enter a brief description of'
. ' what you are doing.'
)
]
);
$decorator->decorate($this->getElement('comment'));
$this->addElement(
'checkbox',
'persistent',
[
'label' => t('Persistent Comment'),
'value' => (bool) $config->get('settings', 'acknowledge_persistent', false),
'description' => t(
'If you want the comment to remain even when the acknowledgement is removed, check this'
. ' option.'
)
]
);
$decorator->decorate($this->getElement('persistent'));
$this->addElement(
'checkbox',
'notify',
[
'label' => t('Send Notification'),
'value' => (bool) $config->get('settings', 'acknowledge_notify', true),
'description' => t(
'If you want an acknowledgement notification to be sent out to the appropriate contacts,'
. ' check this option.'
)
]
);
$decorator->decorate($this->getElement('notify'));
$this->addElement(
'checkbox',
'sticky',
[
'label' => t('Sticky Acknowledgement'),
'value' => (bool) $config->get('settings', 'acknowledge_sticky', false),
'description' => t(
'If you want the acknowledgement to remain until the host or service recovers even if the host'
. ' or service changes state, check this option.'
)
]
);
$decorator->decorate($this->getElement('sticky'));
$acknowledgeExpire = (bool) $config->get('settings', 'acknowledge_expire', false);
$this->addElement(
'checkbox',
'expire',
[
'ignore' => true,
'class' => 'autosubmit',
'value' => $acknowledgeExpire,
'label' => t('Use Expire Time'),
'description' => t('If the acknowledgement should expire, check this option.')
]
);
$decorator->decorate($this->getElement('expire'));
if ($acknowledgeExpire || $this->getPopulatedValue('expire') === 'y') {
$expireTime = new DateTime();
$expireTime->add(new DateInterval($config->get('settings', 'acknowledge_expire_time', 'PT1H')));
$this->addElement(
'localDateTime',
'expire_time',
[
'required' => true,
'value' => $expireTime,
'label' => t('Expire Time'),
'description' => t('Choose the date and time when Icinga should delete the acknowledgement.')
]
);
$decorator->decorate($this->getElement('expire_time'));
}
}
protected function assembleSubmitButton()
{
$this->addElement(
'submit',
'btn_submit',
[
'required' => true,
'label' => tp('Acknowledge problem', 'Acknowledge problems', count($this->getObjects()))
]
);
(new IcingaFormDecorator())->decorate($this->getElement('btn_submit'));
}
protected function getCommand(Model $object)
{
$command = new AcknowledgeProblemCommand();
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setAuthor($this->getAuth()->getUser()->getUsername());
$command->setNotify($this->getElement('notify')->isChecked());
$command->setSticky($this->getElement('sticky')->isChecked());
$command->setPersistent($this->getElement('persistent')->isChecked());
if (($expireTime = $this->getValue('expire_time')) !== null) {
/** @var DateTime $expireTime */
$command->setExpireTime($expireTime->getTimestamp());
}
return $command;
}
}

View file

@ -0,0 +1,110 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms\Command\Object;
use DateInterval;
use DateTime;
use Icinga\Application\Config;
use Icinga\Module\Icingadb\Command\Object\AddCommentCommand;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use ipl\Html\HtmlElement;
use ipl\Orm\Model;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use ipl\Web\Widget\Icon;
class AddCommentForm extends CommandForm
{
use Auth;
protected function assembleElements()
{
$this->add(new HtmlElement('div', ['class' => 'form-description'], [
new Icon('info-circle', ['class' => 'form-description-icon']),
new HtmlElement('ul', null, [
new HtmlElement('li', null, t('This command is used to add host or service comments.'))
])
]));
$decorator = new IcingaFormDecorator();
$this->addElement(
'textarea',
'comment',
[
'required' => true,
'label' => t('Comment'),
'description' => t(
'If you work with other administrators, you may find it useful to share information about'
. ' the host or service that is having problems. Make sure you enter a brief description of'
. ' what you are doing.'
)
]
);
$decorator->decorate($this->getElement('comment'));
$config = Config::module('icingadb');
$commentExpire = (bool) $config->get('settings', 'comment_expire', false);
$this->addElement(
'checkbox',
'expire',
[
'ignore' => true,
'class' => 'autosubmit',
'value' => $commentExpire,
'label' => t('Use Expire Time'),
'description' => t('If the comment should expire, check this option.')
]
);
$decorator->decorate($this->getElement('expire'));
if ($commentExpire || $this->getPopulatedValue('expire') === 'y') {
$expireTime = new DateTime();
$expireTime->add(new DateInterval($config->get('settings', 'comment_expire_time', 'PT1H')));
$this->addElement(
'localDateTime',
'expire_time',
[
'required' => true,
'value' => $expireTime,
'label' => t('Expire Time'),
'description' => t('Choose the date and time when Icinga should delete the comment.')
]
);
$decorator->decorate($this->getElement('expire_time'));
}
}
protected function assembleSubmitButton()
{
$this->addElement(
'submit',
'btn_submit',
[
'required' => true,
'label' => tp('Add comment', 'Add comments', count($this->getObjects()))
]
);
(new IcingaFormDecorator())->decorate($this->getElement('btn_submit'));
}
protected function getCommand(Model $object)
{
$command = new AddCommentCommand();
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setAuthor($this->getAuth()->getUser()->getUsername());
if (($expireTime = $this->getValue('expire_time'))) {
/** @var DateTime $expireTime */
$command->setExpireTime($expireTime->getTimestamp());
}
return $command;
}
}

View file

@ -0,0 +1,63 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms\Command\Object;
use Icinga\Module\Icingadb\Command\Object\ScheduleCheckCommand;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use Icinga\Web\Notification;
use ipl\Orm\Model;
use ipl\Web\Widget\Icon;
class CheckNowForm extends CommandForm
{
use Auth;
protected $defaultAttributes = ['class' => 'inline'];
public function __construct()
{
$this->on(self::ON_SUCCESS, function () {
Notification::success(tp('Scheduling check..', 'Scheduling checks..', count($this->getObjects())));
});
}
protected function assembleElements()
{
}
protected function assembleSubmitButton()
{
$this->addElement(
'submitButton',
'btn_submit',
[
'class' => ['link-button', 'spinner'],
'label' => [
new Icon('sync-alt'),
t('Check Now')
],
'title' => t('Schedule the next active check to run immediately')
]
);
}
protected function getCommand(Model $object)
{
if (
! $object->active_checks_enabled
&& ! $this->getAuth()->hasPermission('monitoring/command/schedule-check')
) {
return null;
}
$command = new ScheduleCheckCommand();
$command->setObject($object);
$command->setCheckTime(time());
$command->setForced();
return $command;
}
}

View file

@ -0,0 +1,47 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms\Command\Object;
use Icinga\Module\Icingadb\Command\Object\DeleteCommentCommand;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use ipl\Orm\Model;
use ipl\Web\Common\RedirectOption;
use ipl\Web\Widget\Icon;
class DeleteCommentForm extends CommandForm
{
use Auth;
use RedirectOption;
protected function assembleElements()
{
$this->addElement($this->createRedirectOption());
}
protected function assembleSubmitButton()
{
$this->addElement(
'submitButton',
'btn_submit',
[
'class' => ['cancel-button', 'spinner'],
'label' => [
new Icon('trash'),
tp('Remove Comment', 'Remove Comments', count($this->getObjects()))
]
]
);
}
protected function getCommand(Model $object)
{
$command = new DeleteCommentCommand();
$command->setCommentName($object->name);
$command->setAuthor($this->getAuth()->getUser()->getUsername());
return $command;
}
}

View file

@ -0,0 +1,49 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms\Command\Object;
use Icinga\Module\Icingadb\Command\Object\DeleteDowntimeCommand;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use ipl\Orm\Model;
use ipl\Web\Common\RedirectOption;
use ipl\Web\Widget\Icon;
class DeleteDowntimeForm extends CommandForm
{
use Auth;
use RedirectOption;
protected $defaultAttributes = ['class' => 'inline'];
protected function assembleElements()
{
$this->addElement($this->createRedirectOption());
}
protected function assembleSubmitButton()
{
$this->addElement(
'submitButton',
'btn_submit',
[
'class' => ['cancel-button', 'spinner'],
'label' => [
new Icon('trash'),
tp('Delete downtime', 'Delete donwtimes', count($this->getObjects()))
]
]
);
}
protected function getCommand(Model $object)
{
$command = new DeleteDowntimeCommand();
$command->setDowntimeName($object->name);
$command->setAuthor($this->getAuth()->getUser()->getUsername());
return $command;
}
}

View file

@ -0,0 +1,111 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms\Command\Object;
use Icinga\Module\Icingadb\Command\Object\ProcessCheckResultCommand;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use Icinga\Module\Icingadb\Model\Host;
use ipl\Html\HtmlElement;
use ipl\Orm\Model;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use ipl\Web\Widget\Icon;
class ProcessCheckResultForm extends CommandForm
{
protected function assembleElements()
{
$this->add(new HtmlElement('div', ['class' => 'form-description'], [
new Icon('info-circle', ['class' => 'form-description-icon']),
new HtmlElement('ul', null, [
new HtmlElement('li', null, t(
'This command is used to submit passive host or service check results.'
))
])
]));
$decorator = new IcingaFormDecorator();
foreach ($this->getObjects() as $object) {
/** @var Model $object */
// Nasty, but as getObjects() returns everything but an object with a real
// iterator interface this is the only way to fetch just the first element
break;
}
$this->addElement(
'select',
'status',
[
'required' => true,
'label' => t('Status'),
'description' => t('The state this check result should report'),
'multiOptions' => $object instanceof Host ? [
ProcessCheckResultCommand::HOST_UP => t('UP', 'icinga.state'),
ProcessCheckResultCommand::HOST_DOWN => t('DOWN', 'icinga.state')
] : [
ProcessCheckResultCommand::SERVICE_OK => t('OK', 'icinga.state'),
ProcessCheckResultCommand::SERVICE_WARNING => t('WARNING', 'icinga.state'),
ProcessCheckResultCommand::SERVICE_CRITICAL => t('CRITICAL', 'icinga.state'),
ProcessCheckResultCommand::SERVICE_UNKNOWN => t('UNKNOWN', 'icinga.state')
]
]
);
$decorator->decorate($this->getElement('status'));
$this->addElement(
'text',
'output',
[
'required' => true,
'label' => t('Output'),
'description' => t('The plugin output of this check result')
]
);
$decorator->decorate($this->getElement('output'));
$this->addElement(
'text',
'perfdata',
[
'allowEmpty' => true,
'label' => t('Performance Data'),
'description' => t(
'The performance data of this check result. Leave empty'
. ' if this check result has no performance data'
)
]
);
$decorator->decorate($this->getElement('perfdata'));
}
protected function assembleSubmitButton()
{
$this->addElement(
'submit',
'btn_submit',
[
'required' => true,
'label' => tp(
'Submit Passive Check Result',
'Submit Passive Check Results',
count($this->getObjects())
)
]
);
(new IcingaFormDecorator())->decorate($this->getElement('btn_submit'));
}
protected function getCommand(Model $object)
{
$command = new ProcessCheckResultCommand();
$command->setObject($object);
$command->setStatus($this->getValue('status'));
$command->setOutput($this->getValue('output'));
$command->setPerformanceData($this->getValue('perfdata'));
return $command;
}
}

View file

@ -0,0 +1,46 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms\Command\Object;
use Icinga\Module\Icingadb\Command\Object\RemoveAcknowledgementCommand;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use ipl\Orm\Model;
use ipl\Web\Widget\Icon;
class RemoveAcknowledgementForm extends CommandForm
{
use Auth;
protected $defaultAttributes = ['class' => 'inline'];
protected function assembleElements()
{
}
protected function assembleSubmitButton()
{
$this->addElement(
'submitButton',
'btn_submit',
[
'class' => ['link-button', 'spinner'],
'label' => [
new Icon('trash'),
tp('Remove acknowledgement', 'Remove acknowledgements', count($this->getObjects()))
]
]
);
}
protected function getCommand(Model $object)
{
$command = new RemoveAcknowledgementCommand();
$command->setObject($object);
$command->setAuthor($this->getAuth()->getUser()->getUsername());
return $command;
}
}

View file

@ -0,0 +1,81 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms\Command\Object;
use DateInterval;
use DateTime;
use Icinga\Module\Icingadb\Command\Object\ScheduleCheckCommand;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use ipl\Html\HtmlElement;
use ipl\Orm\Model;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use ipl\Web\Widget\Icon;
class ScheduleCheckForm extends CommandForm
{
protected function assembleElements()
{
$this->add(new HtmlElement('div', ['class' => 'form-description'], [
new Icon('info-circle', ['class' => 'form-description-icon']),
new HtmlElement('ul', null, [
new HtmlElement('li', null, t(
'This command is used to schedule the next check of hosts or services. Icinga'
. ' will re-queue the hosts or services to be checked at the time you specify.'
))
])
]));
$decorator = new IcingaFormDecorator();
$this->addElement(
'localDateTime',
'check_time',
[
'required' => true,
'label' => t('Check Time'),
'description' => t('Set the date and time when the check should be scheduled.'),
'value' => (new DateTime())->add(new DateInterval('PT1H'))
]
);
$decorator->decorate($this->getElement('check_time'));
$this->addElement(
'checkbox',
'force_check',
[
'label' => t('Force Check'),
'description' => t(
'If you select this option, Icinga will force a check regardless of both what time the'
. ' scheduled check occurs and whether or not checks are enabled.'
)
]
);
$decorator->decorate($this->getElement('force_check'));
}
protected function assembleSubmitButton()
{
$this->addElement(
'submit',
'btn_submit',
[
'required' => true,
'label' => tp('Schedule check', 'Schedule checks', count($this->getObjects()))
]
);
(new IcingaFormDecorator())->decorate($this->getElement('btn_submit'));
}
protected function getCommand(Model $object)
{
$command = new ScheduleCheckCommand();
$command->setObject($object);
$command->setForced($this->getElement('force_check')->isChecked());
$command->setCheckTime($this->getValue('check_time')->getTimestamp());
return $command;
}
}

View file

@ -0,0 +1,77 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms\Command\Object;
use Icinga\Application\Config;
use Icinga\Module\Icingadb\Command\Object\PropagateHostDowntimeCommand;
use Icinga\Module\Icingadb\Command\Object\ScheduleHostDowntimeCommand;
use ipl\Orm\Model;
use ipl\Web\FormDecorator\IcingaFormDecorator;
class ScheduleHostDowntimeForm extends ScheduleServiceDowntimeForm
{
protected function assembleElements()
{
parent::assembleElements();
$decorator = new IcingaFormDecorator();
$config = Config::module('icingadb');
$this->addElement(
'checkbox',
'all_services',
[
'label' => t('All Services'),
'description' => t(
'Sets downtime for all services for the matched host objects. If child options are set,'
. ' all child hosts and their services will schedule a downtime too.'
),
'value' => (bool) $config->get('settings', 'hostdowntime_all_services', false)
]
);
$decorator->decorate($this->getElement('all_services'));
$this->addElement(
'select',
'child_options',
array(
'description' => t('Schedule child downtimes.'),
'label' => t('Child Options'),
'multiOptions' => [
0 => t('Do nothing with child hosts'),
1 => t('Schedule triggered downtime for all child hosts'),
2 => t('Schedule non-triggered downtime for all child hosts')
]
)
);
$decorator->decorate($this->getElement('child_options'));
}
protected function getCommand(Model $object)
{
if (($childOptions = (int) $this->getValue('child_options'))) {
$command = new PropagateHostDowntimeCommand();
$command->setTriggered($childOptions === 1);
} else {
$command = new ScheduleHostDowntimeCommand();
}
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setAuthor($this->getAuth()->getUser()->getUsername());
$command->setStart($this->getValue('start')->getTimestamp());
$command->setEnd($this->getValue('end')->getTimestamp());
$command->setForAllServices($this->getElement('all_services')->isChecked());
if ($this->getElement('flexible')->isChecked()) {
$command->setFixed(false);
$command->setDuration(
$this->getValue('hours') * 3600 + $this->getValue('minutes') * 60
);
}
return $command;
}
}

View file

@ -0,0 +1,178 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms\Command\Object;
use DateInterval;
use DateTime;
use Icinga\Module\Icingadb\Command\Object\ScheduleServiceDowntimeCommand;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use ipl\Html\HtmlElement;
use ipl\Orm\Model;
use ipl\Validator\CallbackValidator;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use ipl\Web\Widget\Icon;
class ScheduleServiceDowntimeForm extends CommandForm
{
use Auth;
protected function assembleElements()
{
$this->add(new HtmlElement('div', ['class' => 'form-description'], [
new Icon('info-circle', ['class' => 'form-description-icon']),
new HtmlElement('ul', null, [
new HtmlElement('li', null, t(
'This command is used to schedule host and service downtimes. During the downtime specified'
. ' by the start and end time, Icinga will not send notifications out about the hosts and'
. ' services. When the scheduled downtime expires, Icinga will send out notifications for'
. ' the hosts and services as it normally would.'
))
])
]));
$decorator = new IcingaFormDecorator();
$this->addElement(
'textarea',
'comment',
[
'required' => true,
'label' => t('Comment'),
'description' => t(
'If you work with other administrators, you may find it useful to share information about'
. ' the host or service that is having problems. Make sure you enter a brief description of'
. ' what you are doing.'
)
]
);
$decorator->decorate($this->getElement('comment'));
$this->addElement(
'localDateTime',
'start',
[
'required' => true,
'value' => new DateTime(),
'label' => t('Start Time'),
'description' => t('Set the start date and time for the downtime.')
]
);
$decorator->decorate($this->getElement('start'));
$this->addElement(
'localDateTime',
'end',
[
'required' => true,
'label' => t('End Time'),
'description' => t('Set the end date and time for the downtime.'),
'value' => (new DateTime())->add(new DateInterval('PT1H')),
'validators' => ['Callback' => function ($value, $validator) {
/** @var CallbackValidator $validator */
if ($value <= $this->getValue('start')) {
$validator->addMessage(t('The end time must be greater than the start time'));
return false;
}
if ($value <= (new DateTime())) {
$validator->addMessage(t('A downtime must not be in the past'));
return false;
}
return true;
}]
]
);
$decorator->decorate($this->getElement('end'));
$this->addElement(
'checkbox',
'flexible',
[
'class' => 'autosubmit',
'label' => t('Flexible'),
'description' => t(
'To make this a flexible downtime, check this option. A flexible downtime starts when the host'
. ' or service enters a problem state sometime between the start and end times you specified.'
. ' It then lasts as long as the duration time you enter.'
)
]
);
$decorator->decorate($this->getElement('flexible'));
if ($this->getPopulatedValue('flexible') === 'y') {
$hoursInput = $this->createElement(
'number',
'hours',
[
'required' => true,
'label' => t('Duration'),
'value' => 2,
'min' => 0
]
);
$this->registerElement($hoursInput);
$decorator->decorate($hoursInput);
$minutesInput = $this->createElement(
'number',
'minutes',
[
'required' => true,
'value' => 0,
'min' => 0
]
);
$this->registerElement($minutesInput);
$minutesInput->addWrapper(
new HtmlElement('label', null, new HtmlElement('span', null, t('Minutes')))
);
$hoursInput->getWrapper()
->add($minutesInput)
->getAttributes()->add('class', 'downtime-duration');
$hoursInput->prependWrapper(
new HtmlElement('label', null, new HtmlElement('span', null, t('Hours')))
);
$this->add($hoursInput);
}
}
protected function assembleSubmitButton()
{
$this->addElement(
'submit',
'btn_submit',
[
'required' => true,
'label' => tp('Schedule downtime', 'Schedule downtimes', count($this->getObjects()))
]
);
(new IcingaFormDecorator())->decorate($this->getElement('btn_submit'));
}
protected function getCommand(Model $object)
{
$command = new ScheduleServiceDowntimeCommand();
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setAuthor($this->getAuth()->getUser()->getUsername());
$command->setStart($this->getValue('start')->getTimestamp());
$command->setEnd($this->getValue('end')->getTimestamp());
if ($this->getElement('flexible')->isChecked()) {
$command->setFixed(false);
$command->setDuration(
$this->getValue('hours') * 3600 + $this->getValue('minutes') * 60
);
}
return $command;
}
}

View file

@ -0,0 +1,86 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms\Command\Object;
use Icinga\Application\Config;
use Icinga\Module\Icingadb\Command\Object\SendCustomNotificationCommand;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use ipl\Html\HtmlElement;
use ipl\Orm\Model;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use ipl\Web\Widget\Icon;
class SendCustomNotificationForm extends CommandForm
{
use Auth;
protected function assembleElements()
{
$this->add(new HtmlElement('div', ['class' => 'form-description'], [
new Icon('info-circle', ['class' => 'form-description-icon']),
new HtmlElement('ul', null, [
new HtmlElement('li', null, t(
'This command is used to send custom notifications about hosts or services.'
))
])
]));
$config = Config::module('icingadb');
$decorator = new IcingaFormDecorator();
$this->addElement(
'textarea',
'comment',
[
'required' => true,
'label' => t('Comment'),
'description' => t(
'Enter a brief description on why you\'re sending this notification. It will be sent with it.'
)
]
);
$decorator->decorate($this->getElement('comment'));
$this->addElement(
'checkbox',
'forced',
[
'label' => t('Forced'),
'value' => (bool) $config->get('settings', 'custom_notification_forced', false),
'description' => t(
'If you check this option, the notification is sent regardless'
. ' of downtimes or whether notifications are enabled or not.'
)
]
);
$decorator->decorate($this->getElement('forced'));
}
protected function assembleSubmitButton()
{
$this->addElement(
'submit',
'btn_submit',
[
'required' => true,
'label' => tp('Send custom notification', 'Send custom notifications', count($this->getObjects()))
]
);
(new IcingaFormDecorator())->decorate($this->getElement('btn_submit'));
}
protected function getCommand(Model $object)
{
$command = new SendCustomNotificationCommand();
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setForced($this->getElement('forced')->isChecked());
$command->setAuthor($this->getAuth()->getUser()->getUsername());
return $command;
}
}

View file

@ -0,0 +1,113 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Forms\Command\Object;
use Icinga\Module\Icingadb\Command\Object\ToggleObjectFeatureCommand;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use ipl\Orm\Model;
use ipl\Web\FormDecorator\IcingaFormDecorator;
class ToggleObjectFeaturesForm extends CommandForm
{
use Auth;
const LEAVE_UNCHANGED = 'noop';
protected $features;
protected $featureStatus;
public function __construct($featureStatus)
{
$this->featureStatus = $featureStatus;
$this->features = [
ToggleObjectFeatureCommand::FEATURE_ACTIVE_CHECKS => [
'label' => t('Active Checks'),
'permission' => 'monitoring/command/feature/object/active-checks'
],
ToggleObjectFeatureCommand::FEATURE_PASSIVE_CHECKS => [
'label' => t('Passive Checks'),
'permission' => 'monitoring/command/feature/object/passive-checks'
],
ToggleObjectFeatureCommand::FEATURE_NOTIFICATIONS => [
'label' => t('Notifications'),
'permission' => 'monitoring/command/feature/object/notifications'
],
ToggleObjectFeatureCommand::FEATURE_EVENT_HANDLER => [
'label' => t('Event Handler'),
'permission' => 'monitoring/command/feature/object/event-handler'
],
ToggleObjectFeatureCommand::FEATURE_FLAP_DETECTION => [
'label' => t('Flap Detection'),
'permission' => 'monitoring/command/feature/object/flap-detection'
]
];
$this->getAttributes()->add('class', 'object-features');
}
protected function assembleElements()
{
$decorator = new IcingaFormDecorator();
foreach ($this->features as $feature => $spec) {
$options = [
'class' => 'autosubmit',
'disabled' => ! $this->getAuth()->hasPermission($spec['permission']),
'label' => $spec['label']
];
if ($this->featureStatus[$feature] === 2) {
$this->addElement(
'select',
$feature,
$options + [
'description' => t('Multiple Values'),
'options' => [
self::LEAVE_UNCHANGED => t('Leave Unchanged'),
t('Disable All'),
t('Enable All')
]
]
);
$decorator->decorate($this->getElement($feature));
$this->getElement($feature)
->getWrapper()
->getAttributes()
->add('class', 'indeterminate');
} else {
$options['value'] = (bool) $this->featureStatus[$feature];
$this->addElement('checkbox', $feature, $options);
$decorator->decorate($this->getElement($feature));
}
}
}
protected function assembleSubmitButton()
{
}
protected function getCommand(Model $object)
{
foreach ($this->features as $feature => $spec) {
$featureState = $this->getElement($feature)->isChecked();
if (
! $this->getAuth()->hasPermission($spec['permission'])
|| $featureState === self::LEAVE_UNCHANGED
|| (int) $featureState === (int) $this->featureStatus[$feature]
) {
continue;
}
$command = new ToggleObjectFeatureCommand();
$command->setObject($object);
$command->setFeature($feature);
$command->setEnabled((int) $featureState);
yield $command;
}
}
}

View file

@ -1,79 +0,0 @@
<?php
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
/** @var \Icinga\Web\View $this */
/** @var \Icinga\Module\Monitoring\Forms\Config\TransportReorderForm $form */
?>
<form id="<?= $form->getId() ?>" name="<?= $form->getName() ?>" enctype="<?= $form->getEncType() ?>"
method="<?= $form->getMethod() ?>" action="<?= $form->getAction() ?>">
<table class="table-row-selectable common-table" data-base-target="_next">
<thead>
<tr>
<th><?= $this->translate('Transport') ?></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<?php
$i = -1;
$transportConfig = $form->getConfig();
$total = $transportConfig->count();
foreach ($transportConfig as $transportName => $config):
++$i;
?>
<tr>
<td>
<?= $this->qlink(
$transportName,
'icingadb/config/update-command-transport',
['transport' => $transportName],
[
'icon' => 'edit',
'title' => sprintf($this->translate('Update command transport %s'), $transportName)
]
); ?>
<span class="config-label-meta">&#40;<?= sprintf(
$this->translate('Type: %s'),
ucfirst($config->get('transport', 'local'))
) ?>&#41;
</span>
</td>
<td class="text-right">
<?= $this->qlink(
'',
'icingadb/config/delete-command-transport',
['transport' => $transportName],
array(
'class' => 'action-link',
'icon' => 'cancel',
'title' => sprintf($this->translate('Delete command transport %s'), $transportName)
)
); ?>
</td>
<td class="icon-col text-right" data-base-target="_self">
<?php if ($i > 0): ?>
<button type="submit" name="transport_newpos" class="link-button icon-only animated move-up"
value="<?= $this->escape(($i - 1) . '|' . $transportName) ?>"
title="<?= $this->translate('Move up in order') ?>"
aria-label="<?= $this->escape(sprintf($this->translate('Move command transport %s upwards'), $transportName)) ?>">
<?= $this->icon('up-small') ?>
</button>
<?php endif ?>
<?php if ($i + 1 < $total): ?>
<button type="submit" name="transport_newpos" class="link-button icon-only animated move-down"
value="<?= $this->escape(($i + 1) . '|' . $transportName) ?>"
title="<?= $this->translate('Move down in order') ?>"
aria-label="<?= $this->escape(sprintf($this->translate('Move command transport %s downwards'), $transportName)) ?>">
<?= $this->icon('down-small') ?>
</button>
<?php endif ?>
</td>
</tr>
<?php endforeach ?>
</tbody>
</table>
<?= $form->getElement($form->getTokenElementName()) ?>
<?= $form->getElement($form->getUidElementName()) ?>
</form>

View file

@ -212,7 +212,7 @@ namespace Icinga\Module\Icingadb
$this->provideConfigTab('command-transports', [
'label' => t('Command Transports'),
'title' => t('Configure command transports'),
'url' => 'config/command-transports'
'url' => 'command-transport'
]);
$this->provideConfigTab('security', [
'label' => t('Security'),

View file

@ -0,0 +1,85 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command;
class IcingaApiCommand
{
/**
* Command data
*
* @var array
*/
protected $data;
/**
* Name of the endpoint
*
* @var string
*/
protected $endpoint;
/**
* Create a new Icinga 2 API command
*
* @param string $endpoint
* @param array $data
*
* @return static
*/
public static function create($endpoint, array $data)
{
return (new static())
->setEndpoint($endpoint)
->setData($data);
}
/**
* Get the command data
*
* @return array
*/
public function getData()
{
return $this->data;
}
/**
* Set the command data
*
* @param array $data
*
* @return $this
*/
public function setData($data)
{
$this->data = $data;
return $this;
}
/**
* Get the name of the endpoint
*
* @return string
*/
public function getEndpoint()
{
return $this->endpoint;
}
/**
* Set the name of the endpoint
*
* @param string $endpoint
*
* @return $this
*/
public function setEndpoint($endpoint)
{
$this->endpoint = $endpoint;
return $this;
}
}

View file

@ -0,0 +1,22 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command;
/**
* Base class for commands sent to an Icinga instance
*/
abstract class IcingaCommand
{
/**
* Get the name of the command
*
* @return string
*/
public function getName()
{
$nsParts = explode('\\', get_called_class());
return substr_replace(end($nsParts), '', -7); // Remove 'Command' Suffix
}
}

View file

@ -0,0 +1,105 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Instance;
use Icinga\Module\Icingadb\Command\IcingaCommand;
/**
* Enable or disable a feature of an Icinga instance
*/
class ToggleInstanceFeatureCommand extends IcingaCommand
{
/**
* Feature for enabling or disabling active host checks on an Icinga instance
*/
const FEATURE_ACTIVE_HOST_CHECKS = 'active_host_checks_enabled';
/**
* Feature for enabling or disabling active service checks on an Icinga instance
*/
const FEATURE_ACTIVE_SERVICE_CHECKS = 'active_service_checks_enabled';
/**
* Feature for enabling or disabling host and service event handlers on an Icinga instance
*/
const FEATURE_EVENT_HANDLERS = 'event_handlers_enabled';
/**
* Feature for enabling or disabling host and service flap detection on an Icinga instance
*/
const FEATURE_FLAP_DETECTION = 'flap_detection_enabled';
/**
* Feature for enabling or disabling host and service notifications on an Icinga instance
*/
const FEATURE_NOTIFICATIONS = 'notifications_enabled';
/**
* Feature for enabling or disabling the processing of host and service performance data on an Icinga instance
*/
const FEATURE_PERFORMANCE_DATA = 'process_performance_data';
/**
* Feature that is to be enabled or disabled
*
* @var string
*/
protected $feature;
/**
* Whether the feature should be enabled or disabled
*
* @var bool
*/
protected $enabled;
/**
* Set the feature that is to be enabled or disabled
*
* @param string $feature
*
* @return $this
*/
public function setFeature($feature)
{
$this->feature = (string) $feature;
return $this;
}
/**
* Get the feature that is to be enabled or disabled
*
* @return string
*/
public function getFeature()
{
return $this->feature;
}
/**
* Set whether the feature should be enabled or disabled
*
* @param bool $enabled
*
* @return $this
*/
public function setEnabled($enabled = true)
{
$this->enabled = (bool) $enabled;
return $this;
}
/**
* Get whether the feature should be enabled or disabled
*
* @return bool
*/
public function getEnabled()
{
return $this->enabled;
}
}

View file

@ -0,0 +1,140 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Object;
/**
* Acknowledge a host or service problem
*/
class AcknowledgeProblemCommand extends WithCommentCommand
{
/**
* Whether the acknowledgement is sticky
*
* Sticky acknowledgements remain until the host or service recovers. Non-sticky acknowledgements will be
* automatically removed when the host or service state changes.
*
* @var bool
*/
protected $sticky = false;
/**
* Whether to send a notification about the acknowledgement
* @var bool
*/
protected $notify = false;
/**
* Whether the comment associated with the acknowledgement is persistent
*
* Persistent comments are not lost the next time the monitoring host restarts.
*
* @var bool
*/
protected $persistent = false;
/**
* Optional time when the acknowledgement should expire
*
* @var int
*/
protected $expireTime;
/**
* Set whether the acknowledgement is sticky
*
* @param bool $sticky
*
* @return $this
*/
public function setSticky($sticky = true)
{
$this->sticky = (bool) $sticky;
return $this;
}
/**
* Is the acknowledgement sticky?
*
* @return bool
*/
public function getSticky()
{
return $this->sticky;
}
/**
* Set whether to send a notification about the acknowledgement
*
* @param bool $notify
*
* @return $this
*/
public function setNotify($notify = true)
{
$this->notify = (bool) $notify;
return $this;
}
/**
* Get whether to send a notification about the acknowledgement
*
* @return bool
*/
public function getNotify()
{
return $this->notify;
}
/**
* Set whether the comment associated with the acknowledgement is persistent
*
* @param bool $persistent
*
* @return $this
*/
public function setPersistent($persistent = true)
{
$this->persistent = (bool) $persistent;
return $this;
}
/**
* Is the comment associated with the acknowledgement is persistent?
*
* @return bool
*/
public function getPersistent()
{
return $this->persistent;
}
/**
* Set the time when the acknowledgement should expire
*
* @param int $expireTime
*
* @return $this
*/
public function setExpireTime($expireTime)
{
$this->expireTime = (int) $expireTime;
return $this;
}
/**
* Get the time when the acknowledgement should expire
*
* @return int
*/
public function getExpireTime()
{
return $this->expireTime;
}
}

View file

@ -0,0 +1,42 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Object;
/**
* Add a comment to a host or service
*/
class AddCommentCommand extends WithCommentCommand
{
/**
* Optional time when the acknowledgement should expire
*
* @var int
*/
protected $expireTime;
/**
* Set the time when the acknowledgement should expire
*
* @param int $expireTime
*
* @return $this
*/
public function setExpireTime($expireTime)
{
$this->expireTime = (int) $expireTime;
return $this;
}
/**
* Get the time when the acknowledgement should expire
*
* @return int
*/
public function getExpireTime()
{
return $this->expireTime;
}
}

View file

@ -0,0 +1,39 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Object;
trait CommandAuthor
{
/**
* Author of the command
*
* @var string
*/
protected $author;
/**
* Set the author
*
* @param string $author
*
* @return $this
*/
public function setAuthor($author)
{
$this->author = (string) $author;
return $this;
}
/**
* Get the author
*
* @return string
*/
public function getAuthor()
{
return $this->author;
}
}

View file

@ -0,0 +1,46 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Object;
use Icinga\Module\Icingadb\Command\IcingaCommand;
/**
* Delete a host or service comment
*/
class DeleteCommentCommand extends IcingaCommand
{
use CommandAuthor;
/**
* Name of the comment
*
* @var string
*/
protected $commentName;
/**
* Get the name of the comment
*
* @return string
*/
public function getCommentName()
{
return $this->commentName;
}
/**
* Set the name of the comment
*
* @param string $commentName
*
* @return $this
*/
public function setCommentName($commentName)
{
$this->commentName = $commentName;
return $this;
}
}

View file

@ -0,0 +1,52 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Object;
use Icinga\Module\Icingadb\Command\IcingaCommand;
/**
* Delete a host or service downtime
*/
class DeleteDowntimeCommand extends IcingaCommand
{
use CommandAuthor;
/**
* Name of the downtime (Icinga 2.4+)
*
* Required for removing the downtime via Icinga 2's API.
*
* @var string
*/
protected $downtimeName;
/**
* Get the name of the downtime (Icinga 2.4+)
*
* Required for removing the downtime via Icinga 2's API.
*
* @return string
*/
public function getDowntimeName()
{
return $this->downtimeName;
}
/**
* Set the name of the downtime (Icinga 2.4+)
*
* Required for removing the downtime via Icinga 2's API.
*
* @param string $downtimeName
*
* @return $this
*/
public function setDowntimeName($downtimeName)
{
$this->downtimeName = $downtimeName;
return $this;
}
}

View file

@ -0,0 +1,45 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Object;
use Icinga\Module\Icingadb\Command\IcingaCommand;
use ipl\Orm\Model;
/**
* Base class for commands that involve a monitored object, i.e. a host or service
*/
abstract class ObjectCommand extends IcingaCommand
{
/**
* Involved object
*
* @var Model
*/
protected $object;
/**
* Set the involved object
*
* @param Model $object
*
* @return $this
*/
public function setObject(Model $object)
{
$this->object = $object;
return $this;
}
/**
* Get the involved object
*
* @return Model
*/
public function getObject()
{
return $this->object;
}
}

View file

@ -0,0 +1,134 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Object;
/**
* Submit a passive check result for a host or service
*/
class ProcessCheckResultCommand extends ObjectCommand
{
/**
* Host up
*/
const HOST_UP = 0;
/**
* Host down
*/
const HOST_DOWN = 1;
/**
* Service ok
*/
const SERVICE_OK = 0;
/**
* Service warning
*/
const SERVICE_WARNING = 1;
/**
* Service critical
*/
const SERVICE_CRITICAL = 2;
/**
* Service unknown
*/
const SERVICE_UNKNOWN = 3;
/**
* Status code of the host or service check result
*
* @var int
*/
protected $status;
/**
* Text output of the host or service check result
*
* @var string
*/
protected $output;
/**
* Optional performance data of the host or service check result
*
* @var string
*/
protected $performanceData;
/**
* Set the status code of the host or service check result
*
* @param int $status
*
* @return $this
*/
public function setStatus($status)
{
$this->status = (int) $status;
return $this;
}
/**
* Get the status code of the host or service check result
*
* @return int
*/
public function getStatus()
{
return $this->status;
}
/**
* Set the text output of the host or service check result
*
* @param string $output
*
* @return $this
*/
public function setOutput($output)
{
$this->output = (string) $output;
return $this;
}
/**
* Get the text output of the host or service check result
*
* @return string
*/
public function getOutput()
{
return $this->output;
}
/**
* Set the performance data of the host or service check result
*
* @param string $performanceData
*
* @return $this
*/
public function setPerformanceData($performanceData)
{
$this->performanceData = (string) $performanceData;
return $this;
}
/**
* Get the performance data of the host or service check result
*
* @return string
*/
public function getPerformanceData()
{
return $this->performanceData;
}
}

View file

@ -0,0 +1,42 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Object;
/**
* Schedule and propagate host downtime
*/
class PropagateHostDowntimeCommand extends ScheduleHostDowntimeCommand
{
/**
* Whether the downtime for child hosts are all set to be triggered by this' host downtime
*
* @var bool
*/
protected $triggered = false;
/**
* Set whether the downtime for child hosts are all set to be triggered by this' host downtime
*
* @param bool $triggered
*
* @return $this
*/
public function setTriggered($triggered = true)
{
$this->triggered = (bool) $triggered;
return $this;
}
/**
* Get whether the downtime for child hosts are all set to be triggered by this' host downtime
*
* @return bool
*/
public function getTriggered()
{
return $this->triggered;
}
}

View file

@ -0,0 +1,13 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Object;
/**
* Remove a problem acknowledgement from a host or service
*/
class RemoveAcknowledgementCommand extends ObjectCommand
{
use CommandAuthor;
}

View file

@ -0,0 +1,80 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Object;
/**
* Schedule a check
*/
class ScheduleCheckCommand extends ObjectCommand
{
/**
* Time when the next check of a host or service is to be scheduled
*
* If active checks are disabled on a host- or service-specific or program-wide basis or the host or service is
* already scheduled to be checked at an earlier time, etc. The check may not actually be scheduled at the time
* specified. This behaviour can be overridden by setting `ScheduledCheck::$forced' to true.
*
* @var int Unix timestamp
*/
protected $checkTime;
/**
* Whether the check is forced
*
* Forced checks are performed regardless of what time it is (e.g. time period restrictions are ignored) and whether
* or not active checks are enabled on a host- or service-specific or program-wide basis.
*
* @var bool
*/
protected $forced = false;
/**
* Set the time when the next check of a host or service is to be scheduled
*
* @param int $checkTime Unix timestamp
*
* @return $this
*/
public function setCheckTime($checkTime)
{
$this->checkTime = (int) $checkTime;
return $this;
}
/**
* Get the time when the next check of a host or service is to be scheduled
*
* @return int Unix timestamp
*/
public function getCheckTime()
{
return $this->checkTime;
}
/**
* Set whether the check is forced
*
* @param bool $forced
*
* @return $this
*/
public function setForced($forced = true)
{
$this->forced = (bool) $forced;
return $this;
}
/**
* Get whether the check is forced
*
* @return bool
*/
public function getForced()
{
return $this->forced;
}
}

View file

@ -0,0 +1,42 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Object;
/**
* Schedule a host downtime
*/
class ScheduleHostDowntimeCommand extends ScheduleServiceDowntimeCommand
{
/**
* Whether to schedule a downtime for all services associated with a particular host
*
* @var bool
*/
protected $forAllServices = false;
/**
* Set whether to schedule a downtime for all services associated with a particular host
*
* @param bool $forAllServices
*
* @return $this
*/
public function setForAllServices($forAllServices = true)
{
$this->forAllServices = (bool) $forAllServices;
return $this;
}
/**
* Get whether to schedule a downtime for all services associated with a particular host
*
* @return bool
*/
public function getForAllServices()
{
return $this->forAllServices;
}
}

View file

@ -0,0 +1,184 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Object;
/**
* Schedule a service downtime
*/
class ScheduleServiceDowntimeCommand extends AddCommentCommand
{
/**
* Downtime starts at the exact time specified
*
* If `Downtime::$fixed' is set to false, the time between `Downtime::$start' and `Downtime::$end' at which a
* host or service transitions to a problem state determines the time at which the downtime actually starts.
* The downtime will then last for `Downtime::$duration' seconds.
*
* @var int Unix timestamp
*/
protected $start;
/**
* Downtime ends at the exact time specified
*
* If `Downtime::$fixed' is set to false, the time between `Downtime::$start' and `Downtime::$end' at which a
* host or service transitions to a problem state determines the time at which the downtime actually starts.
* The downtime will then last for `Downtime::$duration' seconds.
*
* @var int Unix timestamp
*/
protected $end;
/**
* Whether it's a fixed or flexible downtime
*
* @var bool
*/
protected $fixed = true;
/**
* ID of the downtime which triggers this downtime
*
* The start of this downtime is triggered by the start of the other scheduled host or service downtime.
*
* @var int|null
*/
protected $triggerId;
/**
* The duration in seconds the downtime must last if it's a flexible downtime
*
* If `Downtime::$fixed' is set to false, the downtime will last for the duration in seconds specified, even
* if the host or service recovers before the downtime expires.
*
* @var int|null
*/
protected $duration;
/**
* Set the time when the downtime should start
*
* @param int $start Unix timestamp
*
* @return $this
*/
public function setStart($start)
{
$this->start = (int) $start;
return $this;
}
/**
* Get the time when the downtime should start
*
* @return int Unix timestamp
*/
public function getStart()
{
return $this->start;
}
/**
* Set the time when the downtime should end
*
* @param int $end Unix timestamp
*
* @return $this
*/
public function setEnd($end)
{
$this->end = (int) $end;
return $this;
}
/**
* Get the time when the downtime should end
*
* @return int Unix timestamp
*/
public function getEnd()
{
return $this->end;
}
/**
* Set whether it's a fixed or flexible downtime
*
* @param boolean $fixed
*
* @return $this
*/
public function setFixed($fixed = true)
{
$this->fixed = (bool) $fixed;
return $this;
}
/**
* Is the downtime fixed?
*
* @return boolean
*/
public function getFixed()
{
return $this->fixed;
}
/**
* Set the ID of the downtime which triggers this downtime
*
* @param int $triggerId
*
* @return $this
*/
public function setTriggerId($triggerId)
{
$this->triggerId = (int) $triggerId;
return $this;
}
/**
* Get the ID of the downtime which triggers this downtime
*
* @return int|null
*/
public function getTriggerId()
{
return $this->triggerId;
}
/**
* Set the duration in seconds the downtime must last if it's a flexible downtime
*
* @param int $duration
*
* @return $this
*/
public function setDuration($duration)
{
$this->duration = (int) $duration;
return $this;
}
/**
* Get the duration in seconds the downtime must last if it's a flexible downtime
*
* @return int|null
*/
public function getDuration()
{
return $this->duration;
}
public function getName()
{
return 'ScheduleDowntime';
}
}

View file

@ -0,0 +1,44 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Object;
/**
* Send custom notifications for a host or service
*/
class SendCustomNotificationCommand extends WithCommentCommand
{
/**
* Whether the notification is forced
*
* Forced notifications are sent out regardless of time restrictions and whether or not notifications are enabled.
*
* @var bool
*/
protected $forced;
/**
* Get whether to force the notification
*
* @return bool
*/
public function getForced()
{
return $this->forced;
}
/**
* Set whether to force the notification
*
* @param bool $forced
*
* @return $this
*/
public function setForced($forced = true)
{
$this->forced = $forced;
return $this;
}
}

View file

@ -0,0 +1,102 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Object;
/**
* Enable or disable a feature of an Icinga object, i.e. host or service
*/
class ToggleObjectFeatureCommand extends ObjectCommand
{
/**
* Feature for enabling or disabling active checks of a host or service
*/
const FEATURE_ACTIVE_CHECKS = 'active_checks_enabled';
/**
* Feature for enabling or disabling passive checks of a host or service
*/
const FEATURE_PASSIVE_CHECKS = 'passive_checks_enabled';
/**
* Feature for enabling or disabling notifications for a host or service
*
* Notifications will be sent out only if notifications are enabled on a program-wide basis as well.
*/
const FEATURE_NOTIFICATIONS = 'notifications_enabled';
/**
* Feature for enabling or disabling event handler for a host or service
*/
const FEATURE_EVENT_HANDLER = 'event_handler_enabled';
/**
* Feature for enabling or disabling flap detection for a host or service.
*
* In order to enable flap detection flap detection must be enabled on a program-wide basis as well.
*/
const FEATURE_FLAP_DETECTION = 'flapping_enabled';
/**
* Feature that is to be enabled or disabled
*
* @var string
*/
protected $feature;
/**
* Whether the feature should be enabled or disabled
*
* @var bool
*/
protected $enabled;
/**
* Set the feature that is to be enabled or disabled
*
* @param string $feature
*
* @return $this
*/
public function setFeature($feature)
{
$this->feature = (string) $feature;
return $this;
}
/**
* Get the feature that is to be enabled or disabled
*
* @return string
*/
public function getFeature()
{
return $this->feature;
}
/**
* Set whether the feature should be enabled or disabled
*
* @param bool $enabled
*
* @return $this
*/
public function setEnabled($enabled = true)
{
$this->enabled = (bool) $enabled;
return $this;
}
/**
* Get whether the feature should be enabled or disabled
*
* @return bool
*/
public function getEnabled()
{
return $this->enabled;
}
}

View file

@ -0,0 +1,44 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Object;
/**
* Base class for commands adding comments
*/
abstract class WithCommentCommand extends ObjectCommand
{
use CommandAuthor;
/**
* Comment
*
* @var string
*/
protected $comment;
/**
* Set the comment
*
* @param string $comment
*
* @return $this
*/
public function setComment($comment)
{
$this->comment = (string) $comment;
return $this;
}
/**
* Get the comment
*
* @return string
*/
public function getComment()
{
return $this->comment;
}
}

View file

@ -0,0 +1,305 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Renderer;
use Icinga\Module\Icingadb\Command\IcingaApiCommand;
use Icinga\Module\Icingadb\Model\Host;
use Icinga\Module\Icingadb\Model\Service;
use Icinga\Module\Icingadb\Command\Instance\ToggleInstanceFeatureCommand;
use Icinga\Module\Icingadb\Command\Object\AcknowledgeProblemCommand;
use Icinga\Module\Icingadb\Command\Object\AddCommentCommand;
use Icinga\Module\Icingadb\Command\Object\DeleteCommentCommand;
use Icinga\Module\Icingadb\Command\Object\DeleteDowntimeCommand;
use Icinga\Module\Icingadb\Command\Object\ProcessCheckResultCommand;
use Icinga\Module\Icingadb\Command\Object\PropagateHostDowntimeCommand;
use Icinga\Module\Icingadb\Command\Object\RemoveAcknowledgementCommand;
use Icinga\Module\Icingadb\Command\Object\ScheduleHostDowntimeCommand;
use Icinga\Module\Icingadb\Command\Object\ScheduleCheckCommand;
use Icinga\Module\Icingadb\Command\Object\ScheduleServiceDowntimeCommand;
use Icinga\Module\Icingadb\Command\Object\SendCustomNotificationCommand;
use Icinga\Module\Icingadb\Command\Object\ToggleObjectFeatureCommand;
use Icinga\Module\Icingadb\Command\IcingaCommand;
use InvalidArgumentException;
use ipl\Orm\Model;
/**
* Icinga command renderer for the Icinga command file
*/
class IcingaApiCommandRenderer implements IcingaCommandRendererInterface
{
/**
* Name of the Icinga application object
*
* @var string
*/
protected $app = 'app';
/**
* Get the name of the Icinga application object
*
* @return string
*/
public function getApp()
{
return $this->app;
}
/**
* Set the name of the Icinga application object
*
* @param string $app
*
* @return $this
*/
public function setApp($app)
{
$this->app = $app;
return $this;
}
/**
* Apply filter to query data
*
* @param array $data
* @param Model $object
*
* @return array
*/
protected function applyFilter(array &$data, Model $object)
{
if ($object instanceof Host) {
$data['host'] = $object->name;
} else {
/** @var Service $object */
$data['service'] = sprintf('%s!%s', $object->host->name, $object->name);
}
}
/**
* Render a command
*
* @param IcingaCommand $command
*
* @return IcingaApiCommand
*/
public function render(IcingaCommand $command)
{
$renderMethod = 'render' . $command->getName();
if (! method_exists($this, $renderMethod)) {
throw new InvalidArgumentException(
sprintf('Can\'t render command. Method %s not found', $renderMethod)
);
}
return $this->$renderMethod($command);
}
public function renderAddComment(AddCommentCommand $command)
{
$endpoint = 'actions/add-comment';
$data = [
'author' => $command->getAuthor(),
'comment' => $command->getComment()
];
if ($command->getExpireTime() !== null) {
$data['expiry'] = $command->getExpireTime();
}
$this->applyFilter($data, $command->getObject());
return IcingaApiCommand::create($endpoint, $data);
}
public function renderSendCustomNotification(SendCustomNotificationCommand $command)
{
$endpoint = 'actions/send-custom-notification';
$data = [
'author' => $command->getAuthor(),
'comment' => $command->getComment(),
'force' => $command->getForced()
];
$this->applyFilter($data, $command->getObject());
return IcingaApiCommand::create($endpoint, $data);
}
public function renderProcessCheckResult(ProcessCheckResultCommand $command)
{
$endpoint = 'actions/process-check-result';
$data = [
'exit_status' => $command->getStatus(),
'plugin_output' => $command->getOutput(),
'performance_data' => $command->getPerformanceData()
];
$this->applyFilter($data, $command->getObject());
return IcingaApiCommand::create($endpoint, $data);
}
public function renderScheduleCheck(ScheduleCheckCommand $command)
{
$endpoint = 'actions/reschedule-check';
$data = [
'next_check' => $command->getCheckTime(),
'force' => $command->getForced()
];
$this->applyFilter($data, $command->getObject());
return IcingaApiCommand::create($endpoint, $data);
}
public function renderScheduleDowntime(ScheduleServiceDowntimeCommand $command)
{
$endpoint = 'actions/schedule-downtime';
$data = [
'author' => $command->getAuthor(),
'comment' => $command->getComment(),
'start_time' => $command->getStart(),
'end_time' => $command->getEnd(),
'duration' => $command->getDuration(),
'fixed' => $command->getFixed(),
'trigger_name' => $command->getTriggerId()
];
if ($command instanceof PropagateHostDowntimeCommand) {
$data['child_options'] = $command->getTriggered() ? 1 : 2;
}
if ($command instanceof ScheduleHostDowntimeCommand && $command->getForAllServices()) {
$data['all_services'] = true;
}
$this->applyFilter($data, $command->getObject());
return IcingaApiCommand::create($endpoint, $data);
}
public function renderAcknowledgeProblem(AcknowledgeProblemCommand $command)
{
$endpoint = 'actions/acknowledge-problem';
$data = [
'author' => $command->getAuthor(),
'comment' => $command->getComment(),
'sticky' => $command->getSticky(),
'notify' => $command->getNotify(),
'persistent' => $command->getPersistent()
];
if ($command->getExpireTime() !== null) {
$data['expiry'] = $command->getExpireTime();
}
$this->applyFilter($data, $command->getObject());
return IcingaApiCommand::create($endpoint, $data);
}
public function renderToggleObjectFeature(ToggleObjectFeatureCommand $command)
{
switch ($command->getFeature()) {
case ToggleObjectFeatureCommand::FEATURE_ACTIVE_CHECKS:
$attr = 'enable_active_checks';
break;
case ToggleObjectFeatureCommand::FEATURE_PASSIVE_CHECKS:
$attr = 'enable_passive_checks';
break;
case ToggleObjectFeatureCommand::FEATURE_NOTIFICATIONS:
$attr = 'enable_notifications';
break;
case ToggleObjectFeatureCommand::FEATURE_EVENT_HANDLER:
$attr = 'enable_event_handler';
break;
case ToggleObjectFeatureCommand::FEATURE_FLAP_DETECTION:
$attr = 'enable_flapping';
break;
default:
throw new InvalidArgumentException($command->getFeature());
}
$endpoint = 'objects/';
$object = $command->getObject();
if ($object instanceof Host) {
$endpoint .= 'hosts';
} else {
/** @var Service $object */
$endpoint .= 'services';
}
$data = [
'attrs' => [
$attr => $command->getEnabled()
]
];
$this->applyFilter($data, $object);
return IcingaApiCommand::create($endpoint, $data);
}
public function renderDeleteComment(DeleteCommentCommand $command)
{
$endpoint = 'actions/remove-comment';
$data = [
'author' => $command->getAuthor(),
'comment' => $command->getCommentName()
];
return IcingaApiCommand::create($endpoint, $data);
}
public function renderDeleteDowntime(DeleteDowntimeCommand $command)
{
$endpoint = 'actions/remove-downtime';
$data = [
'author' => $command->getAuthor(),
'downtime' => $command->getDowntimeName()
];
return IcingaApiCommand::create($endpoint, $data);
}
public function renderRemoveAcknowledgement(RemoveAcknowledgementCommand $command)
{
$endpoint = 'actions/remove-acknowledgement';
$data = ['author' => $command->getAuthor()];
$this->applyFilter($data, $command->getObject());
return IcingaApiCommand::create($endpoint, $data);
}
public function renderToggleInstanceFeature(ToggleInstanceFeatureCommand $command)
{
$endpoint = 'objects/icingaapplications/' . $this->getApp();
switch ($command->getFeature()) {
case ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS:
$attr = 'enable_host_checks';
break;
case ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS:
$attr = 'enable_service_checks';
break;
case ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS:
$attr = 'enable_event_handlers';
break;
case ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION:
$attr = 'enable_flapping';
break;
case ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS:
$attr = 'enable_notifications';
break;
case ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA:
$attr = 'enable_perfdata';
break;
default:
throw new InvalidArgumentException($command->getFeature());
}
$data = [
'attrs' => [
$attr => $command->getEnabled()
]
];
return IcingaApiCommand::create($endpoint, $data);
}
}

View file

@ -0,0 +1,12 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Renderer;
/**
* Interface for Icinga command renderer
*/
interface IcingaCommandRendererInterface
{
}

View file

@ -0,0 +1,14 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Transport;
use Icinga\Exception\IcingaException;
/**
* Exception thrown if a command was not successful
*/
class ApiCommandException extends IcingaException
{
}

View file

@ -0,0 +1,309 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Transport;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Icinga\Application\Hook\AuditHook;
use Icinga\Application\Logger;
use Icinga\Exception\Json\JsonDecodeException;
use Icinga\Module\Icingadb\Command\IcingaApiCommand;
use Icinga\Module\Icingadb\Command\IcingaCommand;
use Icinga\Module\Icingadb\Command\Renderer\IcingaApiCommandRenderer;
use Icinga\Util\Json;
/**
* Command transport over Icinga 2's REST API
*/
class ApiCommandTransport implements CommandTransportInterface
{
/**
* Transport identifier
*/
const TRANSPORT = 'api';
/**
* API host
*
* @var string
*/
protected $host;
/**
* API password
*
* @var string
*/
protected $password;
/**
* API port
*
* @var int
*/
protected $port = 5665;
/**
* Command renderer
*
* @var IcingaApiCommandRenderer
*/
protected $renderer;
/**
* API username
*
* @var string
*/
protected $username;
/**
* Create a new API command transport
*/
public function __construct()
{
$this->renderer = new IcingaApiCommandRenderer();
}
/**
* Set the name of the Icinga application object
*
* @param string $app
*
* @return $this
*/
public function setApp($app)
{
$this->renderer->setApp($app);
return $this;
}
/**
* Get the API host
*
* @return string
*/
public function getHost()
{
return $this->host;
}
/**
* Set the API host
*
* @param string $host
*
* @return $this
*/
public function setHost($host)
{
$this->host = $host;
return $this;
}
/**
* Get the API password
*
* @return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set the API password
*
* @param string $password
*
* @return $this
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get the API port
*
* @return int
*/
public function getPort()
{
return $this->port;
}
/**
* Set the API port
*
* @param int $port
*
* @return $this
*/
public function setPort($port)
{
$this->port = (int) $port;
return $this;
}
/**
* Get the API username
*
* @return string
*/
public function getUsername()
{
return $this->username;
}
/**
* Set the API username
*
* @param string $username
*
* @return $this
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Get URI for endpoint
*
* @param string $endpoint
*
* @return string
*/
protected function getUriFor($endpoint)
{
return sprintf('https://%s:%u/v1/%s', $this->getHost(), $this->getPort(), $endpoint);
}
protected function sendCommand(IcingaApiCommand $command)
{
Logger::debug(
'Sending Icinga command "%s" to the API "%s:%u"',
$command->getEndpoint(),
$this->getHost(),
$this->getPort()
);
$data = $command->getData();
$payload = Json::encode($data);
AuditHook::logActivity(
'monitoring/command',
"Issued command {$command->getEndpoint()} with the following payload: $payload",
$data
);
try {
$response = (new Client())
->post($this->getUriFor($command->getEndpoint()), [
'auth' => [$this->getUsername(), $this->getPassword()],
'headers' => ['Accept' => 'application/json'],
'json' => $command->getData(),
'http_errors' => false,
'verify' => false
]);
} catch (GuzzleException $e) {
throw new CommandTransportException(
'Can\'t connect to the Icinga 2 API: %u %s',
$e->getCode(),
$e->getMessage()
);
}
try {
$responseData = Json::decode((string) $response->getBody(), true);
} catch (JsonDecodeException $e) {
throw new CommandTransportException(
'Got invalid JSON response from the Icinga 2 API: %s',
$e->getMessage()
);
}
if (! isset($responseData['results']) || empty($responseData['results'])) {
return;
}
$result = array_pop($responseData['results']);
if ($result['code'] < 200 || $result['code'] >= 300) {
throw new ApiCommandException(
'Can\'t send external Icinga command: %u %s',
$result['code'],
$result['status']
);
}
}
/**
* Send the Icinga command over the Icinga 2 API
*
* @param IcingaCommand $command
* @param int|null $now
*
* @throws CommandTransportException
*/
public function send(IcingaCommand $command, $now = null)
{
$this->sendCommand($this->renderer->render($command));
}
/**
* Try to connect to the API
*
* @throws CommandTransportException In case the connection was not successful
*/
public function probe()
{
try {
$response = (new Client())
->get($this->getUriFor(null), [
'auth' => [$this->getUsername(), $this->getPassword()],
'headers' => ['Accept' => 'application/json'],
'http_errors' => false,
'verify' => false
]);
} catch (GuzzleException $e) {
throw new CommandTransportException(
'Can\'t connect to the Icinga 2 API: %u %s',
$e->getCode(),
$e->getMessage()
);
}
try {
$responseData = Json::decode((string) $response->getBody(), true);
} catch (JsonDecodeException $e) {
throw new CommandTransportException(
'Got invalid JSON response from the Icinga 2 API: %s',
$e->getMessage()
);
}
if (! isset($responseData['results']) || empty($responseData['results'])) {
throw new CommandTransportException(
'Got invalid response from the Icinga 2 API: %s',
JSON::encode($responseData)
);
}
$result = array_pop($responseData['results']);
if (! isset($result['user']) || $result['user'] !== $this->getUsername()) {
throw new CommandTransportException(
'Got invalid response from the Icinga 2 API: %s',
JSON::encode($responseData)
);
}
}
}

View file

@ -0,0 +1,128 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Transport;
use Exception;
use Icinga\Application\Config;
use Icinga\Application\Logger;
use Icinga\Data\ConfigObject;
use Icinga\Exception\ConfigurationError;
use Icinga\Module\Icingadb\Command\IcingaCommand;
/**
* Command transport
*/
class CommandTransport implements CommandTransportInterface
{
/**
* Transport configuration
*
* @var Config
*/
protected static $config;
/**
* Get transport configuration
*
* @return Config
*
* @throws ConfigurationError
*/
public static function getConfig()
{
if (static::$config === null) {
$config = Config::module('icingadb', 'commandtransports');
if ($config->isEmpty()) {
throw new ConfigurationError(
t('No command transports have been configured in "%s".'),
$config->getConfigFile()
);
}
static::$config = $config;
}
return static::$config;
}
/**
* Create a transport from config
*
* @param ConfigObject $config
*
* @return ApiCommandTransport
*
* @throws ConfigurationError
*/
public static function createTransport(ConfigObject $config)
{
$config = clone $config;
switch (strtolower($config->transport)) {
case ApiCommandTransport::TRANSPORT:
$transport = new ApiCommandTransport();
break;
default:
throw new ConfigurationError(
t('Cannot create command transport "%s". Invalid transport defined in "%s". Use one of: %s.'),
$config->transport,
static::getConfig()->getConfigFile(),
join(', ', [ApiCommandTransport::TRANSPORT])
);
}
unset($config->transport);
foreach ($config as $key => $value) {
$method = 'set' . ucfirst($key);
if (! method_exists($transport, $method)) {
// Ignore settings from config that don't have a setter on the transport instead of throwing an
// exception here because the transport should throw an exception if it's not fully set up
// when being about to send a command
continue;
}
$transport->$method($value);
}
return $transport;
}
/**
* Send the given command over an appropriate Icinga command transport
*
* This will try one configured transport after another until the command has been successfully sent.
*
* @param IcingaCommand $command The command to send
* @param int|null $now Timestamp of the command or null for now
*
* @throws CommandTransportException If sending the Icinga command failed
*/
public function send(IcingaCommand $command, $now = null)
{
$errors = [];
foreach (static::getConfig() as $name => $transportConfig) {
$transport = static::createTransport($transportConfig);
try {
$transport->send($command, $now);
} catch (CommandTransportException $e) {
Logger::error($e);
$errors[] = sprintf('%s: %s.', $name, rtrim($e->getMessage(), '.'));
continue; // Try the next transport
}
return; // The command was successfully sent
}
if (! empty($errors)) {
throw new CommandTransportException(implode("\n", $errors));
}
throw new CommandTransportException(t(
'Failed to send external Icinga command. No transport has been configured'
. ' for this instance. Please contact your Icinga Web administrator.'
));
}
}

View file

@ -0,0 +1,31 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Transport;
use Icinga\Repository\IniRepository;
class CommandTransportConfig extends IniRepository
{
protected $configs = [
'transport' => [
'name' => 'commandtransports',
'module' => 'icingadb',
'keyColumn' => 'name'
]
];
protected $queryColumns = [
'transport' => [
'name',
'transport',
// API options
'host',
'port',
'username',
'password'
]
];
}

View file

@ -0,0 +1,14 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Transport;
use Icinga\Exception\IcingaException;
/**
* Exception thrown if a command was not sent
*/
class CommandTransportException extends IcingaException
{
}

View file

@ -0,0 +1,23 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Command\Transport;
use Icinga\Module\Icingadb\Command\IcingaCommand;
/**
* Interface for Icinga command transports
*/
interface CommandTransportInterface
{
/**
* Send an Icinga command over the Icinga command transport
*
* @param IcingaCommand $command The command to send
* @param int|null $now Timestamp of the command or null for now
*
* @throws CommandTransportException If sending the Icinga command failed
*/
public function send(IcingaCommand $command, $now = null);
}

View file

@ -0,0 +1,33 @@
<?php
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Common;
use Icinga\Module\Icingadb\Widget\BaseItemList;
/**
* @method BaseOrderedListItem getItemClass()
*/
abstract class BaseOrderedItemList extends BaseItemList
{
protected $tag = 'ol';
protected function assemble()
{
$itemClass = $this->getItemClass();
$i = 0;
foreach ($this->data as $data) {
$item = new $itemClass($data, $this);
$item->setOrder($i++);
$this->add($item);
}
if ($this->isEmpty()) {
$this->setTag('div');
$this->add(new EmptyState(t('No items found.')));
}
}
}

View file

@ -0,0 +1,37 @@
<?php
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Common;
use Icinga\Module\Icingadb\Widget\BaseListItem;
abstract class BaseOrderedListItem extends BaseListItem
{
/** @var int This element's position */
protected $order;
/**
* Set this element's position
*
* @param int $order
*
* @return $this
*/
public function setOrder($order)
{
$this->order = (int) $order;
return $this;
}
/**
* Get this element's position
*
* @return int
*/
public function getOrder()
{
return $this->order;
}
}

View file

@ -4,27 +4,18 @@
namespace Icinga\Module\Icingadb\Common;
use Icinga\Module\Icingadb\Compat\CompatBackend;
use Icinga\Module\Icingadb\Compat\CompatHost;
use Icinga\Module\Icingadb\Compat\CompatObjects;
use Icinga\Module\Icingadb\Compat\CompatService;
use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\CheckNowCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentsCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimeCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimesCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ObjectsCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\RemoveAcknowledgementCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostCheckCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostDowntimeCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceCheckCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceDowntimeCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\SendCustomNotificationCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ToggleObjectFeaturesCommandForm;
use ipl\Html\HtmlString;
use GuzzleHttp\Psr7\ServerRequest;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use Icinga\Module\Icingadb\Forms\Command\Object\AcknowledgeProblemForm;
use Icinga\Module\Icingadb\Forms\Command\Object\AddCommentForm;
use Icinga\Module\Icingadb\Forms\Command\Object\CheckNowForm;
use Icinga\Module\Icingadb\Forms\Command\Object\ProcessCheckResultForm;
use Icinga\Module\Icingadb\Forms\Command\Object\RemoveAcknowledgementForm;
use Icinga\Module\Icingadb\Forms\Command\Object\ScheduleCheckForm;
use Icinga\Module\Icingadb\Forms\Command\Object\ScheduleHostDowntimeForm;
use Icinga\Module\Icingadb\Forms\Command\Object\ScheduleServiceDowntimeForm;
use Icinga\Module\Icingadb\Forms\Command\Object\SendCustomNotificationForm;
use Icinga\Module\Icingadb\Forms\Command\Object\ToggleObjectFeaturesForm;
use ipl\Orm\Model;
use ipl\Orm\Query;
use ipl\Web\Url;
@ -82,118 +73,80 @@ trait CommandActions
return $this->commandTargetModel;
}
/**
* Get command objects
*
* @return CompatObjects
*/
protected function getCommandObjects()
{
switch ($this->getCommandTargetModel()->getTableName()) {
case 'host':
$compatClass = CompatHost::class;
break;
case 'service':
$compatClass = CompatService::class;
break;
default:
throw new LogicException('Only hosts and services are supported');
}
return new CompatObjects($this->getCommandTargets(), $compatClass);
}
/**
* Handle and register the given command form
*
* @param string|ObjectsCommandForm $form
* @param string|CommandForm $form
*/
protected function handleCommandForm($form)
{
if (is_string($form)) {
$form = new $form([
'backend' => new CompatBackend(),
'objects' => $this->getCommandObjects()
]);
/** @var \Icinga\Module\Icingadb\Forms\Command\CommandForm $form */
$form = new $form();
}
$form->setRedirectUrl($this->getCommandTargetsUrl());
$actionUrl = $this->getRequest()->getUrl();
if ($this->view->compact) {
$actionUrl = clone $actionUrl;
// TODO: This solves https://github.com/Icinga/icingadb-web/issues/124 but I'd like to omit this
// entirely. I think it should be solved like https://github.com/Icinga/icingaweb2/pull/4300 so
// that a request's url object still has params like showCompact and _dev
$actionUrl->getParams()->add('showCompact', true);
}
$form->handleRequest();
$this->addContent(HtmlString::create($form->render()));
$form->setAction($actionUrl->getAbsoluteUrl());
$form->setObjects($this->getCommandTargets());
$form->on($form::ON_SUCCESS, function () {
// This forces the column to reload nearly instantly after the redirect
// and ensures the effect of the command is visible to the user asap
$this->getResponse()->setAutoRefreshInterval(1);
$this->redirectNow($this->getCommandTargetsUrl());
});
$form->handleRequest(ServerRequest::fromGlobals());
$this->addContent($form);
}
public function acknowledgeAction()
{
$this->assertPermission('monitoring/command/acknowledge-problem');
$this->setTitle(t('Acknowledge Problem'));
$this->handleCommandForm(AcknowledgeProblemCommandForm::class);
$this->handleCommandForm(AcknowledgeProblemForm::class);
}
public function addCommentAction()
{
$this->assertPermission('monitoring/command/comment/add');
$this->setTitle(t('Add Comment'));
$this->handleCommandForm(AddCommentCommandForm::class);
$this->handleCommandForm(AddCommentForm::class);
}
public function checkNowAction()
{
$this->assertPermission('monitoring/command/schedule-check');
$this->handleCommandForm(CheckNowCommandForm::class);
}
public function deleteCommentAction()
{
$this->assertPermission('monitoring/command/comment/delete');
$this->handleCommandForm(DeleteCommentCommandForm::class);
}
public function deleteCommentsAction()
{
$this->assertPermission('monitoring/command/comment/delete');
$this->handleCommandForm(DeleteCommentsCommandForm::class);
}
public function deleteDowntimeAction()
{
$this->assertPermission('monitoring/command/downtime/delete');
$this->handleCommandForm(DeleteDowntimeCommandForm::class);
}
public function deleteDowntimesAction()
{
$this->assertPermission('monitoring/command/downtime/delete');
$this->handleCommandForm(DeleteDowntimesCommandForm::class);
$this->handleCommandForm(CheckNowForm::class);
}
public function processCheckresultAction()
{
$this->assertPermission('monitoring/command/process-check-result');
$this->setTitle(t('Submit Passive Check Result'));
$this->handleCommandForm(ProcessCheckResultCommandForm::class);
$this->handleCommandForm(ProcessCheckResultForm::class);
}
public function removeAcknowledgementAction()
{
$this->assertPermission('monitoring/command/remove-acknowledgement');
$this->handleCommandForm(RemoveAcknowledgementCommandForm::class);
$this->handleCommandForm(RemoveAcknowledgementForm::class);
}
public function scheduleCheckAction()
{
$this->assertPermission('monitoring/command/schedule-check');
switch ($this->getCommandTargetModel()->getTableName()) {
case 'host':
$this->setTitle(t('Reschedule Host Check'));
$this->handleCommandForm(ScheduleHostCheckCommandForm::class);
break;
case 'service':
$this->setTitle(t('Reschedule Service Check'));
$this->handleCommandForm(ScheduleServiceCheckCommandForm::class);
break;
}
$this->setTitle(t('Reschedule Check'));
$this->handleCommandForm(ScheduleCheckForm::class);
}
public function scheduleDowntimeAction()
@ -203,11 +156,11 @@ trait CommandActions
switch ($this->getCommandTargetModel()->getTableName()) {
case 'host':
$this->setTitle(t('Schedule Host Downtime'));
$this->handleCommandForm(ScheduleHostDowntimeCommandForm::class);
$this->handleCommandForm(ScheduleHostDowntimeForm::class);
break;
case 'service':
$this->setTitle(t('Schedule Service Downtime'));
$this->handleCommandForm(ScheduleServiceDowntimeCommandForm::class);
$this->handleCommandForm(ScheduleServiceDowntimeForm::class);
break;
}
}
@ -216,26 +169,22 @@ trait CommandActions
{
$this->assertPermission('monitoring/command/send-custom-notification');
$this->setTitle(t('Send Custom Notification'));
$this->handleCommandForm(SendCustomNotificationCommandForm::class);
$this->handleCommandForm(SendCustomNotificationForm::class);
}
public function toggleFeaturesAction()
{
$commandObjects = $this->getCommandObjects();
$form = new ToggleObjectFeaturesCommandForm([
'backend' => new CompatBackend(),
'objects' => $commandObjects
]);
$commandObjects = $this->getCommandTargets();
if (count($commandObjects) > 1) {
if (! method_exists($this, 'getFeatureStatus')) {
throw new LogicException('You must implement getFeatureStatus() first');
}
$form->load($this->getFeatureStatus());
$form = new ToggleObjectFeaturesForm($this->getFeatureStatus());
} else {
foreach ($commandObjects as $object) {
$form->load($object);
// There's only a single result, a foreach is the most compatible way to retrieve the object
$form = new ToggleObjectFeaturesForm($object);
}
}

View file

@ -29,11 +29,6 @@ abstract class HostLinks
return Url::fromPath('icingadb/host/schedule-check', ['name' => $host->name]);
}
public static function cancelDowntime(Host $host)
{
return Url::fromPath('icingadb/host/delete-downtime', ['name' => $host->name]);
}
public static function comments(Host $host)
{
return Url::fromPath('icingadb/host/comments', ['name' => $host->name]);
@ -54,11 +49,6 @@ abstract class HostLinks
return Url::fromPath('icingadb/host/remove-acknowledgement', ['name' => $host->name]);
}
public static function removeComment(Host $host)
{
return Url::fromPath('icingadb/host/delete-comment', ['name' => $host->name]);
}
public static function scheduleDowntime(Host $host)
{
return Url::fromPath('icingadb/host/schedule-downtime', ['name' => $host->name]);

View file

@ -42,14 +42,6 @@ abstract class ServiceLinks
);
}
public static function cancelDowntime(Service $service, Host $host)
{
return Url::fromPath(
'icingadb/service/delete-downtime',
['name' => $service->name, 'host.name' => $host->name]
);
}
public static function comments(Service $service, Host $host)
{
return Url::fromPath(
@ -82,14 +74,6 @@ abstract class ServiceLinks
);
}
public static function removeComment(Service $service, Host $host)
{
return Url::fromPath(
'icingadb/service/delete-comment',
['name' => $service->name, 'host.name' => $host->name]
);
}
public static function scheduleDowntime(Service $service, Host $host)
{
return Url::fromPath(

View file

@ -1,35 +0,0 @@
<?php
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Compat;
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
class CompatBackend extends MonitoringBackend
{
/**
* @param string $name Ignored
* @param mixed $config Ignored
*/
public function __construct()
{
}
/**
* @param string $programVersion Ignored
* @return true
*/
public function isIcinga2($_ = null)
{
return true;
}
/**
* @return string "2.12", hardcoded
*/
public function getProgramVersion()
{
return '2.12';
}
}

View file

@ -1,37 +0,0 @@
<?php
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Compat;
use Countable;
use IteratorAggregate;
use Traversable;
class CompatObjects implements IteratorAggregate, Countable
{
protected $query;
protected $compatClass;
public function __construct($query, $compatClass)
{
$this->query = $query;
$this->compatClass = $compatClass;
}
public function getIterator()
{
foreach ($this->query as $object) {
yield new $this->compatClass($object);
}
}
public function count()
{
if ($this->query instanceof Traversable) {
return $this->query->count();
}
return count($this->query);
}
}

View file

@ -1,44 +0,0 @@
<?php
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Compat;
use ArrayObject;
class FeatureStatus extends ArrayObject
{
public function __construct($type, $summary)
{
$prefix = "{$type}s";
$featureStatus = [
'active_checks_enabled' => $this->getFeatureStatus('active_checks_enabled', $prefix, $summary),
'passive_checks_enabled' => $this->getFeatureStatus('passive_checks_enabled', $prefix, $summary),
'notifications_enabled' => $this->getFeatureStatus('notifications_enabled', $prefix, $summary),
'event_handler_enabled' => $this->getFeatureStatus('event_handler_enabled', $prefix, $summary),
'flap_detection_enabled' => $this->getFeatureStatus('flapping_enabled', $prefix, $summary)
];
parent::__construct($featureStatus, ArrayObject::ARRAY_AS_PROPS);
}
protected function getFeatureStatus($feature, $prefix, $summary)
{
$key = "{$prefix}_{$feature}";
$value = (int) $summary->$key;
if ($value === 0) {
return 0;
}
$totalKey = "{$prefix}_total";
$total = (int) $summary->$totalKey;
if ($value === $total) {
return 1;
}
return 2;
}
}

View file

@ -0,0 +1,50 @@
<?php
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Util;
use ArrayObject;
use Icinga\Module\Icingadb\Command\Object\ToggleObjectFeatureCommand;
class FeatureStatus extends ArrayObject
{
public function __construct($type, $summary)
{
$prefix = "{$type}s";
$featureStatus = [
ToggleObjectFeatureCommand::FEATURE_ACTIVE_CHECKS =>
$this->getFeatureStatus('active_checks_enabled', $prefix, $summary),
ToggleObjectFeatureCommand::FEATURE_PASSIVE_CHECKS =>
$this->getFeatureStatus('passive_checks_enabled', $prefix, $summary),
ToggleObjectFeatureCommand::FEATURE_NOTIFICATIONS =>
$this->getFeatureStatus('notifications_enabled', $prefix, $summary),
ToggleObjectFeatureCommand::FEATURE_EVENT_HANDLER =>
$this->getFeatureStatus('event_handler_enabled', $prefix, $summary),
ToggleObjectFeatureCommand::FEATURE_FLAP_DETECTION =>
$this->getFeatureStatus('flapping_enabled', $prefix, $summary)
];
parent::__construct($featureStatus, ArrayObject::ARRAY_AS_PROPS);
}
protected function getFeatureStatus($feature, $prefix, $summary)
{
$key = "{$prefix}_{$feature}";
$value = (int) $summary->$key;
if ($value === 0) {
return 0;
}
$totalKey = "{$prefix}_total";
$total = (int) $summary->$totalKey;
if ($value === $total) {
return 1;
}
return 2;
}
}

View file

@ -12,6 +12,8 @@ use ipl\Web\Url;
/**
* Base class for item lists
*
* @todo Move this to Icinga\Module\Icingadb\Common
*/
abstract class BaseItemList extends BaseHtmlElement
{

View file

@ -11,6 +11,8 @@ use ipl\Web\Filter\QueryString;
/**
* Base class for list items
*
* @todo Move this to Icinga\Module\Icingadb\Common
*/
abstract class BaseListItem extends BaseHtmlElement
{

View file

@ -6,17 +6,13 @@ namespace Icinga\Module\Icingadb\Widget\Detail;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Common\HostLink;
use Icinga\Module\Icingadb\Common\HostLinks;
use Icinga\Module\Icingadb\Common\Links;
use Icinga\Module\Icingadb\Common\MarkdownText;
use Icinga\Module\Icingadb\Common\ServiceLink;
use Icinga\Module\Icingadb\Common\ServiceLinks;
use Icinga\Module\Icingadb\Forms\Command\Object\DeleteCommentForm;
use Icinga\Module\Icingadb\Widget\TimeUntil;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentCommandForm;
use ipl\Html\BaseHtmlElement;
use ipl\Html\Html;
use ipl\Html\HtmlDocument;
use ipl\Html\HtmlString;
use ipl\Web\Widget\Icon;
class CommentDetail extends BaseHtmlElement
{
@ -70,32 +66,14 @@ class CommentDetail extends BaseHtmlElement
protected function createRemoveCommentForm()
{
$formData = [
'comment_id' => $this->comment->name,
'comment_name' => $this->comment->name,
'redirect' => '__BACK__'
];
// TODO: Check permission
$action = Links::commentsDelete();
$action->setParam('name', $this->comment->name);
if ($this->comment->object_type === 'host') {
$action = HostLinks::removeComment($this->comment->host);
} else {
$action = ServiceLinks::removeComment($this->comment->service, $this->comment->service->host);
$formData['comment_is_service'] = true;
}
$removeCommentForm = (new DeleteCommentCommandForm())
->create()
->populate($formData)
->setAction($action);
$submitButton = $removeCommentForm->getElement('btn_submit');
$submitButton->content = (new HtmlDocument())
->add([new Icon('trash'), t('Remove Comment')])
->setSeparator(' ')
->render();
return new HtmlString($removeCommentForm->render());
return (new DeleteCommentForm())
->setObjects([$this->comment])
->populate(['redirect' => '__BACK__'])
->setAction($action->getAbsoluteUrl());
}
protected function assemble()

View file

@ -8,13 +8,12 @@ use Icinga\Date\DateFormatter;
use Icinga\Date\DateFormatter as WebDateFormatter;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Common\HostLink;
use Icinga\Module\Icingadb\Common\HostLinks;
use Icinga\Module\Icingadb\Common\Links;
use Icinga\Module\Icingadb\Common\MarkdownText;
use Icinga\Module\Icingadb\Common\ServiceLink;
use Icinga\Module\Icingadb\Common\ServiceLinks;
use Icinga\Module\Icingadb\Forms\Command\Object\DeleteDowntimeForm;
use Icinga\Module\Icingadb\Model\Downtime;
use Icinga\Module\Icingadb\Widget\HorizontalKeyValue;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimeCommandForm;
use ipl\Html\BaseHtmlElement;
use ipl\Html\Html;
use ipl\Html\HtmlDocument;
@ -73,31 +72,14 @@ class DowntimeDetail extends BaseHtmlElement
protected function createCancelDowntimeForm()
{
$formData = [
'downtime_id' => $this->downtime->name,
'downtime_name' => $this->downtime->name,
'redirect' => '__BACK__'
];
// TODO: Check permission
$action = Links::downtimesDelete();
$action->setParam('name', $this->downtime->name);
if ($this->downtime->object_type === 'host') {
$action = HostLinks::cancelDowntime($this->downtime->host);
} else {
$action = ServiceLinks::cancelDowntime($this->downtime->service, $this->downtime->service->host);
$formData['downtime_is_service'] = true;
}
$cancelDowntimeForm = (new DeleteDowntimeCommandForm())
->create()
->populate($formData)
->setAction($action);
$submitButton = $cancelDowntimeForm->getElement('btn_submit');
$submitButton->content = (new HtmlDocument())
->add([new Icon('trash'), t('Cancel Downtime')])
->setSeparator(' ')
->render();
return new HtmlString($cancelDowntimeForm->render());
return (new DeleteDowntimeForm())
->setObjects([$this->downtime])
->populate(['redirect' => '__BACK__'])
->setAction($action->getAbsoluteUrl());
}
protected function createTimeline()

View file

@ -6,11 +6,10 @@ namespace Icinga\Module\Icingadb\Widget\Detail;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Common\BaseFilter;
use Icinga\Module\Monitoring\Forms\Command\Object\CheckNowCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\RemoveAcknowledgementCommandForm;
use Icinga\Module\Icingadb\Forms\Command\Object\CheckNowForm;
use Icinga\Module\Icingadb\Forms\Command\Object\RemoveAcknowledgementForm;
use ipl\Html\BaseHtmlElement;
use ipl\Html\Html;
use ipl\Html\HtmlString;
use ipl\Web\Filter\QueryString;
use ipl\Web\Url;
@ -55,12 +54,11 @@ class MultiselectQuickActions extends BaseHtmlElement
$this->summary->$acks > 0
&& $this->getAuth()->hasPermission('monitoring/command/remove-acknowledgement')
) {
$removeAckForm = (new RemoveAcknowledgementCommandForm())
$removeAckForm = (new RemoveAcknowledgementForm())
->setAction($this->getLink('removeAcknowledgement'))
->setLabelEnabled(true)
->setObjects([true]);
->setObjects(array_fill(0, $this->summary->$acks, null));
$this->add(Html::tag('li', new HtmlString($removeAckForm->render())));
$this->add(Html::tag('li', $removeAckForm));
}
if (
@ -70,10 +68,7 @@ class MultiselectQuickActions extends BaseHtmlElement
&& $this->getAuth()->hasPermission('monitoring/command/schedule-check/active-only')
)
) {
$checkNowForm = (new CheckNowCommandForm())
->setAction($this->getLink('checkNow'));
$this->add(Html::tag('li', new HtmlString($checkNowForm->render())));
$this->add(Html::tag('li', (new CheckNowForm())->setAction($this->getLink('checkNow'))));
}
if ($this->getAuth()->hasPermission('monitoring/command/comment/add')) {
@ -151,6 +146,7 @@ class MultiselectQuickActions extends BaseHtmlElement
protected function getLink($action)
{
return Url::fromPath("icingadb/{$this->type}s/$action")
->setQueryString(QueryString::render($this->getBaseFilter()));
->setQueryString(QueryString::render($this->getBaseFilter()))
->getAbsoluteUrl();
}
}

View file

@ -12,12 +12,10 @@ use Icinga\Module\Icingadb\Common\Icons;
use Icinga\Module\Icingadb\Common\Links;
use Icinga\Module\Icingadb\Common\MarkdownText;
use Icinga\Module\Icingadb\Common\ServiceLinks;
use Icinga\Module\Icingadb\Compat\CompatBackend;
use Icinga\Module\Icingadb\Compat\CompatHost;
use Icinga\Module\Icingadb\Compat\CompatObject;
use Icinga\Module\Icingadb\Compat\CompatPluginOutput;
use Icinga\Module\Icingadb\Compat\CompatService;
use Icinga\Module\Icingadb\Compat\CustomvarFilter;
use Icinga\Module\Icingadb\Forms\Command\Object\ToggleObjectFeaturesForm;
use Icinga\Module\Icingadb\Model\Host;
use Icinga\Module\Icingadb\Widget\DowntimeList;
use Icinga\Module\Icingadb\Widget\EmptyState;
@ -25,7 +23,6 @@ use Icinga\Module\Icingadb\Widget\HorizontalKeyValue;
use Icinga\Module\Icingadb\Widget\ItemList\CommentList;
use Icinga\Module\Icingadb\Widget\ShowMore;
use Icinga\Module\Icingadb\Widget\TagList;
use Icinga\Module\Monitoring\Forms\Command\Object\ToggleObjectFeaturesCommandForm;
use Icinga\Module\Monitoring\Hook\DetailviewExtensionHook;
use Icinga\Module\Monitoring\Hook\ObjectActionsHook;
use Icinga\Web\Helper\Markdown;
@ -364,21 +361,17 @@ class ObjectDetail extends BaseHtmlElement
protected function createFeatureToggles()
{
$form = new ToggleObjectFeaturesCommandForm([
'backend' => new CompatBackend()
]);
$form = new ToggleObjectFeaturesForm($this->object);
if ($this->objectType === 'host') {
$form->load(new CompatHost($this->object));
$form->setAction(HostLinks::toggleFeatures($this->object));
$form->setAction(HostLinks::toggleFeatures($this->object)->getAbsoluteUrl());
} else {
$form->load(new CompatService($this->object));
$form->setAction(ServiceLinks::toggleFeatures($this->object, $this->object->host));
$form->setAction(ServiceLinks::toggleFeatures($this->object, $this->object->host)->getAbsoluteUrl());
}
return [
Html::tag('h2', t('Feature Commands')),
HtmlString::create($form->render())
$form
];
}

View file

@ -7,13 +7,12 @@ namespace Icinga\Module\Icingadb\Widget\Detail;
use Icinga\Chart\Donut;
use Icinga\Module\Icingadb\Common\BaseFilter;
use Icinga\Module\Icingadb\Common\Links;
use Icinga\Module\Icingadb\Compat\CompatBackend;
use Icinga\Module\Icingadb\Compat\FeatureStatus;
use Icinga\Module\Icingadb\Forms\Command\Object\ToggleObjectFeaturesForm;
use Icinga\Module\Icingadb\Util\FeatureStatus;
use Icinga\Module\Icingadb\Widget\EmptyState;
use Icinga\Module\Icingadb\Widget\HostStateBadges;
use Icinga\Module\Icingadb\Widget\ServiceStateBadges;
use Icinga\Module\Icingadb\Widget\VerticalKeyValue;
use Icinga\Module\Monitoring\Forms\Command\Object\ToggleObjectFeaturesCommandForm;
use ipl\Html\BaseHtmlElement;
use ipl\Html\Html;
use ipl\Html\HtmlString;
@ -127,25 +126,25 @@ class ObjectsDetail extends BaseHtmlElement
protected function createFeatureToggles()
{
$form = new ToggleObjectFeaturesCommandForm([
'backend' => new CompatBackend()
]);
$form->load(new FeatureStatus($this->type, $this->summary));
$form = new ToggleObjectFeaturesForm(new FeatureStatus($this->type, $this->summary));
if ($this->type === 'host') {
$form->setAction(
Links::toggleHostsFeatures()->setQueryString(QueryString::render($this->getBaseFilter()))
Links::toggleHostsFeatures()
->setQueryString(QueryString::render($this->getBaseFilter()))
->getAbsoluteUrl()
);
} else {
$form->setAction(
Links::toggleServicesFeatures()->setQueryString(QueryString::render($this->getBaseFilter()))
Links::toggleServicesFeatures()
->setQueryString(QueryString::render($this->getBaseFilter()))
->getAbsoluteUrl()
);
}
return [
Html::tag('h2', t('Feature Commands')),
HtmlString::create($form->render())
$form
];
}

View file

@ -7,13 +7,12 @@ namespace Icinga\Module\Icingadb\Widget\Detail;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Common\HostLinks;
use Icinga\Module\Icingadb\Common\ServiceLinks;
use Icinga\Module\Icingadb\Forms\Command\Object\CheckNowForm;
use Icinga\Module\Icingadb\Forms\Command\Object\RemoveAcknowledgementForm;
use Icinga\Module\Icingadb\Model\Host;
use Icinga\Module\Icingadb\Model\Service;
use Icinga\Module\Monitoring\Forms\Command\Object\CheckNowCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\RemoveAcknowledgementCommandForm;
use ipl\Html\BaseHtmlElement;
use ipl\Html\Html;
use ipl\Html\HtmlString;
use ipl\Web\Widget\Icon;
class QuickActions extends BaseHtmlElement
@ -37,12 +36,11 @@ class QuickActions extends BaseHtmlElement
if ($this->object->state->is_problem) {
if ($this->object->state->is_acknowledged) {
if ($this->getAuth()->hasPermission('monitoring/command/remove-acknowledgement')) {
$removeAckForm = (new RemoveAcknowledgementCommandForm())
$removeAckForm = (new RemoveAcknowledgementForm())
->setAction($this->getLink('removeAcknowledgement'))
->setLabelEnabled(true)
->setObjects([true]);
->setObjects([$this->object]);
$this->add(Html::tag('li', new HtmlString($removeAckForm->render())));
$this->add(Html::tag('li', $removeAckForm));
}
} elseif ($this->getAuth()->hasPermission('monitoring/command/acknowledge-problem')) {
$this->assembleAction(
@ -61,10 +59,7 @@ class QuickActions extends BaseHtmlElement
&& $this->getAuth()->hasPermission('monitoring/command/schedule-check/active-only')
)
) {
$checkNowForm = (new CheckNowCommandForm())
->setAction($this->getLink('checkNow'));
$this->add(Html::tag('li', new HtmlString($checkNowForm->render())));
$this->add(Html::tag('li', (new CheckNowForm())->setAction($this->getLink('checkNow'))));
}
if ($this->getAuth()->hasPermission('monitoring/command/comment/add')) {
@ -145,9 +140,9 @@ class QuickActions extends BaseHtmlElement
protected function getLink($action)
{
if ($this->object instanceof Host) {
return HostLinks::$action($this->object);
return HostLinks::$action($this->object)->getAbsoluteUrl();
} else {
return ServiceLinks::$action($this->object, $this->object->host);
return ServiceLinks::$action($this->object, $this->object->host)->getAbsoluteUrl();
}
}
}

View file

@ -0,0 +1,22 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Widget\ItemList;
use Icinga\Module\Icingadb\Common\BaseOrderedItemList;
use ipl\Web\Url;
class CommandTransportList extends BaseOrderedItemList
{
protected function init()
{
$this->getAttributes()->add('class', 'command-transport-list');
$this->setDetailUrl(Url::fromPath('icingadb/command-transport/show'));
}
protected function getItemClass()
{
return CommandTransportListItem::class;
}
}

View file

@ -0,0 +1,69 @@
<?php
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
namespace Icinga\Module\Icingadb\Widget\ItemList;
use Icinga\Module\Icingadb\Common\BaseOrderedListItem;
use ipl\Html\BaseHtmlElement;
use ipl\Html\HtmlElement;
use ipl\Stdlib\Filter;
use ipl\Web\Url;
use ipl\Web\Widget\Icon;
use ipl\Web\Widget\Link;
class CommandTransportListItem extends BaseOrderedListItem
{
protected function init()
{
$this->setDetailFilter(Filter::equal('name', $this->item->name));
}
protected function assembleHeader(BaseHtmlElement $header)
{
}
protected function assembleMain(BaseHtmlElement $main)
{
$main->add(new Link(
new HtmlElement('strong', null, $this->item->name),
Url::fromPath('icingadb/command-transport/show', ['name' => $this->item->name])
));
$main->add(new Link(
new Icon('trash', ['title' => sprintf(t('Remove command transport "%s"'), $this->item->name)]),
Url::fromPath('icingadb/command-transport/remove', ['name' => $this->item->name]),
[
'class' => 'pull-right action-link',
'data-icinga-modal' => true,
'data-no-icinga-ajax' => true
]
));
if ($this->getOrder() + 1 < $this->list->count()) {
$main->add((new Link(
new Icon('arrow-down'),
Url::fromPath('icingadb/command-transport/sort', [
'name' => $this->item->name,
'pos' => $this->getOrder() + 1
]),
['class' => 'pull-right action-link']
))->setBaseTarget('_self'));
}
if ($this->getOrder() > 0) {
$main->add((new Link(
new Icon('arrow-up'),
Url::fromPath('icingadb/command-transport/sort', [
'name' => $this->item->name,
'pos' => $this->getOrder() - 1
]),
['class' => 'pull-right action-link']
))->setBaseTarget('_self'));
}
}
protected function createVisual()
{
}
}

View file

@ -0,0 +1,19 @@
/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
a.spinner.active > i.fa-sync-alt,
button.spinner.active > i.fa-sync-alt,
i.fa-sync-alt.spinner.active {
&:before {
// We use a different icon font, hence why the unicode is redefined (overridden) here
content: '\f2f1';
}
}
a.spinner.active > i.fa-trash,
button.spinner.active > i.fa-trash,
i.fa-trash.spinner.active {
&:before {
// We use a different icon font, hence why the unicode is redefined (overridden) here
content: '\f1f8';
}
}

View file

@ -0,0 +1,21 @@
.downtime-duration {
> label {
display: flex;
flex: 1 1 auto;
flex-flow: row-reverse;
input {
flex: 1 1 auto;
}
span {
margin-left: .5em;
padding: .5625em 0;
line-height: 1.1em;
}
&:not(:last-child) {
margin-right: 1.5em;
}
}
}

View file

@ -42,7 +42,7 @@
ActionList.prototype.onClick = function (event) {
var _this = event.data.self;
var $activeItems;
var $target = $(this);
var $target = $(event.currentTarget);
var $item = $target.closest('.list-item');
var $list = $item.parent('.action-list');
@ -90,7 +90,7 @@
$activeItems = $list.find('.list-item.active');
if ($activeItems.length === 0) {
if (_this.icinga.loader.getLinkTargetFor($item).attr('id') === 'col2') {
if (_this.icinga.loader.getLinkTargetFor($target).attr('id') === 'col2') {
_this.icinga.ui.layout1col();
}
} else {
@ -107,7 +107,7 @@
}
_this.icinga.loader.loadUrl(
url, _this.icinga.loader.getLinkTargetFor($item)
url, _this.icinga.loader.getLinkTargetFor($target)
);
}
};
@ -147,7 +147,7 @@
var $list = $target.find('.action-list');
if ($list.length && $list.is('[data-icinga-multiselect-url]')) {
if ($list.length && $list.is('[data-icinga-multiselect-url], [data-icinga-detail-url]')) {
var _this = event.data.self;
var detailUrl = _this.icinga.utils.parseUrl(_this.icinga.history.getCol2State().replace(/^#!/, ''));