TransportConfigForm: validate whether the Icinga 2 API can be connected to

refs #2674
This commit is contained in:
Alexander A. Klimov 2017-02-10 14:11:56 +01:00
parent d2341369a9
commit 96e7411e25
5 changed files with 175 additions and 2 deletions

View File

@ -64,7 +64,8 @@ class ApiTransportForm extends Form
'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'
)
),
'renderPassword' => true
)
)
));

View File

@ -3,6 +3,9 @@
namespace Icinga\Module\Monitoring\Forms\Config;
use Icinga\Data\ConfigObject;
use Icinga\Module\Monitoring\Command\Transport\CommandTransport;
use Icinga\Module\Monitoring\Exception\CommandTransportException;
use InvalidArgumentException;
use Icinga\Application\Platform;
use Icinga\Exception\IcingaException;
@ -34,6 +37,11 @@ class TransportConfigForm extends ConfigForm
*/
protected $instanceNames;
/**
* @var bool
*/
protected $validatePartial = true;
/**
* Initialize this form
*/
@ -250,6 +258,59 @@ class TransportConfigForm extends ConfigForm
$this->addSubForm($this->getTransportForm($transportType)->create($formData), 'transport_form');
}
/**
* Add a submit button to this form and one to manually validate the configuration
*
* Calls parent::addSubmitButton() to add the submit button.
*
* @return $this
*/
public function addSubmitButton()
{
parent::addSubmitButton();
if ($this->getSubForm('transport_form') instanceof ApiTransportForm) {
$this->getElement('btn_submit')
->setDecorators(array('ViewHelper'));
$this->addElement(
'submit',
'transport_validation',
array(
'ignore' => true,
'label' => $this->translate('Validate Configuration'),
'data-progress-label' => $this->translate('Validation In Progress'),
'decorators' => array('ViewHelper')
)
);
$this->setAttrib('data-progress-element', 'transport-progress');
$this->addElement(
'note',
'transport-progress',
array(
'decorators' => array(
'ViewHelper',
array('Spinner', array('id' => 'transport-progress'))
)
)
);
$this->addDisplayGroup(
array('btn_submit', 'transport_validation', 'transport-progress'),
'submit_validation',
array(
'decorators' => array(
'FormElements',
array('HtmlTag', array('tag' => 'div', 'class' => 'control-group form-controls'))
)
)
);
}
return $this;
}
/**
* Populate the configuration of the transport to load
*/
@ -261,4 +322,57 @@ class TransportConfigForm extends ConfigForm
$this->populate($data);
}
}
/**
* {@inheritdoc}
*/
public function isValidPartial(array $formData)
{
$isValidPartial = parent::isValidPartial($formData);
$transportValidation = $this->getElement('transport_validation');
if ($transportValidation !== null && $transportValidation->isChecked() && $this->isValid($formData)) {
$this->info($this->translate('The configuration has been successfully validated.'));
}
return $isValidPartial;
}
/**
* {@inheritdoc}
*/
public function isValid($formData)
{
if (! parent::isValid($formData)) {
return false;
}
if (! ($this->getElement('transport_validation') === null || (
$this->isSubmitted() && isset($formData['force_creation']) && $formData['force_creation'])
)) {
try {
CommandTransport::createTransport(new ConfigObject($this->getValues()))->probe();
} catch (CommandTransportException $e) {
$this->error(sprintf(
$this->translate('Failed to successfully validate the configuration: %s'),
$e->getMessage()
));
$this->addElement(
'checkbox',
'force_creation',
array(
'order' => 0,
'ignore' => true,
'label' => $this->translate('Force Changes'),
'description' => $this->translate('Check this box to enforce changes without connectivity validation')
)
);
return false;
}
}
return true;
}
}

View File

@ -9,6 +9,7 @@ use Icinga\Module\Monitoring\Command\IcingaApiCommand;
use Icinga\Module\Monitoring\Command\IcingaCommand;
use Icinga\Module\Monitoring\Command\Renderer\IcingaApiCommandRenderer;
use Icinga\Module\Monitoring\Exception\CommandTransportException;
use Icinga\Module\Monitoring\Exception\CurlException;
use Icinga\Module\Monitoring\Web\Rest\RestRequest;
/**
@ -238,4 +239,32 @@ class ApiCommandTransport implements CommandTransportInterface
{
$this->sendCommand($this->renderer->render($command));
}
/**
* Try to connect to the API
*
* @throws CommandTransportException In case of failure
*/
public function probe()
{
$request = RestRequest::get($this->getUriFor(null))
->authenticateWith($this->getUsername(), $this->getPassword())
->noStrictSsl();
try {
$response = $request->send();
} catch (CurlException $e) {
throw new CommandTransportException('Couldn\'t connect to the Icinga 2 API: %s', $e->getMessage());
} catch (JsonDecodeException $e) {
throw new CommandTransportException('Got invalid JSON response from the Icinga 2 API: %s', $e->getMessage());
}
if (isset($response['error'])) {
throw new CommandTransportException(
'Can\'t connect to the Icinga 2 API: %u %s',
$response['error'],
$response['status']
);
}
}
}

View File

@ -0,0 +1,13 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Exception;
use Icinga\Exception\IcingaException;
/**
* Exception thrown if {@link curl_exec()} fails
*/
class CurlException extends IcingaException
{
}

View File

@ -6,6 +6,7 @@ namespace Icinga\Module\Monitoring\Web\Rest;
use Exception;
use Icinga\Application\Logger;
use Icinga\Util\Json;
use Icinga\Module\Monitoring\Exception\CurlException;
/**
* REST Request
@ -75,6 +76,21 @@ class RestRequest
*/
protected $timeout = 30;
/**
* Create a GET REST request
*
* @param string $uri
*
* @return static
*/
public static function get($uri)
{
$request = new static;
$request->uri = $uri;
$request->method = 'GET';
return $request;
}
/**
* Create a POST REST request
*
@ -251,7 +267,7 @@ class RestRequest
$result = curl_exec($ch);
if ($result === false) {
throw new Exception(curl_error($ch));
throw new CurlException('%s', curl_error($ch));
}
curl_close($ch);