Merge branch 'feature/api-command-transport-11398'

resolves #11398
This commit is contained in:
Eric Lippmann 2016-09-01 10:50:50 +02:00
commit 8c079d1db8
33 changed files with 1120 additions and 48 deletions

View File

@ -37,6 +37,7 @@ class CommentController extends Controller
'type' => 'comment_type',
'persistent' => 'comment_is_persistent',
'expiration' => 'comment_expiration',
'name' => 'comment_name',
'host_name',
'service_description',
'host_display_name',
@ -73,6 +74,7 @@ class CommentController extends Controller
->populate(array(
'comment_id' => $this->comment->id,
'comment_is_service' => isset($this->comment->service_description),
'comment_name' => $this->comment->name,
'redirect' => $listUrl
))
->handleRequest();

View File

@ -46,6 +46,7 @@ class CommentsController extends Controller
'type' => 'comment_type',
'persistent' => 'comment_is_persistent',
'expiration' => 'comment_expiration',
'name' => 'comment_name',
'host_name',
'service_description',
'host_display_name',

View File

@ -44,6 +44,7 @@ class DowntimeController extends Controller
'is_fixed' => 'downtime_is_fixed',
'is_in_effect' => 'downtime_is_in_effect',
'entry_time' => 'downtime_entry_time',
'name' => 'downtime_name',
'host_state',
'service_state',
'host_name',
@ -91,6 +92,7 @@ class DowntimeController extends Controller
->populate(array(
'downtime_id' => $this->downtime->id,
'downtime_is_service' => $isService,
'downtime_name' => $this->downtime->name,
'redirect' => Url::fromPath('monitoring/list/downtimes'),
))
->handleRequest();

View File

@ -51,6 +51,7 @@ class DowntimesController extends Controller
'is_fixed' => 'downtime_is_fixed',
'is_in_effect' => 'downtime_is_in_effect',
'entry_time' => 'downtime_entry_time',
'name' => 'downtime_name',
'host_state',
'service_state',
'host_name',

View File

@ -229,6 +229,7 @@ class ListController extends Controller
'is_fixed' => 'downtime_is_fixed',
'is_in_effect' => 'downtime_is_in_effect',
'entry_time' => 'downtime_entry_time',
'name' => 'downtime_name',
'host_state',
'service_state',
'host_name',
@ -422,6 +423,7 @@ class ListController extends Controller
'type' => 'comment_type',
'persistent' => 'comment_is_persistent',
'expiration' => 'comment_expiration',
'name' => 'comment_name',
'host_name',
'service_description',
'host_display_name',

View File

