Merge branch 'feature/rename-monitoring-instances-to-command-transports-and-allow-to-link-them-with-a-monitoring-instance-9651'

resolves #9651
resolves #8981
fixes #9765
This commit is contained in:
Johannes Meyer 2015-08-31 13:05:52 +02:00
commit 656e5aad01
20 changed files with 689 additions and 458 deletions

View File

@ -275,9 +275,11 @@ The first release candidate of Icinga Web 2 introduces the following non-backwar
`icingaweb_group_membership` were altered to ensure referential integrity.
Please use the upgrade script located in **etc/schema/** to update your
database schema
* Users who are using PostgreSQL < v9.1 are required to upgrade their
environment to v9.1+ as this is the new minimum required version
for utilizing PostgreSQL as database backend
* The restrictions `monitoring/hosts/filter` and `monitoring/services/filter`
provided by the monitoring module were merged together. The new
restriction is called `monitoring/filter/objects` and supports only a
@ -287,12 +289,16 @@ The first release candidate of Icinga Web 2 introduces the following non-backwar
## <a id="upgrading-to-2.0.0"></a> Upgrading to Icinga Web 2 2.0.0
* Icinga Web 2 installations from package on RHEL/CentOS 7 now depend on `php-ZendFramework` which is available through
the [EPEL repository](http://fedoraproject.org/wiki/EPEL). Before, Zend was installed as Icinga Web 2 vendor library
through the package `icingaweb2-vendor-zend`. After upgrading, please make sure to remove the package
`icingaweb2-vendor-zend`.
the [EPEL repository](http://fedoraproject.org/wiki/EPEL). Before, Zend was installed as Icinga Web 2 vendor library
through the package `icingaweb2-vendor-zend`. After upgrading, please make sure to remove the package
`icingaweb2-vendor-zend`.
* Icinga Web 2 version 2.0.0 requires permissions for accessing modules. Those permissions are automatically generated
for each installed module in the format `module/<moduleName>`. Administrators have to grant the module permissions to
users and/or user groups in the roles configuration for permitting access to specific modules.
In addition, restrictions provided by modules are now configurable for each installed module too. Before,
a module had to be enabled before having the possibility to configure restrictions.
for each installed module in the format `module/<moduleName>`. Administrators have to grant the module permissions to
users and/or user groups in the roles configuration for permitting access to specific modules.
In addition, restrictions provided by modules are now configurable for each installed module too. Before,
a module had to be enabled before having the possibility to configure restrictions.
* The **instances.ini** configuration file provided by the monitoring module
has been renamed to **commandtransports.ini**. The content and location of
the file remains unchanged.

View File

@ -8,11 +8,11 @@ use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Module\Monitoring\Forms\Config\BackendConfigForm;
use Icinga\Module\Monitoring\Forms\Config\InstanceConfigForm;
use Icinga\Module\Monitoring\Forms\Config\SecurityConfigForm;
use Icinga\Web\Controller;
use Icinga\Web\Notification;
use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Forms\Config\BackendConfigForm;
use Icinga\Module\Monitoring\Forms\Config\SecurityConfigForm;
use Icinga\Module\Monitoring\Forms\Config\TransportConfigForm;
/**
* Configuration controller for editing monitoring resources
@ -20,12 +20,12 @@ use Icinga\Web\Notification;
class ConfigController extends Controller
{
/**
* Display a list of available backends and instances
* Display a list of available backends and command transports
*/
public function indexAction()
{
$this->view->backendsConfig = $this->Config('backends');
$this->view->instancesConfig = $this->Config('instances');
$this->view->transportConfig = $this->Config('commandtransports');
$this->view->tabs = $this->Module()->getConfigTabs()->activate('backends');
}
@ -149,31 +149,34 @@ class ConfigController extends Controller
}
/**
* Remove a monitoring instance
* Remove a command transport
*/
public function removeinstanceAction()
public function removetransportAction()
{
$instanceName = $this->params->getRequired('instance');
$transportName = $this->params->getRequired('transport');
$instanceForm = new InstanceConfigForm();
$instanceForm->setIniConfig($this->Config('instances'));
$transportForm = new TransportConfigForm();
$transportForm->setIniConfig($this->Config('commandtransports'));
$form = new ConfirmRemovalForm();
$form->setRedirectUrl('monitoring/config');
$form->setTitle(sprintf($this->translate('Remove Monitoring Instance %s'), $instanceName));
$form->addDescription($this->translate(
'If you have still any environments or views referring to this instance, '
. 'you won\'t be able to send commands anymore after deletion.'
));
$form->setOnSuccess(function (ConfirmRemovalForm $form) use ($instanceName, $instanceForm) {
$form->setTitle(sprintf($this->translate('Remove Command Transport %s'), $transportName));
$form->info(
$this->translate(
'If you have still any environments or views referring to this transport, '
. 'you won\'t be able to send commands anymore after deletion.'
),
false
);
$form->setOnSuccess(function (ConfirmRemovalForm $form) use ($transportName, $transportForm) {
try {
$instanceForm->delete($instanceName);
$transportForm->delete($transportName);
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($instanceForm->save()) {
Notification::success(sprintf(t('Monitoring instance "%s" successfully removed'), $instanceName));
if ($transportForm->save()) {
Notification::success(sprintf(t('Command transport "%s" successfully removed'), $transportName));
return true;
}
@ -186,19 +189,20 @@ class ConfigController extends Controller
}
/**
* Edit a monitoring instance
* Edit a command transport
*/
public function editinstanceAction()
public function edittransportAction()
{
$instanceName = $this->params->getRequired('instance');
$transportName = $this->params->getRequired('transport');
$form = new InstanceConfigForm();
$form = new TransportConfigForm();
$form->setRedirectUrl('monitoring/config');
$form->setTitle(sprintf($this->translate('Edit Monitoring Instance %s'), $instanceName));
$form->setIniConfig($this->Config('instances'));
$form->setOnSuccess(function (InstanceConfigForm $form) use ($instanceName) {
$form->setTitle(sprintf($this->translate('Edit Command Transport %s'), $transportName));
$form->setIniConfig($this->Config('commandtransports'));
$form->setInstanceNames($this->backend->select()->from('instance', array('instance_name'))->fetchColumn());
$form->setOnSuccess(function (TransportConfigForm $form) use ($transportName) {
try {
$form->edit($instanceName, array_map(
$form->edit($transportName, array_map(
function ($v) {
return $v !== '' ? $v : null;
},
@ -210,7 +214,7 @@ class ConfigController extends Controller
}
if ($form->save()) {
Notification::success(sprintf(t('Monitoring instance "%s" successfully updated'), $instanceName));
Notification::success(sprintf(t('Command transport "%s" successfully updated'), $transportName));
return true;
}
@ -218,10 +222,10 @@ class ConfigController extends Controller
});
try {
$form->load($instanceName);
$form->load($transportName);
$form->handleRequest();
} catch (NotFoundError $_) {
$this->httpNotFound(sprintf($this->translate('Monitoring instance "%s" not found'), $instanceName));
$this->httpNotFound(sprintf($this->translate('Command transport "%s" not found'), $transportName));
}
$this->view->form = $form;
@ -229,15 +233,16 @@ class ConfigController extends Controller
}
/**
* Create a new monitoring instance
* Create a new command transport
*/
public function createinstanceAction()
public function createtransportAction()
{
$form = new InstanceConfigForm();
$form = new TransportConfigForm();
$form->setRedirectUrl('monitoring/config');
$form->setTitle($this->translate('Create New Monitoring Instance'));
$form->setIniConfig($this->Config('instances'));
$form->setOnSuccess(function (InstanceConfigForm $form) {
$form->setTitle($this->translate('Create New Command Transport'));
$form->setIniConfig($this->Config('commandtransports'));
$form->setInstanceNames($this->backend->select()->from('instance', array('instance_name'))->fetchColumn());
$form->setOnSuccess(function (TransportConfigForm $form) {
try {
$form->add(array_filter($form->getValues()));
} catch (Exception $e) {
@ -246,7 +251,7 @@ class ConfigController extends Controller
}
if ($form->save()) {
Notification::success(t('Monitoring instance successfully created'));
Notification::success(t('Command transport successfully created'));
return true;
}
@ -269,6 +274,5 @@ class ConfigController extends Controller
$this->view->form = $form;
$this->view->tabs = $this->Module()->getConfigTabs()->activate('security');
$this->render('form');
}
}

View File

@ -3,10 +3,12 @@
namespace Icinga\Module\Monitoring\Forms\Command;
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
use Icinga\Module\Monitoring\Command\Transport\CommandTransport;
use Icinga\Exception\ConfigurationError;
use Icinga\Web\Form;
use Icinga\Web\Request;
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
use Icinga\Module\Monitoring\Command\Transport\CommandTransport;
use Icinga\Module\Monitoring\Command\Transport\CommandTransportInterface;
/**
* Base class for command forms
@ -46,18 +48,28 @@ abstract class CommandForm extends Form
/**
* Get the transport used to send commands
*
* @param Request $request
* @param Request $request
*
* @return \Icinga\Module\Monitoring\Command\Transport\CommandTransportInterface
* @return CommandTransportInterface
*
* @throws ConfigurationError
*/
public function getTransport(Request $request)
{
$instance = $request->getParam('instance');
if ($instance !== null) {
$transport = CommandTransport::create($instance);
if (($transportName = $request->getParam('transport')) !== null) {
$config = CommandTransport::getConfig();
if ($config->hasSection($transportName)) {
$transport = CommandTransport::createTransport($config->getSection($transportName));
} else {
throw new ConfigurationError(sprintf(
mt('monitoring', 'Command transport "%s" not found.'),
$transportName
));
}
} else {
$transport = CommandTransport::first();
$transport = new CommandTransport();
}
return $transport;
}
}

View File

@ -1,245 +0,0 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Config;
use InvalidArgumentException;
use Icinga\Exception\IcingaException;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfigForm;
use Icinga\Module\Monitoring\Command\Transport\LocalCommandFile;
use Icinga\Module\Monitoring\Command\Transport\RemoteCommandFile;
use Icinga\Module\Monitoring\Forms\Config\Instance\LocalInstanceForm;
use Icinga\Module\Monitoring\Forms\Config\Instance\RemoteInstanceForm;
/**
* Form for managing monitoring instances
*/
class InstanceConfigForm extends ConfigForm
{
/**
* The instance to load when displaying the form for the first time
*
* @var string
*/
protected $instanceToLoad;
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_monitoring_instance');
$this->setSubmitLabel($this->translate('Save Changes'));
}
/**
* Return a form object for the given instance type
*
* @param string $type The instance type for which to return a form
*
* @return Form
*
* @throws InvalidArgumentException In case the given instance type is invalid
*/
public function getInstanceForm($type)
{
switch (strtolower($type)) {
case LocalCommandFile::TRANSPORT:
return new LocalInstanceForm();
case RemoteCommandFile::TRANSPORT;
return new RemoteInstanceForm();
default:
throw new InvalidArgumentException(
sprintf($this->translate('Invalid monitoring instance type "%s" given'), $type)
);
}
}
/**
* Populate the form with the given instance's config
*
* @param string $name
*
* @return $this
*
* @throws NotFoundError In case no instance with the given name is found
*/
public function load($name)
{
if (! $this->config->hasSection($name)) {
throw new NotFoundError('No monitoring instance called "%s" found', $name);
}
$this->instanceToLoad = $name;
return $this;
}
/**
* Add a new instance
*
* The instance to add is identified by the array-key `name'.
*
* @param array $data
*
* @return $this
*
* @throws InvalidArgumentException In case $data does not contain a instance name
* @throws IcingaException In case a instance with the same name already exists
*/
public function add(array $data)
{
if (! isset($data['name'])) {
throw new InvalidArgumentException('Key \'name\' missing');
}
$instanceName = $data['name'];
if ($this->config->hasSection($instanceName)) {
throw new IcingaException(
$this->translate('A monitoring instance with the name "%s" does already exist'),
$instanceName
);
}
unset($data['name']);
$this->config->setSection($instanceName, $data);
return $this;
}
/**
* Edit an existing instance
*
* @param string $name
* @param array $data
*
* @return $this
*
* @throws NotFoundError In case no instance with the given name is found
*/
public function edit($name, array $data)
{
if (! $this->config->hasSection($name)) {
throw new NotFoundError('No monitoring instance called "%s" found', $name);
}
$instanceConfig = $this->config->getSection($name);
if (isset($data['name'])) {
if ($data['name'] !== $name) {
$this->config->removeSection($name);
$name = $data['name'];
}
unset($data['name']);
}
$instanceConfig->merge($data);
foreach ($instanceConfig->toArray() as $k => $v) {
if ($v === null) {
unset($instanceConfig->$k);
}
}
$this->config->setSection($name, $instanceConfig);
return $this;
}
/**
* Remove a instance
*
* @param string $name
*
* @return $this
*/
public function delete($name)
{
$this->config->removeSection($name);
return $this;
}
/**
* Create and add elements to this form
*
* @param array $formData
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => $this->translate('Instance Name'),
'description' => $this->translate(
'The name of this monitoring instance that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);
$instanceTypes = array(
LocalCommandFile::TRANSPORT => $this->translate('Local Command File'),
RemoteCommandFile::TRANSPORT => $this->translate('Remote Command File')
);
$instanceType = isset($formData['transport']) ? $formData['transport'] : null;
if ($instanceType === null) {
$instanceType = key($instanceTypes);
}
$this->addElements(array(
array(
'select',
'transport',
array(
'required' => true,
'autosubmit' => true,
'label' => $this->translate('Instance Type'),
'description' => $this->translate('The type of transport to use for this monitoring instance'),
'multiOptions' => $instanceTypes
)
)
));
$this->addSubForm($this->getInstanceForm($instanceType)->create($formData), 'instance_form');
}
/**
* Populate the configuration of the instance to load
*/
public function onRequest()
{
if ($this->instanceToLoad) {
$data = $this->config->getSection($this->instanceToLoad)->toArray();
$data['name'] = $this->instanceToLoad;
$this->populate($data);
}
}
/**
* Retrieve all form element values
*
* @param bool $suppressArrayNotation Ignored
*
* @return array
*/
public function getValues($suppressArrayNotation = false)
{
$values = parent::getValues();
$values = array_merge($values, $values['instance_form']);
unset($values['instance_form']);
return $values;
}
}

View File

@ -1,11 +1,11 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Config\Instance;
namespace Icinga\Module\Monitoring\Forms\Config\Transport;
use Icinga\Web\Form;
class LocalInstanceForm extends Form
class LocalTransportForm extends Form
{
/**
* (non-PHPDoc)
@ -13,7 +13,7 @@ class LocalInstanceForm extends Form
*/
public function init()
{
$this->setName('form_config_monitoring_instance_local');
$this->setName('form_config_command_transport_local');
}
/**

View File

@ -1,16 +1,16 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Config\Instance;
namespace Icinga\Module\Monitoring\Forms\Config\Transport;
use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError;
use Icinga\Web\Form;
class RemoteInstanceForm extends Form
class RemoteTransportForm extends Form
{
/**
* The available monitoring instance resources split by type
* The available resources split by type
*
* @var array
*/
@ -22,7 +22,7 @@ class RemoteInstanceForm extends Form
*/
public function init()
{
$this->setName('form_config_monitoring_instance_remote');
$this->setName('form_config_command_transport_remote');
}
/**
@ -44,7 +44,7 @@ class RemoteInstanceForm extends Form
}
if (empty($resources)) {
throw new ConfigurationError($this->translate('Could not find any valid monitoring instance resources'));
throw new ConfigurationError($this->translate('Could not find any valid SSH resources'));
}
$this->resources = $resources;

View File

@ -0,0 +1,290 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Config;
use InvalidArgumentException;
use Icinga\Exception\IcingaException;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfigForm;
use Icinga\Module\Monitoring\Command\Transport\LocalCommandFile;
use Icinga\Module\Monitoring\Command\Transport\RemoteCommandFile;
use Icinga\Module\Monitoring\Forms\Config\Transport\LocalTransportForm;
use Icinga\Module\Monitoring\Forms\Config\Transport\RemoteTransportForm;
/**
* Form for managing command transports
*/
class TransportConfigForm extends ConfigForm
{
/**
* The transport to load when displaying the form for the first time
*
* @var string
*/
protected $transportToLoad;
/**
* The names of all available Icinga instances
*
* @var array
*/
protected $instanceNames;
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_command_transports');
$this->setSubmitLabel($this->translate('Save Changes'));
}
/**
* Set the names of all available Icinga instances
*
* @param array $names
*
* @return $this
*/
public function setInstanceNames(array $names)
{
$this->instanceNames = $names;
return $this;
}
/**
* Return the names of all available Icinga instances
*
* @return array
*/
public function getInstanceNames()
{
return $this->instanceNames ?: array();
}
/**
* Return a form object for the given transport type
*
* @param string $type The transport type for which to return a form
*
* @return Form
*
* @throws InvalidArgumentException In case the given transport type is invalid
*/
public function getTransportForm($type)
{
switch (strtolower($type)) {
case LocalCommandFile::TRANSPORT:
return new LocalTransportForm();
case RemoteCommandFile::TRANSPORT;
return new RemoteTransportForm();
default:
throw new InvalidArgumentException(
sprintf($this->translate('Invalid command transport type "%s" given'), $type)
);
}
}
/**
* Populate the form with the given transport's config
*
* @param string $name
*
* @return $this
*
* @throws NotFoundError In case no transport with the given name is found
*/
public function load($name)
{
if (! $this->config->hasSection($name)) {
throw new NotFoundError('No command transport called "%s" found', $name);
}
$this->transportToLoad = $name;
return $this;
}
/**
* Add a new command transport
*
* The transport to add is identified by the array-key `name'.
*
* @param array $data
*
* @return $this
*
* @throws InvalidArgumentException In case $data does not contain a transport name
* @throws IcingaException In case a transport with the same name already exists
*/
public function add(array $data)
{
if (! isset($data['name'])) {
throw new InvalidArgumentException('Key \'name\' missing');
}
$transportName = $data['name'];
if ($this->config->hasSection($transportName)) {
throw new IcingaException(
$this->translate('A command transport with the name "%s" does already exist'),
$transportName
);
}
unset($data['name']);
$this->config->setSection($transportName, $data);
return $this;
}
/**
* Edit an existing command transport
*
* @param string $name
* @param array $data
*
* @return $this
*
* @throws NotFoundError In case no transport with the given name is found
*/
public function edit($name, array $data)
{
if (! $this->config->hasSection($name)) {
throw new NotFoundError('No command transport called "%s" found', $name);
}
$transportConfig = $this->config->getSection($name);
if (isset($data['name'])) {
if ($data['name'] !== $name) {
$this->config->removeSection($name);
$name = $data['name'];
}
unset($data['name']);
}
$transportConfig->merge($data);
foreach ($transportConfig->toArray() as $k => $v) {
if ($v === null) {
unset($transportConfig->$k);
}
}
$this->config->setSection($name, $transportConfig);
return $this;
}
/**
* Remove a command transport
*
* @param string $name
*
* @return $this
*/
public function delete($name)
{
$this->config->removeSection($name);
return $this;
}
/**
* Create and add elements to this form
*
* @param array $formData
*/
public function createElements(array $formData)
{
$instanceNames = $this->getInstanceNames();
if (count($instanceNames) > 1) {
$options = array('none' => $this->translate('None', 'command transport instance association'));
$this->addElement(
'select',
'instance',
array(
'label' => $this->translate('Instance Link'),
'description' => $this->translate(
'The name of the Icinga instance this transport should exclusively transfer commands to.'
),
'multiOptions' => array_merge($options, array_combine($instanceNames, $instanceNames))
)
);
}
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => $this->translate('Transport Name'),
'description' => $this->translate(
'The name of this command transport that is used to differentiate it from others'
),
'validators' => array(
array(
'Regex',
false,
array(
'pattern' => '/^[^\\[\\]:]+$/',
'messages' => array(
'regexNotMatch' => $this->translate(
'The name cannot contain \'[\', \']\' or \':\'.'
)
)
)
)
)
)
);
$transportTypes = array(
LocalCommandFile::TRANSPORT => $this->translate('Local Command File'),
RemoteCommandFile::TRANSPORT => $this->translate('Remote Command File')
);
$transportType = isset($formData['transport']) ? $formData['transport'] : null;
if ($transportType === null) {
$transportType = key($transportTypes);
}
$this->addElements(array(
array(
'select',
'transport',
array(
'required' => true,
'autosubmit' => true,
'label' => $this->translate('Transport Type'),
'multiOptions' => $transportTypes
)
)
));
$this->addSubForm($this->getTransportForm($transportType)->create($formData), 'transport_form');
}
/**
* Populate the configuration of the transport to load
*/
public function onRequest()
{
if ($this->transportToLoad) {
$data = $this->config->getSection($this->transportToLoad)->toArray();
$data['name'] = $this->transportToLoad;
$this->populate($data);
}
}
/**
* Retrieve all form element values
*
* @param bool $suppressArrayNotation Ignored
*
* @return array
*/
public function getValues($suppressArrayNotation = false)
{
$values = parent::getValues();
$values = array_merge($values, $values['transport_form']);
unset($values['transport_form']);
return $values;
}
}

View File

@ -1,32 +0,0 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Setup;
use Icinga\Web\Form;
use Icinga\Module\Monitoring\Forms\Config\InstanceConfigForm;
class InstancePage extends Form
{
public function init()
{
$this->setName('setup_monitoring_instance');
$this->setTitle($this->translate('Monitoring Instance', 'setup.page.title'));
$this->addDescription($this->translate(
'Please define the settings specific to your monitoring instance below.'
));
}
public function createElements(array $formData)
{
$instanceConfigForm = new InstanceConfigForm();
$this->addSubForm($instanceConfigForm, 'instance_form');
$instanceConfigForm->create($formData);
$instanceConfigForm->getElement('name')->setValue('icinga');
}
public function getValues($suppressArrayNotation = false)
{
return $this->getSubForm('instance_form')->getValues($suppressArrayNotation);
}
}

View File

@ -0,0 +1,33 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Setup;
use Icinga\Web\Form;
use Icinga\Module\Monitoring\Forms\Config\TransportConfigForm;
class TransportPage extends Form
{
public function init()
{
$this->setName('setup_command_transport');
$this->setTitle($this->translate('Command Transport', 'setup.page.title'));
$this->addDescription($this->translate(
'Please define below how you want to send commands to your monitoring instance.'
));
}
public function createElements(array $formData)
{
$transportConfigForm = new TransportConfigForm();
$this->addSubForm($transportConfigForm, 'transport_form');
$transportConfigForm->create($formData);
$transportConfigForm->removeElement('instance');
$transportConfigForm->getElement('name')->setValue('icinga2');
}
public function getValues($suppressArrayNotation = false)
{
return $this->getSubForm('transport_form')->getValues($suppressArrayNotation);
}
}

View File

@ -47,28 +47,28 @@
<?php endforeach; ?>
</tbody>
</table>
<h1><?= $this->translate('Monitoring Instances') ?></h1>
<h1><?= $this->translate('Command Transports') ?></h1>
<p>
<a href="<?= $this->href('/monitoring/config/createinstance'); ?>">
<?= $this->icon('plus'); ?> <?= $this->translate('Create New Instance'); ?>
<a href="<?= $this->href('/monitoring/config/createtransport'); ?>">
<?= $this->icon('plus'); ?> <?= $this->translate('Create New Transport'); ?>
</a>
</p>
<table class="action alternating">
<thead>
<th><?= $this->translate('Instance'); ?></th>
<th><?= $this->translate('Transport'); ?></th>
<th style="width: 5em"><?= $this->translate('Remove'); ?></th>
</thead>
<tbody>
<?php foreach ($this->instancesConfig as $instanceName => $config): ?>
<?php foreach ($this->transportConfig as $transportName => $config): ?>
<tr>
<td>
<?= $this->qlink(
$instanceName,
'/monitoring/config/editinstance',
array('instance' => $instanceName),
$transportName,
'/monitoring/config/edittransport',
array('transport' => $transportName),
array(
'icon' => 'edit',
'title' => sprintf($this->translate('Edit monitoring instance %s'), $instanceName)
'title' => sprintf($this->translate('Edit command transport %s'), $transportName)
)
); ?>
<small>(<?= sprintf(
@ -79,11 +79,11 @@
<td>
<?= $this->qlink(
'',
'/monitoring/config/removeinstance',
array('instance' => $instanceName),
'/monitoring/config/removetransport',
array('transport' => $transportName),
array(
'icon' => 'trash',
'title' => sprintf($this->translate('Remove monitoring instance %s'), $instanceName)
'title' => sprintf($this->translate('Remove command transport %s'), $transportName)
)
); ?>
</td>

View File

@ -0,0 +1,6 @@
<div class="controls">
<?= $tabs; ?>
</div>
<div class="content">
<?= $form; ?>
</div>

View File

@ -0,0 +1,96 @@
# <a id="commandtransports"></a> The commandtransports.ini configuration file
## Abstract
The commandtransports.ini defines how Icinga Web 2 accesses the command pipe of
your Icinga instance in order to submit external commands. Depending on the
config path (default: /etc/icingaweb2) of your Icinga Web 2 installation you can
find it under ./modules/monitoring/commandtransports.ini.
## Syntax
You can define multiple command transports in the commandtransports.ini. Every
transport starts with a section header containing its name, followed by the
config directives for this transport in the standard INI-format.
Icinga Web 2 will try one transport after another to send a command, depending
on the respective Icinga instance, until the command is successfully sent. The
order in which Icinga Web 2 processes the configured transports is defined by
the order of sections in the commandtransports.ini.
## Using a local command pipe
A local Icinga instance requires the following directives:
````
[icinga2]
transport = local
path = /var/run/icinga2/cmd/icinga2.cmd
````
When sending commands to the Icinga instance, Icinga Web 2 opens the file found
on the local filesystem underneath 'path' and writes the external command to it.
## Using SSH for accessing a remote command pipe
A command pipe on a remote host's filesystem can be accessed by configuring a
SSH based command transport and requires the following directives:
````
[icinga2]
transport = remote
path = /var/run/icinga2/cmd/icinga2.cmd
host = example.tld
;port = 22 ; Optional. The default is 22
user = icinga
````
To make this example work, you'll need to permit your web-server's user
public-key based access to the defined remote host so that Icinga Web 2 can
connect to it and login as the defined user.
You can also make use of a dedicated SSH resource to permit access for a
different user than the web-server's one. This way, you can provide a private
key file on the local filesystem that is used to access the remote host.
To accomplish this, a new resource is required that is defined in your
transport's configuration instead of a user:
````
[icinga2]
transport = remote
path = /var/run/icinga2/cmd/icinga2.cmd
host = example.tld
;port = 22 ; Optional. The default is 22
resource = example.tld-icinga2
````
The resource's configuration needs to be put into the resources.ini file:
````
[example.tld-icinga2]
type = ssh
user = icinga
private_key = /etc/icingaweb2/ssh/icinga
````
## Configuring transports for different Icinga instances
If there are multiple but different Icinga instances writing to your IDO you can
define which transport belongs to which Icinga instance by providing the
directive 'instance'. This directive should contain the name of the Icinga
instance you want to assign to the transport:
````
[icinga1]
...
instance = icinga1
[icinga2]
...
instance = icinga2
````
Associating a transport to a specific Icinga instance causes this transport to
be used to send commands to the linked instance only. Transports without a
linked Icinga instance are utilized to send commands to all instances.

View File

@ -10,7 +10,7 @@ stored in `/etc/icingaweb2` by default (depending on your config setup).
modules/monitoring | Directory | `monitoring` module specific configuration
modules/monitoring | config.ini | Security settings (e.g. protected custom vars) for the `monitoring` module
modules/monitoring | backends.ini | Backend type and resources (e.g. Icinga IDO DB)
modules/monitoring | [instances.ini](instances.md#instances) | Instances and their transport (e.g. local external command pipe)
modules/monitoring | [commandtransports.ini](commandtransports.md#commandtransports) | Command transports for specific Icinga instances

View File

@ -1,55 +0,0 @@
# <a id="instances"></a> The instance.ini configuration file
## Abstract
The instance.ini defines how icingaweb accesses the command pipe of your icinga process in order to submit external
commands. Depending on the config path (default: /etc/icingaweb2) of your icingaweb installation you can find it
under ./modules/monitoring/instances.ini.
## Syntax
You can define multiple instances in the instances.ini, icingaweb will use the first one as the default instance.
Every instance starts with a section header containing the name of the instance, followed by the config directives for
this instance in the standard ini format used by icingaweb.
## Using a local icinga pipe
A local icinga instance can be easily setup and only requires the 'path' parameter:
[icinga]
path=/usr/local/icinga/var/rw/icinga.cmd
When sending commands to the icinga instance, icingaweb just opens the file found underneath 'path' and writes the external
command to it.
## Using ssh for accessing an icinga pipe
When providing at least a host directive to the instances.ini, SSH will be used for accessing the pipe. You must have
setup key authentication at the endpoint and allow your icingweb's user to access the machine without a password at this time:
[icinga]
path=/usr/local/icinga/var/rw/icinga.cmd ; the path on the remote machine where the icinga.cmd can be found
host=my.remote.machine.com ; the hostname or address of the remote machine
port=22 ; the port to use (22 if none is given)
user=jdoe ; the user to authenticate with
You can also make use of the ssh resource for accessing an icinga pipe with key-based authentication, which will give
you the possibility to define the location of the private key for a specific user, let's have a look:
[icinga]
path=/usr/local/icinga/var/rw/icinga.cmd ; the path on the remote machine where the icinga.cmd can be found
host=my.remote.machine.com ; the hostname or address of the remote machine
port=22 ; the port to use (22 if none is given)
resource=ssh ; the ssh resource which contains the username and the location of the private key
And the associated ssh resource:
[ssh]
type = "ssh"
user = "ssh-user"
private_key = "/etc/icingaweb2/ssh/ssh-user"

View File

@ -4,15 +4,19 @@
namespace Icinga\Module\Monitoring\Command\Transport;
use Icinga\Application\Config;
use Icinga\Application\Logger;
use Icinga\Data\ConfigObject;
use Icinga\Exception\ConfigurationError;
use Icinga\Module\Monitoring\Command\IcingaCommand;
use Icinga\Module\Monitoring\Command\Object\ObjectCommand;
use Icinga\Module\Monitoring\Exception\CommandTransportException;
/**
* Command transport factory
* Command transport
*
* This class is subject to change as we do not have environments yet (#4471).
*/
abstract class CommandTransport
class CommandTransport implements CommandTransportInterface
{
/**
* Transport configuration
@ -24,21 +28,25 @@ abstract class CommandTransport
/**
* Get transport configuration
*
* @return Config
* @throws ConfigurationError
* @return Config
*
* @throws ConfigurationError
*/
public static function getConfig()
{
if (! isset(self::$config)) {
self::$config = Config::module('monitoring', 'instances');
if (self::$config->isEmpty()) {
if (static::$config === null) {
$config = Config::module('monitoring', 'commandtransports');
if ($config->isEmpty()) {
throw new ConfigurationError(
'No instances have been configured in \'%s\'.',
self::$config->getConfigFile()
mt('monitoring', 'No command transports have been configured in "%s".'),
$config->getConfigFile()
);
}
static::$config = $config;
}
return self::$config;
return static::$config;
}
/**
@ -47,9 +55,10 @@ abstract class CommandTransport
* @param ConfigObject $config
*
* @return LocalCommandFile|RemoteCommandFile
*
* @throws ConfigurationError
*/
public static function fromConfig(ConfigObject $config)
public static function createTransport(ConfigObject $config)
{
$config = clone $config;
switch (strtolower($config->transport)) {
@ -62,14 +71,18 @@ abstract class CommandTransport
break;
default:
throw new ConfigurationError(
'Can\'t create command transport \'%s\'. Invalid transport defined in \'%s\'.'
. ' Use one of \'%s\' or \'%s\'.',
mt(
'monitoring',
'Cannot create command transport "%s". Invalid transport'
. ' defined in "%s". Use one of "%s" or "%s".'
),
$config->transport,
self::$config->getConfigFile(),
static::getConfig()->getConfigFile(),
LocalCommandFile::TRANSPORT,
RemoteCommandFile::TRANSPORT
);
}
unset($config->transport);
foreach ($config as $key => $value) {
$method = 'set' . ucfirst($key);
@ -79,37 +92,80 @@ abstract class CommandTransport
// when being about to send a command
continue;
}
$transport->$method($value);
}
return $transport;
}
/**
* Create a transport by name
* Send the given command over an appropriate Icinga command transport
*
* @param string $name
* This will try one configured transport after another until the command has been successfully sent.
*
* @return LocalCommandFile|RemoteCommandFile
* @throws ConfigurationError
* @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 static function create($name)
public function send(IcingaCommand $command, $now = null)
{
$config = self::getConfig()->getSection($name);
if ($config->isEmpty()) {
throw new ConfigurationError();
$tries = 0;
foreach (static::getConfig() as $transportConfig) {
$transport = static::createTransport($transportConfig);
if ($this->transferPossible($command, $transport)) {
try {
$transport->send($command, $now);
} catch (CommandTransportException $e) {
Logger::error($e);
$tries += 1;
continue; // Try the next transport
}
return; // The command was successfully sent
}
}
return self::fromConfig($config);
if ($tries > 0) {
throw new CommandTransportException(
mt(
'monitoring',
'Failed to send external Icinga command. None of the configured transports'
. ' was able to transfer the command. Please see the log for more details.'
)
);
}
throw new CommandTransportException(
mt(
'monitoring',
'Failed to send external Icinga command. No transport has been configured'
. ' for this instance. Please contact your Icinga Web administrator.'
)
);
}
/**
* Create a transport by the first section of the configuration
* Return whether it is possible to send the given command using the given transport
*
* @return LocalCommandFile|RemoteCommandFile
* @param IcingaCommand $command
* @param CommandTransportInterface $transport
*
* @return bool
*/
public static function first()
protected function transferPossible($command, $transport)
{
$config = self::getConfig();
$config->rewind();
return self::fromConfig($config->current());
if (! method_exists($transport, 'getInstance') || !$command instanceof ObjectCommand) {
return true;
}
$transportInstance = $transport->getInstance();
if (! $transportInstance || $transportInstance === 'none') {
return true;
}
return strtolower($transportInstance) === strtolower($command->getObject()->instance_name);
}
}

View File

@ -22,6 +22,13 @@ class LocalCommandFile implements CommandTransportInterface
*/
const TRANSPORT = 'local';
/**
* The name of the Icinga instance this transport will transfer commands to
*
* @var string
*/
protected $instanceName;
/**
* Path to the icinga command file
*
@ -51,6 +58,29 @@ class LocalCommandFile implements CommandTransportInterface
$this->renderer = new IcingaCommandFileCommandRenderer();
}
/**
* Set the name of the Icinga instance this transport will transfer commands to
*
* @param string $name
*
* @return $this
*/
public function setInstance($name)
{
$this->instanceName = $name;
return $this;
}
/**
* Return the name of the Icinga instance this transport will transfer commands to
*
* @return string
*/
public function getInstance()
{
return $this->instanceName;
}
/**
* Set the path to the local Icinga command file
*

View File

@ -22,6 +22,13 @@ class RemoteCommandFile implements CommandTransportInterface
*/
const TRANSPORT = 'remote';
/**
* The name of the Icinga instance this transport will transfer commands to
*
* @var string
*/
protected $instanceName;
/**
* Remote host
*
@ -74,6 +81,29 @@ class RemoteCommandFile implements CommandTransportInterface
$this->renderer = new IcingaCommandFileCommandRenderer();
}
/**
* Set the name of the Icinga instance this transport will transfer commands to
*
* @param string $name
*
* @return $this
*/
public function setInstance($name)
{
$this->instanceName = $name;
return $this;
}
/**
* Return the name of the Icinga instance this transport will transfer commands to
*
* @return string
*/
public function getInstance()
{
return $this->instanceName;
}
/**
* Set the remote host
*

View File

@ -12,8 +12,8 @@ use Icinga\Module\Setup\RequirementSet;
use Icinga\Module\Setup\Forms\SummaryPage;
use Icinga\Module\Monitoring\Forms\Setup\WelcomePage;
use Icinga\Module\Monitoring\Forms\Setup\BackendPage;
use Icinga\Module\Monitoring\Forms\Setup\InstancePage;
use Icinga\Module\Monitoring\Forms\Setup\SecurityPage;
use Icinga\Module\Monitoring\Forms\Setup\TransportPage;
use Icinga\Module\Monitoring\Forms\Setup\IdoResourcePage;
use Icinga\Module\Monitoring\Forms\Setup\LivestatusResourcePage;
use Icinga\Module\Setup\Requirement\ClassRequirement;
@ -33,7 +33,7 @@ class MonitoringWizard extends Wizard implements SetupWizard
$this->addPage(new BackendPage());
$this->addPage(new IdoResourcePage());
$this->addPage(new LivestatusResourcePage());
$this->addPage(new InstancePage());
$this->addPage(new TransportPage());
$this->addPage(new SecurityPage());
$this->addPage(new SummaryPage(array('name' => 'setup_monitoring_summary')));
}
@ -150,8 +150,8 @@ class MonitoringWizard extends Wizard implements SetupWizard
);
$setup->addStep(
new InstanceStep(array(
'instanceConfig' => $pageData['setup_monitoring_instance']
new TransportStep(array(
'transportConfig' => $pageData['setup_command_transport']
))
);

View File

@ -8,7 +8,7 @@ use Icinga\Module\Setup\Step;
use Icinga\Application\Config;
use Icinga\Exception\IcingaException;
class InstanceStep extends Step
class TransportStep extends Step
{
protected $data;
@ -21,13 +21,13 @@ class InstanceStep extends Step
public function apply()
{
$instanceConfig = $this->data['instanceConfig'];
$instanceName = $instanceConfig['name'];
unset($instanceConfig['name']);
$transportConfig = $this->data['transportConfig'];
$transportName = $transportConfig['name'];
unset($transportConfig['name']);
try {
Config::fromArray(array($instanceName => $instanceConfig))
->setConfigFile(Config::resolvePath('modules/monitoring/instances.ini'))
Config::fromArray(array($transportName => $transportConfig))
->setConfigFile(Config::resolvePath('modules/monitoring/commandtransports.ini'))
->saveIni();
} catch (Exception $e) {
$this->error = $e;
@ -40,16 +40,16 @@ class InstanceStep extends Step
public function getSummary()
{
$pageTitle = '<h2>' . mt('monitoring', 'Monitoring Instance', 'setup.page.title') . '</h2>';
$pageTitle = '<h2>' . mt('monitoring', 'Command Transport', 'setup.page.title') . '</h2>';
if (isset($this->data['instanceConfig']['host'])) {
if (isset($this->data['transportConfig']['host'])) {
$pipeHtml = '<p>' . sprintf(
mt(
'monitoring',
'Icinga Web 2 will use the named pipe located on a remote machine at "%s" to send commands'
. ' to your monitoring instance by using the connection details listed below:'
),
$this->data['instanceConfig']['path']
$this->data['transportConfig']['path']
) . '</p>';
$pipeHtml .= ''
@ -57,15 +57,15 @@ class InstanceStep extends Step
. '<tbody>'
. '<tr>'
. '<td><strong>' . mt('monitoring', 'Remote Host') . '</strong></td>'
. '<td>' . $this->data['instanceConfig']['host'] . '</td>'
. '<td>' . $this->data['transportConfig']['host'] . '</td>'
. '</tr>'
. '<tr>'
. '<td><strong>' . mt('monitoring', 'Remote SSH Port') . '</strong></td>'
. '<td>' . $this->data['instanceConfig']['port'] . '</td>'
. '<td>' . $this->data['transportConfig']['port'] . '</td>'
. '</tr>'
. '<tr>'
. '<td><strong>' . mt('monitoring', 'Remote SSH User') . '</strong></td>'
. '<td>' . $this->data['instanceConfig']['user'] . '</td>'
. '<td>' . $this->data['transportConfig']['user'] . '</td>'
. '</tr>'
. '</tbody>'
. '</table>';
@ -76,7 +76,7 @@ class InstanceStep extends Step
'Icinga Web 2 will use the named pipe located at "%s"'
. ' to send commands to your monitoring instance.'
),
$this->data['instanceConfig']['path']
$this->data['transportConfig']['path']
) . '</p>';
}
@ -87,17 +87,17 @@ class InstanceStep extends Step
{
if ($this->error === false) {
return array(sprintf(
mt('monitoring', 'Monitoring instance configuration has been successfully created: %s'),
Config::resolvePath('modules/monitoring/instances.ini')
mt('monitoring', 'Command transport configuration has been successfully created: %s'),
Config::resolvePath('modules/monitoring/commandtransports.ini')
));
} elseif ($this->error !== null) {
return array(
sprintf(
mt(
'monitoring',
'Monitoring instance configuration could not be written to: %s. An error occured:'
'Command transport configuration could not be written to: %s. An error occured:'
),
Config::resolvePath('modules/monitoring/instances.ini')
Config::resolvePath('modules/monitoring/commandtransports.ini')
),
sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->error))
);

View File

@ -89,7 +89,7 @@ By default the Icinga 2 DB IDO is used by the monitoring module in
The external command pipe is required for sending commands
and configured for Icinga 2 in
`/etc/icingaweb2/modules/monitoring/instances.ini`
`/etc/icingaweb2/modules/monitoring/commandtransports.ini`
#### Authentication configuration