Skip to content

Commit

Permalink
Improve performance of Icinga 2 API commands processed in bulk (#721)
Browse files Browse the repository at this point in the history
  • Loading branch information
nilmerg committed Aug 10, 2023
2 parents 90a6878 + 137de73 commit 17adfc2
Show file tree
Hide file tree
Showing 27 changed files with 383 additions and 477 deletions.
2 changes: 1 addition & 1 deletion application/controllers/HostController.php
Expand Up @@ -86,7 +86,7 @@ public function indexAction()
public function sourceAction()
{
$this->assertPermission('icingadb/object/show-source');
$apiResult = (new CommandTransport())->send((new GetObjectCommand())->setObject($this->host));
$apiResult = (new CommandTransport())->send((new GetObjectCommand())->setObjects([$this->host]));

if ($this->host->state->is_overdue) {
$this->controls->addAttributes(['class' => 'overdue']);
Expand Down
2 changes: 1 addition & 1 deletion application/controllers/ServiceController.php
Expand Up @@ -91,7 +91,7 @@ public function indexAction()
public function sourceAction()
{
$this->assertPermission('icingadb/object/show-source');
$apiResult = (new CommandTransport())->send((new GetObjectCommand())->setObject($this->service));
$apiResult = (new CommandTransport())->send((new GetObjectCommand())->setObjects([$this->service]));

if ($this->service->state->is_overdue) {
$this->controls->addAttributes(['class' => 'overdue']);
Expand Down
55 changes: 34 additions & 21 deletions application/forms/Command/CommandForm.php
Expand Up @@ -4,18 +4,23 @@

namespace Icinga\Module\Icingadb\Forms\Command;

use ArrayIterator;
use Exception;
use Generator;
use Icinga\Application\Logger;
use Icinga\Module\Icingadb\Command\IcingaCommand;
use Icinga\Module\Icingadb\Command\Transport\CommandTransport;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Web\Notification;
use Icinga\Web\Session;
use ipl\Html\Form;
use ipl\Orm\Model;
use ipl\Web\Common\CsrfCounterMeasure;
use Traversable;

abstract class CommandForm extends Form
{
use Auth;
use CsrfCounterMeasure;

protected $defaultAttributes = ['class' => 'icinga-form icinga-controls'];
Expand Down Expand Up @@ -71,13 +76,13 @@ abstract protected function assembleElements();
abstract protected function assembleSubmitButton();

/**
* Get the command to issue for the given object
* Get the commands to issue for the given objects
*
* @param Model $object
* @param Traversable<Model> $objects
*
* @return IcingaCommand|IcingaCommand[]|null NULL in case no command should be issued for the object
* @return Traversable<IcingaCommand>
*/
abstract protected function getCommand(Model $object);
abstract protected function getCommands(Traversable $objects): Traversable;

protected function assemble()
{
Expand All @@ -89,23 +94,14 @@ protected function assemble()
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();
}
$objects = $this->getObjects();

foreach ($this->getCommands(is_array($objects) ? new ArrayIterator($objects) : $objects) as $command) {
try {
$this->sendCommand($command);
} catch (Exception $e) {
Logger::error($e->getMessage());
$errors[] = $e->getMessage();
}
}

Expand Down Expand Up @@ -133,4 +129,21 @@ protected function sendCommand(IcingaCommand $command)
{
(new CommandTransport())->send($command);
}

/**
* Yield the $objects the currently logged in user has the permission $permission for
*
* @param string $permission
* @param Traversable $objects
*
* @return Generator
*/
protected function filterGrantedOn(string $permission, Traversable $objects): Generator
{
foreach ($objects as $object) {
if ($this->isGrantedOn($permission, $object)) {
yield $object;
}
}
}
}
Expand Up @@ -5,16 +5,13 @@
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 Icinga\Web\Notification;
use ipl\Orm\Model;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use Traversable;

class ToggleInstanceFeaturesForm extends CommandForm
{
use Auth;

protected $features;

protected $featureStatus;
Expand Down Expand Up @@ -136,7 +133,7 @@ protected function assembleSubmitButton()
{
}

protected function getCommand(Model $object): \Generator
protected function getCommands(Traversable $objects): Traversable
{
foreach ($this->features as $feature => $spec) {
$featureState = $this->getElement($feature)->isChecked();
Expand Down
42 changes: 18 additions & 24 deletions application/forms/Command/Object/AcknowledgeProblemForm.php
Expand Up @@ -8,22 +8,19 @@
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 Icinga\Module\Icingadb\Model\Host;
use Icinga\Web\Notification;
use ipl\Html\Attributes;
use ipl\Html\HtmlElement;
use ipl\Html\Text;
use ipl\Orm\Model;
use ipl\Validator\CallbackValidator;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use ipl\Web\Widget\Icon;
use Traversable;

class AcknowledgeProblemForm extends CommandForm
{
use Auth;

public function __construct()
{
$this->on(self::ON_SUCCESS, function () {
Expand Down Expand Up @@ -187,28 +184,25 @@ protected function assembleSubmitButton()
(new IcingaFormDecorator())->decorate($this->getElement('btn_submit'));
}

/**
* @return ?AcknowledgeProblemCommand
*/
protected function getCommand(Model $object)
protected function getCommands(Traversable $objects): Traversable
{
if (! $this->isGrantedOn('icingadb/command/acknowledge-problem', $object)) {
return null;
}
$granted = $this->filterGrantedOn('icingadb/command/acknowledge-problem', $objects);

if ($granted->valid()) {
$command = new AcknowledgeProblemCommand();
$command->setObjects($granted);
$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());
}

$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());
yield $command;
}

return $command;
}
}
34 changes: 14 additions & 20 deletions application/forms/Command/Object/AddCommentForm.php
Expand Up @@ -8,22 +8,19 @@
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 Icinga\Module\Icingadb\Model\Host;
use Icinga\Web\Notification;
use ipl\Html\Attributes;
use ipl\Html\HtmlElement;
use ipl\Html\Text;
use ipl\Orm\Model;
use ipl\Validator\CallbackValidator;
use ipl\Web\FormDecorator\IcingaFormDecorator;
use ipl\Web\Widget\Icon;
use Traversable;

class AddCommentForm extends CommandForm
{
use Auth;

public function __construct()
{
$this->on(self::ON_SUCCESS, function () {
Expand Down Expand Up @@ -142,25 +139,22 @@ protected function assembleSubmitButton()
(new IcingaFormDecorator())->decorate($this->getElement('btn_submit'));
}

/**
* @return ?AddCommentCommand
*/
protected function getCommand(Model $object)
protected function getCommands(Traversable $objects): Traversable
{
if (! $this->isGrantedOn('icingadb/command/comment/add', $object)) {
return null;
}
$granted = $this->filterGrantedOn('icingadb/command/comment/add', $objects);

$command = new AddCommentCommand();
$command->setObject($object);
$command->setComment($this->getValue('comment'));
$command->setAuthor($this->getAuth()->getUser()->getUsername());
if ($granted->valid()) {
$command = new AddCommentCommand();
$command->setObjects($granted);
$command->setComment($this->getValue('comment'));
$command->setAuthor($this->getAuth()->getUser()->getUsername());

if (($expireTime = $this->getValue('expire_time'))) {
/** @var DateTime $expireTime */
$command->setExpireTime($expireTime->getTimestamp());
}
if (($expireTime = $this->getValue('expire_time'))) {
/** @var DateTime $expireTime */
$command->setExpireTime($expireTime->getTimestamp());
}

return $command;
yield $command;
}
}
}
45 changes: 23 additions & 22 deletions application/forms/Command/Object/CheckNowForm.php
Expand Up @@ -4,17 +4,15 @@

namespace Icinga\Module\Icingadb\Forms\Command\Object;

use Generator;
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;
use Traversable;

class CheckNowForm extends CommandForm
{
use Auth;

protected $defaultAttributes = ['class' => 'inline'];

public function __construct()
Expand Down Expand Up @@ -46,26 +44,29 @@ protected function assembleSubmitButton()
);
}

/**
* @return ?ScheduleCheckCommand
*/
protected function getCommand(Model $object)
protected function getCommands(Traversable $objects): Traversable
{
if (
! $this->isGrantedOn('icingadb/command/schedule-check', $object)
&& (
! $object->active_checks_enabled
|| ! $this->isGrantedOn('icingadb/command/schedule-check/active-only', $object)
)
) {
return null;
}
$granted = (function () use ($objects): Generator {
foreach ($objects as $object) {
if (
$this->isGrantedOn('icingadb/command/schedule-check', $object)
|| (
$object->active_checks_enabled
&& $this->isGrantedOn('icingadb/command/schedule-check/active-only', $object)
)
) {
yield $object;
}
}
})();

$command = new ScheduleCheckCommand();
$command->setObject($object);
$command->setCheckTime(time());
$command->setForced();
if ($granted->valid()) {
$command = new ScheduleCheckCommand();
$command->setObjects($granted);
$command->setCheckTime(time());
$command->setForced();

return $command;
yield $command;
}
}
}
30 changes: 16 additions & 14 deletions application/forms/Command/Object/DeleteCommentForm.php
Expand Up @@ -4,17 +4,16 @@

namespace Icinga\Module\Icingadb\Forms\Command\Object;

use Generator;
use Icinga\Module\Icingadb\Command\Object\DeleteCommentCommand;
use Icinga\Module\Icingadb\Common\Auth;
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
use Icinga\Web\Notification;
use ipl\Orm\Model;
use ipl\Web\Common\RedirectOption;
use ipl\Web\Widget\Icon;
use Traversable;

class DeleteCommentForm extends CommandForm
{
use Auth;
use RedirectOption;

protected $defaultAttributes = ['class' => 'inline'];
Expand Down Expand Up @@ -55,19 +54,22 @@ protected function assembleSubmitButton()
);
}

/**
* @return ?DeleteCommentCommand
*/
protected function getCommand(Model $object)
protected function getCommands(Traversable $objects): Traversable
{
if (! $this->isGrantedOn('icingadb/command/comment/delete', $object->{$object->object_type})) {
return null;
}
$granted = (function () use ($objects): Generator {
foreach ($objects as $object) {
if ($this->isGrantedOn('icingadb/command/comment/delete', $object->{$object->object_type})) {
yield $object;
}
}
})();

$command = new DeleteCommentCommand();
$command->setCommentName($object->name);
$command->setAuthor($this->getAuth()->getUser()->getUsername());
if ($granted->valid()) {
$command = new DeleteCommentCommand();
$command->setObjects($granted);
$command->setAuthor($this->getAuth()->getUser()->getUsername());

return $command;
yield $command;
}
}
}

0 comments on commit 17adfc2

Please sign in to comment.