@ -20,42 +20,6 @@ class DeleteCommentCommandForm extends CommandForm
$this->setAttrib('class', 'inline');
}
/**
* {@inheritdoc}
*/
public function createElements(array $formData = array())
{
$this->addElements(
array(
array(
'hidden',
'comment_id',
array(
'required' => true,
'validators' => array('NotEmpty'),
'decorators' => array('ViewHelper')
)
),
array(
'hidden',
'comment_is_service',
array(
'filters' => array('Boolean'),
'decorators' => array('ViewHelper')
)
),
array(
'hidden',
'redirect',
array(
'decorators' => array('ViewHelper')
)
)
)
);
return $this;
}
/**
* {@inheritdoc}
*/
@ -80,14 +44,59 @@ class DeleteCommentCommandForm extends CommandForm
return $this;
}
/**
* {@inheritdoc}
*/
public function createElements(array $formData = array())
{
$this->addElements(
array(
array(
'hidden',
'comment_id',
array(
'required' => true,
'validators' => array('NotEmpty'),
'decorators' => array('ViewHelper')
)
),
array(
'hidden',
'comment_is_service',
array(
'filters' => array('Boolean'),
'decorators' => array('ViewHelper')
)
),
array(
'hidden',
'comment_name',
array(
'decorators' => array('ViewHelper')
)
),
array(
'hidden',
'redirect',
array(
'decorators' => array('ViewHelper')
)
)
)
);
return $this;
}
/**
* {@inheritdoc}
*/
public function onSuccess()
{
$cmd = new DeleteCommentCommand();
$cmd->setIsService($this->getElement('comment_is_service')->getValue())
->setCommentId($this->getElement('comment_id')->getValue());
$cmd
->setCommentId($this->getElement('comment_id')->getValue())
->setCommentName($this->getElement('comment_name')->getValue())
->setIsService($this->getElement('comment_is_service')->getValue());
$this->getTransport($this->request)->send($cmd);
$redirect = $this->getElement('redirect')->getValue();
if (! empty($redirect)) {

View File

@ -72,6 +72,7 @@ class DeleteCommentsCommandForm extends CommandForm
$cmd = new DeleteCommentCommand();
$cmd
->setCommentId($comment->id)
->setCommentName($comment->name)
->setIsService(isset($comment->service_description));
$this->getTransport($this->request)->send($cmd);
}

View File

@ -68,6 +68,13 @@ class DeleteDowntimeCommandForm extends CommandForm
'filters' => array('Boolean')
)
),
array(
'hidden',
'downtime_name',
array(
'decorators' => array('ViewHelper')
)
),
array(
'hidden',
'redirect',
@ -86,8 +93,10 @@ class DeleteDowntimeCommandForm extends CommandForm
public function onSuccess()
{
$cmd = new DeleteDowntimeCommand();
$cmd->setDowntimeId($this->getElement('downtime_id')->getValue());
$cmd->setIsService($this->getElement('downtime_is_service')->getValue());
$cmd
->setDowntimeId($this->getElement('downtime_id')->getValue())
->setDowntimeName($this->getElement('downtime_name')->getValue())
->setIsService($this->getElement('downtime_is_service')->getValue());
$this->getTransport($this->request)->send($cmd);
$redirect = $this->getElement('redirect')->getValue();

View File

@ -72,6 +72,7 @@ class DeleteDowntimesCommandForm extends CommandForm
$delDowntime = new DeleteDowntimeCommand();
$delDowntime
->setDowntimeId($downtime->id)
->setDowntimeName($downtime->name)
->setIsService(isset($downtime->service_description));
$this->getTransport($this->request)->send($delDowntime);
}

View File

@ -0,0 +1,73 @@
<?php
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Config\Transport;
use Icinga\Web\Form;
class ApiTransportForm extends Form
{
/**
* {@inheritdoc}
*/
public function init()
{
$this->setName('form_config_command_transport_api');
}
/**
* {@inheritdoc}
*/
public function createElements(array $formData = array())
{
$this->addElements(array(
array(
'text',
'host',
array(
'required' => true,
'label' => $this->translate('Host'),
'description' => $this->translate(
'Hostname or address of the remote Icinga instance'
)
)
),
array(
'number',
'port',
array(
'required' => true,
'label' => $this->translate('Port'),
'description' => $this->translate('SSH port to connect to on the remote Icinga instance'),
'value' => 5665
)
),
array(
'text',
'username',
array(
'required' => true,
'label' => $this->translate('API Username'),
'description' => $this->translate(
'User to log in as on the remote Icinga instance. Please note that key-based SSH login must be'
. ' possible for this user'
)
)
),
array(
'password',
'password',
array(
'required' => true,
'label' => $this->translate('API Password'),
'description' => $this->translate(
'User to log in as on the remote Icinga instance. Please note that key-based SSH login must be'
. ' possible for this user'
)
)
)
));
return $this;
}
}

View File

@ -7,8 +7,10 @@ use InvalidArgumentException;
use Icinga\Exception\IcingaException;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfigForm;
use Icinga\Module\Monitoring\Command\Transport\ApiCommandTransport;
use Icinga\Module\Monitoring\Command\Transport\LocalCommandFile;
use Icinga\Module\Monitoring\Command\Transport\RemoteCommandFile;
use Icinga\Module\Monitoring\Forms\Config\Transport\ApiTransportForm;
use Icinga\Module\Monitoring\Forms\Config\Transport\LocalTransportForm;
use Icinga\Module\Monitoring\Forms\Config\Transport\RemoteTransportForm;
@ -68,7 +70,7 @@ class TransportConfigForm extends ConfigForm
*
* @param string $type The transport type for which to return a form
*
* @return Form
* @return \Icinga\Web\Form
*
* @throws InvalidArgumentException In case the given transport type is invalid
*/
@ -79,6 +81,8 @@ class TransportConfigForm extends ConfigForm
return new LocalTransportForm();
case RemoteCommandFile::TRANSPORT;
return new RemoteTransportForm();
case ApiCommandTransport::TRANSPORT:
return new ApiTransportForm();
default:
throw new InvalidArgumentException(
sprintf($this->translate('Invalid command transport type "%s" given'), $type)
@ -223,7 +227,8 @@ class TransportConfigForm extends ConfigForm
$transportTypes = array(
LocalCommandFile::TRANSPORT => $this->translate('Local Command File'),
RemoteCommandFile::TRANSPORT => $this->translate('Remote Command File')
RemoteCommandFile::TRANSPORT => $this->translate('Remote Command File'),
ApiCommandTransport::TRANSPORT => $this->translate('Icinga 2 API')
);
$transportType = isset($formData['transport']) ? $formData['transport'] : null;

View File

@ -92,7 +92,7 @@
); ?>
<span class="config-label-meta">&#40;<?= sprintf(
$this->translate('Type: %s'),
$config->host !== null ? $this->translate('Remote') : $this->translate('Local')
ucfirst($config->get('transport', 'local'))
) ?>&#41;
</span>
</td>

View File

@ -53,7 +53,8 @@
$deleteButton->populate(
array(
'comment_id' => $comment->id,
'comment_is_service' => isset($comment->service_description)
'comment_is_service' => isset($comment->service_description),
'comment_name' => $comment->name
)
);
$deleteButton->getElement('btn_submit')

View File

@ -61,8 +61,9 @@
$deleteButton->setAttrib('class', $deleteButton->getAttrib('class') . ' remove-action');
$deleteButton->populate(
array(
'downtime_id' => $downtime->id,
'downtime_is_service' => isset($downtime->service_description)
'downtime_id' => $downtime->id,
'downtime_is_service' => isset($downtime->service_description),
'downtime_name' => $downtime->name
)
);
$deleteButton->getElement('btn_submit')

View File

@ -23,6 +23,7 @@ class CommentQuery extends IdoQuery
'comment_expiration' => 'c.comment_expiration',
'comment_internal_id' => 'c.comment_internal_id',
'comment_is_persistent' => 'c.comment_is_persistent',
'comment_name' => 'c.comment_name',
'comment_timestamp' => 'c.comment_timestamp',
'comment_type' => 'c.comment_type',
'instance_name' => 'c.instance_name',
@ -85,6 +86,9 @@ class CommentQuery extends IdoQuery
*/
protected function joinBaseTables()
{
if (version_compare($this->getIdoVersion(), '1.14.0', '<')) {
$this->columnMap['comments']['comment_name'] = '(NULL)';
}
$this->commentQuery = $this->db->select();
$this->select->from(
array('c' => $this->commentQuery),

View File

@ -27,6 +27,7 @@ class DowntimeQuery extends IdoQuery
'downtime_is_fixed' => 'd.downtime_is_fixed',
'downtime_is_flexible' => 'd.downtime_is_flexible',
'downtime_is_in_effect' => 'd.downtime_is_in_effect',
'downtime_name' => 'd.downtime_name',
'downtime_scheduled_end' => 'd.downtime_scheduled_end',
'downtime_scheduled_start' => 'd.downtime_scheduled_start',
'downtime_start' => 'd.downtime_start',
@ -90,6 +91,9 @@ class DowntimeQuery extends IdoQuery
*/
protected function joinBaseTables()
{
if (version_compare($this->getIdoVersion(), '1.14.0', '<')) {
$this->columnMap['downtimes']['downtime_name'] = '(NULL)';
}
$this->downtimeQuery = $this->db->select();
$this->select->from(
array('d' => $this->downtimeQuery),

View File

@ -34,6 +34,7 @@ class HostcommentQuery extends IdoQuery
'comment_expiration' => 'CASE c.expires WHEN 1 THEN UNIX_TIMESTAMP(c.expiration_time) ELSE NULL END',
'comment_internal_id' => 'c.internal_comment_id',
'comment_is_persistent' => 'c.is_persistent',
'comment_name' => 'c.name',
'comment_timestamp' => 'UNIX_TIMESTAMP(c.comment_time)',
'comment_type' => "CASE c.entry_type WHEN 1 THEN 'comment' WHEN 2 THEN 'downtime' WHEN 3 THEN 'flapping' WHEN 4 THEN 'ack' END",
'host' => 'ho.name1 COLLATE latin1_general_ci',
@ -72,6 +73,9 @@ class HostcommentQuery extends IdoQuery
*/
protected function joinBaseTables()
{
if (version_compare($this->getIdoVersion(), '1.14.0', '<')) {
$this->columnMap['comments']['comment_name'] = '(NULL)';
}
$this->select->from(
array('c' => $this->prefix . 'comments'),
array()

View File

@ -38,6 +38,7 @@ class HostdowntimeQuery extends IdoQuery
'downtime_is_fixed' => 'sd.is_fixed',
'downtime_is_flexible' => 'CASE WHEN sd.is_fixed = 0 THEN 1 ELSE 0 END',
'downtime_is_in_effect' => 'sd.is_in_effect',
'downtime_name' => 'sd.name',
'downtime_scheduled_end' => 'UNIX_TIMESTAMP(sd.scheduled_end_time)',
'downtime_scheduled_start' => 'UNIX_TIMESTAMP(sd.scheduled_start_time)',
'downtime_start' => 'UNIX_TIMESTAMP(CASE WHEN UNIX_TIMESTAMP(sd.trigger_time) > 0 then sd.trigger_time ELSE sd.scheduled_start_time END)',
@ -78,6 +79,9 @@ class HostdowntimeQuery extends IdoQuery
*/
protected function joinBaseTables()
{
if (version_compare($this->getIdoVersion(), '1.14.0', '<')) {
$this->columnMap['downtimes']['downtime_name'] = '(NULL)';
}
$this->select->from(
array('sd' => $this->prefix . 'scheduleddowntime'),
array()

View File

@ -34,6 +34,7 @@ class ServicecommentQuery extends IdoQuery
'comment_expiration' => 'CASE c.expires WHEN 1 THEN UNIX_TIMESTAMP(c.expiration_time) ELSE NULL END',
'comment_internal_id' => 'c.internal_comment_id',
'comment_is_persistent' => 'c.is_persistent',
'comment_name' => 'c.name',
'comment_timestamp' => 'UNIX_TIMESTAMP(c.comment_time)',
'comment_type' => "CASE c.entry_type WHEN 1 THEN 'comment' WHEN 2 THEN 'downtime' WHEN 3 THEN 'flapping' WHEN 4 THEN 'ack' END",
'host' => 'so.name1 COLLATE latin1_general_ci',
@ -77,6 +78,9 @@ class ServicecommentQuery extends IdoQuery
*/
protected function joinBaseTables()
{
if (version_compare($this->getIdoVersion(), '1.14.0', '<')) {
$this->columnMap['comments']['comment_name'] = '(NULL)';
}
$this->select->from(
array('c' => $this->prefix . 'comments'),
array()

View File

@ -38,6 +38,7 @@ class ServicedowntimeQuery extends IdoQuery
'downtime_is_fixed' => 'sd.is_fixed',
'downtime_is_flexible' => 'CASE WHEN sd.is_fixed = 0 THEN 1 ELSE 0 END',
'downtime_is_in_effect' => 'sd.is_in_effect',
'downtime_name' => 'sd.name',
'downtime_scheduled_end' => 'UNIX_TIMESTAMP(sd.scheduled_end_time)',
'downtime_scheduled_start' => 'UNIX_TIMESTAMP(sd.scheduled_start_time)',
'downtime_start' => 'UNIX_TIMESTAMP(CASE WHEN UNIX_TIMESTAMP(sd.trigger_time) > 0 then sd.trigger_time ELSE sd.scheduled_start_time END)',
@ -83,6 +84,9 @@ class ServicedowntimeQuery extends IdoQuery
*/
protected function joinBaseTables()
{
if (version_compare($this->getIdoVersion(), '1.14.0', '<')) {
$this->columnMap['downtimes']['downtime_name'] = '(NULL)';
}
$this->select->from(
array('sd' => $this->prefix . 'scheduleddowntime'),
array()

View File

@ -0,0 +1,86 @@
<?php
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\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)
{
$command = new static();
$command
->setEndpoint($endpoint)
->setData($data);
return $command;
}
/**
* 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

@ -17,6 +17,15 @@ class DeleteCommentCommand extends IcingaCommand
*/
protected $commentId;
/**
* Name of the comment (Icinga 2.4+)
*
* Required for removing the comment via Icinga 2's API.
*
* @var string
*/
protected $commentName;
/**
* Whether the command affects a service comment
*
@ -47,6 +56,33 @@ class DeleteCommentCommand extends IcingaCommand
return $this;
}
/**
* Get the name of the comment (Icinga 2.4+)
*
* Required for removing the comment via Icinga 2's API.
*
* @return string
*/
public function getCommentName()
{
return $this->commentName;
}
/**
* Set the name of the comment (Icinga 2.4+)
*
* Required for removing the comment via Icinga 2's API.
*
* @param string $commentName
*
* @return $this
*/
public function setCommentName($commentName)
{
$this->commentName = $commentName;
return $this;
}
/**
* Get whether the command affects a service comment
*

View File

@ -17,6 +17,15 @@ class DeleteDowntimeCommand extends IcingaCommand
*/
protected $downtimeId;
/**
* Name of the downtime (Icinga 2.4+)
*
* Required for removing the downtime via Icinga 2's API.
*
* @var string
*/
protected $downtimeName;
/**
* Whether the command affects a service downtime
*
@ -47,6 +56,33 @@ class DeleteDowntimeCommand extends IcingaCommand
return $this;
}
/**
* 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;
}
/**
* Get whether the command affects a service
*

View File

@ -0,0 +1,276 @@
<?php
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Command\Renderer;
use Icinga\Module\Monitoring\Command\IcingaApiCommand;
use Icinga\Module\Monitoring\Command\Instance\ToggleInstanceFeatureCommand;
use Icinga\Module\Monitoring\Command\Object\AcknowledgeProblemCommand;
use Icinga\Module\Monitoring\Command\Object\AddCommentCommand;
use Icinga\Module\Monitoring\Command\Object\DeleteCommentCommand;
use Icinga\Module\Monitoring\Command\Object\DeleteDowntimeCommand;
use Icinga\Module\Monitoring\Command\Object\ProcessCheckResultCommand;
use Icinga\Module\Monitoring\Command\Object\RemoveAcknowledgementCommand;
use Icinga\Module\Monitoring\Command\Object\ScheduleServiceCheckCommand;
use Icinga\Module\Monitoring\Command\Object\ScheduleServiceDowntimeCommand;
use Icinga\Module\Monitoring\Command\Object\SendCustomNotificationCommand;
use Icinga\Module\Monitoring\Command\Object\ToggleObjectFeatureCommand;
use Icinga\Module\Monitoring\Command\IcingaCommand;
use Icinga\Module\Monitoring\Object\MonitoredObject;
use InvalidArgumentException;
/**
* 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 MonitoredObject $object
*
* @return array
*/
protected function applyFilter(array &$data, MonitoredObject $object)
{
if ($object->getType() === $object::TYPE_HOST) {
/** @var \Icinga\Module\Monitoring\Object\Host $object */
$data['host'] = $object->getName();
} else {
/** @var \Icinga\Module\Monitoring\Object\Service $object */
$data['service'] = sprintf('%s!%s', $object->getHost()->getName(), $object->getName());
}
}
/**
* Render a command
*
* @param IcingaCommand $command
*
* @return IcingaApiCommand
*/
public function render(IcingaCommand $command)
{
$renderMethod = 'render' . $command->getName();
if (! method_exists($this, $renderMethod)) {
die($renderMethod);
}
return $this->$renderMethod($command);
}
public function renderAddComment(AddCommentCommand $command)
{
$endpoint = 'actions/add-comment';
$data = array(
'author' => $command->getAuthor(),
'comment' => $command->getComment()
);
$this->applyFilter($data, $command->getObject());
return IcingaApiCommand::create($endpoint, $data);
}
public function renderSendCustomNotification(SendCustomNotificationCommand $command)
{
$endpoint = 'actions/send-custom-notification';
$data = array(
'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 = array(
'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(ScheduleServiceCheckCommand $command)
{
$endpoint = 'actions/reschedule-check';
$data = array(
'next_check' => $command->getCheckTime(),
'force_check' => $command->getForced()
);
$this->applyFilter($data, $command->getObject());
return IcingaApiCommand::create($endpoint, $data);
}
public function renderScheduleDowntime(ScheduleServiceDowntimeCommand $command)
{
$endpoint = 'actions/schedule-downtime';
$data = array(
'author' => $command->getAuthor(),
'comment' => $command->getComment(),
'start_time' => $command->getStart(),
'end_time' => $command->getEnd(),
'duration' => $command->getDuration(),
'fixed' => $command->getFixed(),
'trigger_name' => $command->getTriggerId()
);
$this->applyFilter($data, $command->getObject());
return IcingaApiCommand::create($endpoint, $data);
}
public function renderAcknowledgeProblem(AcknowledgeProblemCommand $command)
{
$endpoint = 'actions/acknowledge-problem';
$data = array(
'author' => $command->getAuthor(),
'comment' => $command->getComment(),
'expiry' => $command->getExpireTime(),
'sticky' => $command->getSticky(),
'notify' => $command->getNotify()
);
$this->applyFilter($data, $command->getObject());
return IcingaApiCommand::create($endpoint, $data);
}
public function renderToggleObjectFeature(ToggleObjectFeatureCommand $command)
{
if ($command->getEnabled() === true) {
$enabled = true;
} else {
$enabled = false;
}
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->getType() === ToggleObjectFeatureCommand::TYPE_HOST) {
/** @var \Icinga\Module\Monitoring\Object\Host $object */
$endpoint .= 'hosts';
} else {
/** @var \Icinga\Module\Monitoring\Object\Service $object */
$endpoint .= 'services';
}
$data = array(
'attrs' => array(
$attr => $enabled
)
);
$this->applyFilter($data, $object);
return IcingaApiCommand::create($endpoint, $data);
}
public function renderDeleteComment(DeleteCommentCommand $command)
{
$endpoint = 'actions/remove-comment';
$data = array(
'comment' => $command->getCommentName()
);
return IcingaApiCommand::create($endpoint, $data);
}
public function renderDeleteDowntime(DeleteDowntimeCommand $command)
{
$endpoint = 'actions/remove-downtime';
$data = array(
'downtime' => $command->getDowntimeName()
);
return IcingaApiCommand::create($endpoint, $data);
}
public function renderRemoveAcknowledgement(RemoveAcknowledgementCommand $command)
{
$endpoint = 'actions/remove-acknowledgement';
$data = array();
$this->applyFilter($data, $command->getObject());
return IcingaApiCommand::create($endpoint, $data);
}
public function renderToggleInstanceFeature(ToggleInstanceFeatureCommand $command)
{
$endpoint = 'objects/icingaapplications/' . $this->getApp();
if ($command->getEnabled() === true) {
$enabled = true;
} else {
$enabled = false;
}
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 = array(
'attrs' => array(
$attr => $enabled
)
);
return IcingaApiCommand::create($endpoint, $data);
}
}

View File

@ -0,0 +1,227 @@
<?php
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Command\Transport;
use Icinga\Application\Logger;
use Icinga\Module\Monitoring\Command\IcingaCommand;
use Icinga\Module\Monitoring\Command\Renderer\IcingaApiCommandRenderer;
use Icinga\Module\Monitoring\Exception\CommandTransportException;
use Icinga\Module\Monitoring\Web\Rest\RestRequest;
/**
* 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);
}
/**
* 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)
{
$command = $this->renderer->render($command);
Logger::debug(
'Sending Icinga command "%s" to the API "%s:%u"',
$command->getEndpoint(),
$this->getHost(),
$this->getPort()
);
$response = RestRequest::post($this->getUriFor($command->getEndpoint()))
->authenticateWith($this->getUsername(), $this->getPassword())
->sendJson()
->noStrictSsl()
->setPayload($command->getData())
->send();
if (isset($response['error'])) {
throw new CommandTransportException(
'Can\'t send external Icinga command: %u %s',
$response['error'],
$response['status']
);
}
$result = array_pop($response['results']);
if ($result['code'] < 200 || $result['code'] >= 300) {
throw new CommandTransportException(
'Can\'t send external Icinga command: %u %s',
$result['code'],
$result['status']
);
}
}
}

View File

@ -65,6 +65,9 @@ class CommandTransport implements CommandTransportInterface
case RemoteCommandFile::TRANSPORT:
$transport = new RemoteCommandFile();
break;
case ApiCommandTransport::TRANSPORT:
$transport = new ApiCommandTransport();
break;
case LocalCommandFile::TRANSPORT:
case '': // Casting null to string is the empty string
$transport = new LocalCommandFile();
@ -74,12 +77,13 @@ class CommandTransport implements CommandTransportInterface
mt(
'monitoring',
'Cannot create command transport "%s". Invalid transport'
. ' defined in "%s". Use one of "%s" or "%s".'
. ' defined in "%s". Use one of "%s", "%s" or "%s".'
),
$config->transport,
static::getConfig()->getConfigFile(),
LocalCommandFile::TRANSPORT,
RemoteCommandFile::TRANSPORT
RemoteCommandFile::TRANSPORT,
ApiCommandTransport::TRANSPORT
);
}

View File

@ -19,6 +19,7 @@ class Comment extends DataView
'comment_expiration',
'comment_internal_id',
'comment_is_persistent',
'comment_name',
'comment_timestamp',
'comment_type',
'host_display_name',

View File

@ -23,6 +23,7 @@ class Downtime extends DataView
'downtime_is_fixed',
'downtime_is_flexible',
'downtime_is_in_effect',
'downtime_name',
'downtime_scheduled_end',
'downtime_scheduled_start',
'downtime_start',

View File

@ -20,6 +20,7 @@ class Hostcomment extends DataView
'comment_expiration',
'comment_internal_id',
'comment_is_persistent',
'comment_name',
'comment_timestamp',
'comment_type',
'host_display_name',

View File

@ -24,6 +24,7 @@ class Hostdowntime extends DataView
'downtime_is_fixed',
'downtime_is_flexible',
'downtime_is_in_effect',
'downtime_name',
'downtime_scheduled_end',
'downtime_scheduled_start',
'downtime_start',

View File

@ -20,6 +20,7 @@ class Servicecomment extends DataView
'comment_expiration',
'comment_internal_id',
'comment_is_persistent',
'comment_name',
'comment_timestamp',
'comment_type',
'host_display_name',

View File

@ -21,6 +21,7 @@ class Servicedowntime extends DataView
'downtime_is_fixed',
'downtime_is_flexible',
'downtime_is_in_effect',
'downtime_name',
'downtime_scheduled_end',
'downtime_scheduled_start',
'downtime_start',

View File

@ -0,0 +1,269 @@
<?php
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Web\Rest;
use Exception;
/**
* REST Request
*/
class RestRequest
{
/**
* Request URI
*
* @var string
*/
protected $uri;
/**
* Request method
*
* @var string
*/
protected $method;
/**
* Request content type
*
* @var string
*/
protected $contentType;
/**
* Whether to authenticate with basic auth
*
* @var bool
*/
protected $hasBasicAuth;
/**
* Auth username
*
* @var string
*/
protected $username;
/**
* Auth password
*
* @var string
*/
protected $password;
/**
* Request payload
*
* @var mixed
*/
protected $payload;
/**
* Whether strict SSL is enabled
*
* @var bool
*/
protected $strictSsl = true;
/**
* Request timeout
*
* @var int
*/
protected $timeout = 30;
/**
* Create a POST REST request
*
* @param string $uri
*
* @return static
*/
public static function post($uri)
{
$request = new static;
$request->uri = $uri;
$request->method = 'POST';
return $request;
}
/**
* Send content type JSON
*
* @return $this
*/
public function sendJson()
{
$this->contentType = 'application/json';
return $this;
}
/**
* Set basic auth credentials
*
* @param string $username
* @param string $password
*
* @return $this
*/
public function authenticateWith($username, $password)
{
$this->hasBasicAuth = true;
$this->username = $username;
$this->password = $password;
return $this;
}
/**
* Set request payload
*
* @param mixed $payload
*
* @return $this
*/
public function setPayload($payload)
{
$this->payload = $payload;
return $this;
}
/**
* Disable strict SSL
*
* @return $this
*/
public function noStrictSsl()
{
$this->strictSsl = false;
return $this;
}
/**
* Serialize payload according to content type
*
* @param mixed $payload
* @param string $contentType
*
* @return string
*/
public function serializePayload($payload, $contentType)
{
switch ($contentType) {
case 'application/json':
$payload = json_encode($payload);
break;
}
return $payload;
}
/**
* Send the request
*
* @return mixed
*
* @throws Exception
*/
public function send()
{
$defaults = array(
'host' => 'localhost',
'path' => '/'
);
$url = array_merge($defaults, parse_url($this->uri));
if (isset($url['port'])) {
$url['host'] .= sprintf(':%u', $url['port']);
}
if (isset($url['query'])) {
$url['path'] .= sprintf('?%s', $url['query']);
}
$headers = array(
"{$this->method} {$url['path']} HTTP/1.1",
"Host: {$url['host']}",
"Content-Type: {$this->contentType}",
'Accept: application/json',
// Bypass "Expect: 100-continue" timeouts
'Expect:'
);
$ch = curl_init();
$options = array(
CURLOPT_URL => $this->uri,
CURLOPT_TIMEOUT => $this->timeout,
// Ignore proxy settings
CURLOPT_PROXY => '',
CURLOPT_CUSTOMREQUEST => $this->method,
CURLOPT_RETURNTRANSFER => true
);
if ($this->strictSsl) {
$options[CURLOPT_SSL_VERIFYHOST] = 2;
$options[CURLOPT_SSL_VERIFYPEER] = true;
} else {
$options[CURLOPT_SSL_VERIFYHOST] = false;
$options[CURLOPT_SSL_VERIFYPEER] = false;
}
if ($this->hasBasicAuth) {
$options[CURLOPT_USERPWD] = sprintf('%s:%s', $this->username, $this->password);
}
if (! empty($this->payload)) {
$payload = $this->serializePayload($this->payload, $this->contentType);
$options[CURLOPT_POSTFIELDS] = $payload;
}
$options[CURLOPT_HTTPHEADER] = $headers;
curl_setopt_array($ch, $options);
$result = curl_exec($ch);
if ($result === false) {
throw new Exception(curl_error($ch));
}
curl_close($ch);
$response = @json_decode($result, true);
if ($response === null) {
if (version_compare(PHP_VERSION, '5.5.0', '>=')) {
throw new Exception(json_last_error_msg());
} else {
switch (json_last_error()) {
case JSON_ERROR_DEPTH:
$msg = 'The maximum stack depth has been exceeded';
break;
case JSON_ERROR_CTRL_CHAR:
$msg = 'Control character error, possibly incorrectly encoded';
break;
case JSON_ERROR_STATE_MISMATCH:
$msg = 'Invalid or malformed JSON';
break;
case JSON_ERROR_SYNTAX:
$msg = 'Syntax error';
break;
case JSON_ERROR_UTF8:
$msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
break;
default:
$msg = 'An error occured when parsing a JSON string';
}
throw new Exception($msg);
}
}
return $response;
}
}