mirror of
https://github.com/Icinga/icingaweb2-module-director.git
synced 2025-04-08 17:15:10 +02:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1c1f5e306e | ||
|
e49cccd13c | ||
|
087ce05ebe | ||
|
babf42c764 | ||
|
05dfef5ccc | ||
|
19df9eef8c | ||
|
2527541326 | ||
|
cda5f56038 | ||
|
9de28e7c79 | ||
|
62a684b928 | ||
|
98b07b6dbe | ||
|
367fb811b1 | ||
|
d87057e43d | ||
|
3a2f019e18 | ||
|
575c8fad4e | ||
|
b80fdc9c37 | ||
|
032b043e3a | ||
|
ea2c4fdaf7 |
@ -3,7 +3,8 @@
|
||||
namespace Icinga\Module\Director\Controllers;
|
||||
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Application\Modules\Manager;
|
||||
use Icinga\Module\Director\Application\DependencyChecker;
|
||||
use Icinga\Module\Director\Web\Table\Dependency\DependencyInfoTable;
|
||||
use Icinga\Web\Controller;
|
||||
|
||||
class PhperrorController extends Controller
|
||||
@ -24,39 +25,19 @@ class PhperrorController extends Controller
|
||||
|
||||
public function dependenciesAction()
|
||||
{
|
||||
$dependencies = $this->view->dependencies = $this->Module()->getDependencies();
|
||||
$modules = $this->view->modules = Icinga::app()->getModuleManager();
|
||||
// Hint: we're duplicating some code here
|
||||
$satisfied = true;
|
||||
foreach ($dependencies as $module => $required) {
|
||||
/** @var Manager $this ->modules */
|
||||
if ($modules->hasEnabled($module)) {
|
||||
$installed = $modules->getModule($module, false)->getVersion();
|
||||
$installed = \ltrim($installed, 'v'); // v0.6.0 VS 0.6.0
|
||||
if (\preg_match('/^([<>=]+)\s*v?(\d+\.\d+\.\d+)$/', $required, $match)) {
|
||||
$operator = $match[1];
|
||||
$vRequired = $match[2];
|
||||
if (\version_compare($installed, $vRequired, $operator)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
$satisfied = false;
|
||||
}
|
||||
|
||||
if ($satisfied) {
|
||||
$checker = new DependencyChecker(Icinga::app());
|
||||
if ($checker->satisfiesDependencies($this->Module())) {
|
||||
$this->redirectNow('director');
|
||||
}
|
||||
|
||||
$this->setAutorefreshInterval(15);
|
||||
$this->getTabs()->add('error', array(
|
||||
$this->getTabs()->add('error', [
|
||||
'label' => $this->translate('Error'),
|
||||
'url' => $this->getRequest()->getUrl()
|
||||
))->activate('error');
|
||||
$msg = $this->translate(
|
||||
])->activate('error');
|
||||
$this->view->title = $this->translate('Unsatisfied dependencies');
|
||||
$this->view->table = (new DependencyInfoTable($checker, $this->Module()))->render();
|
||||
$this->view->message = $this->translate(
|
||||
"Icinga Director depends on the following modules, please install/upgrade as required"
|
||||
);
|
||||
$this->view->title = $this->translate('Unsatisfied dependencies');
|
||||
$this->view->message = sprintf($msg, PHP_VERSION);
|
||||
}
|
||||
}
|
||||
|
@ -136,12 +136,7 @@ class SelfServiceController extends ActionController
|
||||
throw new NotFoundError('The host "%s" is not an agent', $name);
|
||||
}
|
||||
|
||||
$this->sendPowerShellResponse(
|
||||
Util::getIcingaTicket(
|
||||
$name,
|
||||
$this->api()->getTicketSalt()
|
||||
)
|
||||
);
|
||||
$this->sendPowerShellResponse($this->api()->getTicket($name));
|
||||
} catch (Exception $e) {
|
||||
if ($e instanceof NotFoundError) {
|
||||
$this->sendPowerShellError($e->getMessage(), 404);
|
||||
|
@ -230,12 +230,14 @@ class IcingaHostForm extends DirectorObjectForm
|
||||
$links->addAttributes(['class' => 'strike-links']);
|
||||
/** @var BaseHtmlElement $link */
|
||||
foreach ($links->getContent() as $link) {
|
||||
$link->addAttributes([
|
||||
'title' => $this->translate(
|
||||
'Group has been inherited, but will be overridden'
|
||||
. ' by locally assigned group(s)'
|
||||
)
|
||||
]);
|
||||
if ($link instanceof BaseHtmlElement) {
|
||||
$link->addAttributes([
|
||||
'title' => $this->translate(
|
||||
'Group has been inherited, but will be overridden'
|
||||
. ' by locally assigned group(s)'
|
||||
)
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->addElement('simpleNote', 'inherited_groups', [
|
||||
|
@ -139,6 +139,14 @@ class IcingaServiceForm extends DirectorObjectForm
|
||||
} else {
|
||||
$this->addOverrideHint();
|
||||
$group = $this->getDisplayGroup('custom_fields');
|
||||
if (! $group) {
|
||||
foreach ($this->getDisplayGroups() as $groupName => $eventualGroup) {
|
||||
if (preg_match('/^custom_fields:/', $groupName)) {
|
||||
$group = $eventualGroup;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($group) {
|
||||
$elements = $group->getElements();
|
||||
$group->setElements([$this->getElement('inheritance_hint')]);
|
||||
|
@ -10,62 +10,5 @@ use Icinga\Application\Modules\Manager;
|
||||
|
||||
<div class="content">
|
||||
<p class="legacy-error"><?= $this->escape($this->message) ?></p>
|
||||
<table class="common-table table-row-selectable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?= $this->translate('Module name') ?></th>
|
||||
<th><?= $this->translate('Required') ?></th>
|
||||
<th><?= $this->translate('Installed') ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-base-target="_next">
|
||||
<?php
|
||||
|
||||
foreach ($this->dependencies as $module => $required) {
|
||||
/** @var Manager $this->modules */
|
||||
if ($modules->hasEnabled($module)) {
|
||||
$installed = $modules->getModule($module, false)->getVersion();
|
||||
$installed = \ltrim($installed, 'v'); // v0.6.0 VS 0.6.0
|
||||
if (\preg_match('/^([<>=]+)\s*v?(\d+\.\d+\.\d+)$/', $required, $match)) {
|
||||
$operator = $match[1];
|
||||
$vRequired = $match[2];
|
||||
if (\version_compare($installed, $vRequired, $operator)) {
|
||||
$icon = 'ok';
|
||||
} else {
|
||||
$icon = 'cancel';
|
||||
}
|
||||
} else {
|
||||
$icon = 'cancel';
|
||||
}
|
||||
$link = $this->qlink(
|
||||
$module,
|
||||
'config/module',
|
||||
['name' => $module],
|
||||
['class' => "icon-$icon"]
|
||||
);
|
||||
} elseif ($modules->hasInstalled($module)) {
|
||||
$installed = $this->translate('disabled');
|
||||
$link = $this->qlink($module, 'config/module', ['name' => $module], ['class' => 'icon-cancel']);
|
||||
} else {
|
||||
$installed = $this->translate('missing');
|
||||
$link = sprintf(
|
||||
'<a href="#" class="icon-cancel">%s</a> (<a href="https://github.com/Icinga/icingaweb2-module-%s"'
|
||||
. ' target="_blank" rel="noreferrer">%s</a>)',
|
||||
$this->escape($module),
|
||||
$this->escape($module),
|
||||
$this->translate('more')
|
||||
);
|
||||
}
|
||||
|
||||
\printf(
|
||||
'<tr><td>%s</a></td><td>%s</td><td>%s</td></tr>',
|
||||
$link,
|
||||
$this->escape($required),
|
||||
$this->escape($installed)
|
||||
);
|
||||
}
|
||||
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?= $this->table ?>
|
||||
</div>
|
||||
|
@ -20,7 +20,7 @@ Requirements
|
||||
might show smaller UI bugs and are not actively tested
|
||||
* The following Icinga modules must be installed and enabled:
|
||||
* [ipl](https://github.com/Icinga/icingaweb2-module-ipl) (>=0.3.0)
|
||||
* [incubator](https://github.com/Icinga/icingaweb2-module-incubator) (>=0.5.0)
|
||||
* [incubator](https://github.com/Icinga/icingaweb2-module-incubator) (>=0.21.0)
|
||||
* [reactbundle](https://github.com/Icinga/icingaweb2-module-reactbundle) (>=0.7.0)
|
||||
* A database, MySQL (>= 5.1) or PostgreSQL (>= 9.1). MariaDB and other
|
||||
MySQL forks are also fine. Mentioned versions are the required minimum,
|
||||
@ -102,7 +102,7 @@ You might want to use a script as follows for this task:
|
||||
ICINGAWEB_MODULEPATH="/usr/share/icingaweb2/modules"
|
||||
REPO_URL="https://github.com/icinga/icingaweb2-module-director"
|
||||
TARGET_DIR="${ICINGAWEB_MODULEPATH}/director"
|
||||
MODULE_VERSION="1.8.0"
|
||||
MODULE_VERSION="1.8.2"
|
||||
URL="${REPO_URL}/archive/v${MODULE_VERSION}.tar.gz"
|
||||
install -d -m 0755 "${TARGET_DIR}"
|
||||
wget -q -O - "$URL" | tar xfz - -C "${TARGET_DIR}" --strip-components 1
|
||||
@ -119,7 +119,7 @@ It will be immediately ready for use:
|
||||
ICINGAWEB_MODULEPATH="/usr/share/icingaweb2/modules"
|
||||
REPO_URL="https://github.com/icinga/icingaweb2-module-director"
|
||||
TARGET_DIR="${ICINGAWEB_MODULEPATH}/director"
|
||||
MODULE_VERSION="1.8.0"
|
||||
MODULE_VERSION="1.8.2"
|
||||
git clone "${REPO_URL}" "${TARGET_DIR}" --branch v${MODULE_VERSION}
|
||||
|
||||
You can now directly use our current GIT master or check out a specific version.
|
||||
|
@ -43,7 +43,7 @@ pending database migrations to an imported old database snapshot.
|
||||
-------------------------------------------------
|
||||
|
||||
Before upgrading, please upgrade the [incubator module](https://github.com/Icinga/icingaweb2-module-incubator)
|
||||
to (at least) v0.6.0. As always, you'll be prompted to apply pending Database
|
||||
to (at least) v0.21.0. As always, you'll be prompted to apply pending Database
|
||||
Migrations.
|
||||
|
||||
<a name="upgrade-to-1.7.x"></a>Upgrading to 1.7.x
|
||||
|
@ -4,6 +4,45 @@
|
||||
Please make sure to always read our [Upgrading](05-Upgrading.md) documentation
|
||||
before switching to a new version.
|
||||
|
||||
1.8.2
|
||||
-----
|
||||
|
||||
### UI
|
||||
* FIX: The activity log now avoids a bug in PHP introduced with version 8.1.25 (#2828)
|
||||
|
||||
1.8.1
|
||||
-----
|
||||
|
||||
### Fixed issues
|
||||
* You can find issues and feature requests related to this release on our
|
||||
[roadmap](https://github.com/Icinga/icingaweb2-module-director/milestone/24?closed=1)
|
||||
|
||||
### User Interface
|
||||
* FIX: show Override button when all Fields belong to Field Categories (#2303)
|
||||
* FIX: don't fail when showing a Host overriding multiple inherited groups (#2253)
|
||||
* FIX: deal with inherited values which are invalid for a select box (#2288)
|
||||
* FIX: Service Set preview inline Service Template links (#2334)
|
||||
* FIX: show Services applied with Rules involving applied Hostgroups (#2313)
|
||||
* FIX: show "deactivated" services as such also for read-only users (#2344)
|
||||
* FIX: Overrides for Services belonging to Sets on root Host Templates (#2333)
|
||||
* FIX: show no header tabs for search result in web 2.8+ (#2141)
|
||||
* FIX: show and link dependencies for web 2.9+ (#2354)
|
||||
|
||||
### Icinga Configuration
|
||||
* FIX: rare race condition, where generated config might miss some files (#2351)
|
||||
|
||||
### Icinga API
|
||||
* FIX: use Icinga 2's generate-ticket API, required for v2.13.0 (#2348)
|
||||
|
||||
### Import and Sync
|
||||
* FIX: Purge didn't remove more than 1000 services at once (#2339)
|
||||
|
||||
### Automation, User Interface
|
||||
* FIX: error message wording on failing related (or parent) object ref (#2224)
|
||||
|
||||
### REST API
|
||||
* FIX: creating scheduled downtime via api failed (#1879)
|
||||
|
||||
1.8.0
|
||||
-----
|
||||
|
||||
@ -87,6 +126,7 @@ before switching to a new version.
|
||||
### Icinga Agent handling
|
||||
* FIX: Linux Agent installer now fails when unable to retrieve a certificate
|
||||
* FEATURE: Linux Agent installer now supports Alpine Linux (#2216)
|
||||
* FEATURE: Icinga for Windows support (#2147)
|
||||
|
||||
### REST API
|
||||
* FEATURE: Self Service API ignores empty/missing properties (e.g. no address)
|
||||
|
113
library/Director/Application/Dependency.php
Normal file
113
library/Director/Application/Dependency.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Application;
|
||||
|
||||
class Dependency
|
||||
{
|
||||
/** @var string */
|
||||
protected $name;
|
||||
|
||||
/** @var string|null */
|
||||
protected $installedVersion;
|
||||
|
||||
/** @var bool|null */
|
||||
protected $enabled;
|
||||
|
||||
/** @var string */
|
||||
protected $operator;
|
||||
|
||||
/** @var string */
|
||||
protected $requiredVersion;
|
||||
|
||||
/** @var string */
|
||||
protected $requirement;
|
||||
|
||||
/**
|
||||
* Dependency constructor.
|
||||
* @param string $name Usually a module name
|
||||
* @param string $requirement e.g. >=1.7.0
|
||||
* @param string $installedVersion
|
||||
* @param bool $enabled
|
||||
*/
|
||||
public function __construct($name, $requirement, $installedVersion = null, $enabled = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->setRequirement($requirement);
|
||||
if ($installedVersion !== null) {
|
||||
$this->setInstalledVersion($installedVersion);
|
||||
}
|
||||
if ($enabled !== null) {
|
||||
$this->setEnabled($enabled);
|
||||
}
|
||||
}
|
||||
|
||||
public function setRequirement($requirement)
|
||||
{
|
||||
if (preg_match('/^([<>=]+)\s*v?(\d+\.\d+\.\d+)$/', $requirement, $match)) {
|
||||
$this->operator = $match[1];
|
||||
$this->requiredVersion = $match[2];
|
||||
$this->requirement = $requirement;
|
||||
} else {
|
||||
throw new \InvalidArgumentException("'$requirement' is not a valid version constraint");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isInstalled()
|
||||
{
|
||||
return $this->installedVersion !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getInstalledVersion()
|
||||
{
|
||||
return $this->installedVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $version
|
||||
*/
|
||||
public function setInstalledVersion($version)
|
||||
{
|
||||
$this->installedVersion = ltrim($version, 'v'); // v0.6.0 VS 0.6.0
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isEnabled()
|
||||
{
|
||||
return $this->enabled === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $enabled
|
||||
*/
|
||||
public function setEnabled($enabled = true)
|
||||
{
|
||||
$this->enabled = $enabled;
|
||||
}
|
||||
|
||||
public function isSatisfied()
|
||||
{
|
||||
if (! $this->isInstalled() || ! $this->isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return version_compare($this->installedVersion, $this->requiredVersion, $this->operator);
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getRequirement()
|
||||
{
|
||||
return $this->requirement;
|
||||
}
|
||||
}
|
73
library/Director/Application/DependencyChecker.php
Normal file
73
library/Director/Application/DependencyChecker.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Application;
|
||||
|
||||
use Icinga\Application\ApplicationBootstrap;
|
||||
use Icinga\Application\Modules\Module;
|
||||
use Icinga\Application\Version;
|
||||
|
||||
class DependencyChecker
|
||||
{
|
||||
/** @var ApplicationBootstrap */
|
||||
protected $app;
|
||||
|
||||
/** @var \Icinga\Application\Modules\Manager */
|
||||
protected $modules;
|
||||
|
||||
public function __construct(ApplicationBootstrap $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->modules = $app->getModuleManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Module $module
|
||||
* @return Dependency[]
|
||||
*/
|
||||
public function getDependencies(Module $module)
|
||||
{
|
||||
$dependencies = [];
|
||||
$isV290 = version_compare(Version::VERSION, '2.9.0', '>=');
|
||||
foreach ($module->getDependencies() as $moduleName => $required) {
|
||||
if ($isV290 && in_array($moduleName, ['ipl', 'reactbundle'], true)) {
|
||||
continue;
|
||||
}
|
||||
$dependency = new Dependency($moduleName, $required);
|
||||
$dependency->setEnabled($this->modules->hasEnabled($moduleName));
|
||||
if ($this->modules->hasInstalled($moduleName)) {
|
||||
$dependency->setInstalledVersion($this->modules->getModule($moduleName, false)->getVersion());
|
||||
}
|
||||
$dependencies[] = $dependency;
|
||||
}
|
||||
if ($isV290) {
|
||||
$libs = $this->app->getLibraries();
|
||||
foreach ($module->getRequiredLibraries() as $libraryName => $required) {
|
||||
$dependency = new Dependency($libraryName, $required);
|
||||
if ($libs->has($libraryName)) {
|
||||
$dependency->setInstalledVersion($libs->get($libraryName)->getVersion());
|
||||
$dependency->setEnabled();
|
||||
}
|
||||
$dependencies[] = $dependency;
|
||||
}
|
||||
}
|
||||
|
||||
return $dependencies;
|
||||
}
|
||||
|
||||
// if (version_compare(Version::VERSION, '2.9.0', 'ge')) {
|
||||
// }
|
||||
/**
|
||||
* @param Module $module
|
||||
* @return bool
|
||||
*/
|
||||
public function satisfiesDependencies(Module $module)
|
||||
{
|
||||
foreach ($this->getDependencies($module) as $dependency) {
|
||||
if (! $dependency->isSatisfied()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -123,15 +123,38 @@ class CoreApi implements DeploymentApiInterface
|
||||
return $res[$name];
|
||||
}
|
||||
|
||||
public function getTicketSalt()
|
||||
/**
|
||||
* Get a PKI ticket for CSR auto-signing
|
||||
*
|
||||
* @param string $cn The host’s common name for which the ticket should be generated
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getTicket($cn)
|
||||
{
|
||||
// TODO: api must not be the name!
|
||||
$api = $this->getObject('api', 'ApiListeners', array('ticket_salt'));
|
||||
if (isset($api->attrs->ticket_salt)) {
|
||||
return $api->attrs->ticket_salt;
|
||||
$r = $this->client()->post(
|
||||
'actions/generate-ticket',
|
||||
['cn' => $cn]
|
||||
);
|
||||
if (! $r->succeeded()) {
|
||||
throw new RuntimeException($r->getErrorMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
$ticket = $r->getRaw('ticket');
|
||||
if ($ticket === null) {
|
||||
// RestApiResponse::succeeded() returns true if Icinga 2 reports an error in the results key, e.g.
|
||||
// {
|
||||
// "results": [
|
||||
// {
|
||||
// "code": 500.0,
|
||||
// "status": "Ticket salt is not configured in ApiListener object"
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
throw new RuntimeException($r->getRaw('status', 'Ticket is empty'));
|
||||
}
|
||||
|
||||
return $ticket;
|
||||
}
|
||||
|
||||
public function checkHostNow($host)
|
||||
|
@ -8,6 +8,7 @@ use Icinga\Module\Director\Objects\IcingaEndpoint;
|
||||
use Icinga\Module\Director\Objects\IcingaHost;
|
||||
use Icinga\Module\Director\Objects\IcingaZone;
|
||||
use Icinga\Module\Director\Util;
|
||||
use LogicException;
|
||||
|
||||
class AgentWizard
|
||||
{
|
||||
@ -15,12 +16,13 @@ class AgentWizard
|
||||
|
||||
protected $host;
|
||||
|
||||
protected $salt;
|
||||
|
||||
protected $parentZone;
|
||||
|
||||
protected $parentEndpoints;
|
||||
|
||||
/** @var string PKI ticket */
|
||||
protected $ticket;
|
||||
|
||||
public function __construct(IcingaHost $host)
|
||||
{
|
||||
$this->host = $host;
|
||||
@ -96,29 +98,32 @@ class AgentWizard
|
||||
);
|
||||
}
|
||||
|
||||
public function setTicketSalt($salt)
|
||||
{
|
||||
$this->salt = $salt;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the PKI ticket
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws LogicException If ticket has not been set
|
||||
*/
|
||||
protected function getTicket()
|
||||
{
|
||||
return Util::getIcingaTicket(
|
||||
$this->getCertName(),
|
||||
$this->getTicketSalt()
|
||||
);
|
||||
}
|
||||
|
||||
protected function getTicketSalt()
|
||||
{
|
||||
if ($this->salt === null) {
|
||||
throw new ProgrammingError('Requesting salt, but got none');
|
||||
// TODO: No API, not yet. Pass in constructor or throw, still tbd
|
||||
// $this->salt = $this->api()->getTicketSalt();
|
||||
if ($this->ticket === null) {
|
||||
throw new LogicException('Ticket is null');
|
||||
}
|
||||
|
||||
return $this->salt;
|
||||
return $this->ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PKI ticket
|
||||
*
|
||||
* @param string $ticket
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTicket($ticket)
|
||||
{
|
||||
$this->ticket = $ticket;
|
||||
}
|
||||
|
||||
protected function getCertName()
|
||||
|
@ -411,25 +411,31 @@ class IcingaConfig
|
||||
}
|
||||
|
||||
$activity = $this->dbBin($this->getLastActivityChecksum());
|
||||
$this->db->insert(
|
||||
self::$table,
|
||||
array(
|
||||
$this->db->beginTransaction();
|
||||
try {
|
||||
$this->db->insert(self::$table, [
|
||||
'duration' => $this->generationTime,
|
||||
'first_activity_checksum' => $activity,
|
||||
'last_activity_checksum' => $activity,
|
||||
'checksum' => $this->dbBin($this->getChecksum()),
|
||||
)
|
||||
);
|
||||
/** @var IcingaConfigFile $file */
|
||||
foreach ($this->files as $name => $file) {
|
||||
$this->db->insert(
|
||||
'director_generated_config_file',
|
||||
array(
|
||||
]);
|
||||
/** @var IcingaConfigFile $file */
|
||||
foreach ($this->files as $name => $file) {
|
||||
$this->db->insert('director_generated_config_file', [
|
||||
'config_checksum' => $this->dbBin($this->getChecksum()),
|
||||
'file_checksum' => $this->dbBin($file->getChecksum()),
|
||||
'file_path' => $name,
|
||||
)
|
||||
);
|
||||
]);
|
||||
}
|
||||
$this->db->commit();
|
||||
} catch (\Exception $e) {
|
||||
try {
|
||||
$this->db->rollBack();
|
||||
} catch (\Exception $ignored) {
|
||||
// Well...
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
@ -71,14 +71,14 @@ class ImportRunBasedPurgeStrategy extends PurgeStrategy
|
||||
$columns = SyncUtils::getRootVariables(
|
||||
SyncUtils::extractVariableNames($pattern)
|
||||
);
|
||||
|
||||
$resultForCombinedKey = array();
|
||||
foreach (array_chunk($result, 1000) as $keys) {
|
||||
$rows = $runA->fetchRows($columns, null, $keys);
|
||||
$result = array();
|
||||
foreach ($rows as $row) {
|
||||
$result[] = SyncUtils::fillVariables($pattern, $row);
|
||||
$resultForCombinedKey[] = SyncUtils::fillVariables($pattern, $row);
|
||||
}
|
||||
}
|
||||
$result = $resultForCombinedKey;
|
||||
}
|
||||
|
||||
if (empty($result)) {
|
||||
|
@ -535,7 +535,13 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
||||
$this->connection
|
||||
);
|
||||
} catch (NotFoundError $e) {
|
||||
throw new RuntimeException($e->getMessage(), 0, $e);
|
||||
// Hint: eventually a NotFoundError would be better
|
||||
throw new RuntimeException(sprintf(
|
||||
'Unable to load object referenced from %s "%s", %s',
|
||||
$this->getShortTableName(),
|
||||
$this->getObjectName(),
|
||||
lcfirst($e->getMessage())
|
||||
), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
$this->reallySet($name, $object->get('id'));
|
||||
@ -2553,7 +2559,7 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
||||
$type = 'templateChoiceHost';
|
||||
} elseif ($type === 'service_template_choice') {
|
||||
$type = 'TemplateChoiceService';
|
||||
} elseif ($type === 'scheduled_downtime') {
|
||||
} elseif ($type === 'scheduled_downtime' || $type === 'scheduled-downtime') {
|
||||
$type = 'ScheduledDowntime';
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Countable;
|
||||
use Exception;
|
||||
use Icinga\Exception\NotFoundError;
|
||||
use Iterator;
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfigRenderer;
|
||||
@ -251,14 +252,23 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer
|
||||
$connection = $this->object->getConnection();
|
||||
/** @var IcingaObject $class */
|
||||
$class = $this->getImportClass();
|
||||
if (is_array($this->object->getKeyName())) {
|
||||
// Services only
|
||||
$import = $class::load([
|
||||
'object_name' => $name,
|
||||
'object_type' => 'template'
|
||||
], $connection);
|
||||
} else {
|
||||
$import = $class::load($name, $connection);
|
||||
try {
|
||||
if (is_array($this->object->getKeyName())) {
|
||||
// Services only
|
||||
$import = $class::load([
|
||||
'object_name' => $name,
|
||||
'object_type' => 'template'
|
||||
], $connection);
|
||||
} else {
|
||||
$import = $class::load($name, $connection);
|
||||
}
|
||||
} catch (NotFoundError $e) {
|
||||
throw new NotFoundError(sprintf(
|
||||
'Unable to load parent referenced from %s "%s", %s',
|
||||
$this->object->getShortTableName(),
|
||||
$this->object->getObjectName(),
|
||||
lcfirst($e->getMessage())
|
||||
), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return $this->objects[$import->getObjectName()] = $import;
|
||||
|
@ -213,10 +213,26 @@ abstract class ObjectApplyMatches
|
||||
protected function __construct(IcingaObject $object)
|
||||
{
|
||||
$this->object = $object;
|
||||
$this->flatObject = $object->toPlainObject(true, false);
|
||||
$flat = $object->toPlainObject(true, false);
|
||||
// Sure, we are flat - but we might still want to match templates.
|
||||
unset($this->flatObject->imports);
|
||||
$this->flatObject->templates = $object->listFlatResolvedImportNames();
|
||||
static::flattenVars($this->flatObject);
|
||||
unset($flat->imports);
|
||||
$flat->templates = $object->listFlatResolvedImportNames();
|
||||
$this->addAppliedGroupsToFlatObject($flat, $object);
|
||||
static::flattenVars($flat);
|
||||
$this->flatObject = $flat;
|
||||
}
|
||||
|
||||
protected function addAppliedGroupsToFlatObject($flat, IcingaObject $object)
|
||||
{
|
||||
if ($object instanceof IcingaHost) {
|
||||
$appliedGroups = $object->getAppliedGroups();
|
||||
if (! empty($appliedGroups)) {
|
||||
if (isset($flat->groups)) {
|
||||
$flat->groups = array_merge($flat->groups, $appliedGroups);
|
||||
} else {
|
||||
$flat->groups = $appliedGroups;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,11 @@ class OverriddenVarsResolver
|
||||
|
||||
public function resolveFor(IcingaHost $host, IcingaService $service = null)
|
||||
{
|
||||
$overrides = [];
|
||||
$parents = $host->listFlatResolvedImportNames();
|
||||
if (empty($parents)) {
|
||||
return $overrides;
|
||||
}
|
||||
$query = $this->db->select()->from(['hv' => 'icinga_host_var'], [
|
||||
'host_name' => 'h.object_name',
|
||||
'varvalue' => 'hv.varvalue',
|
||||
@ -32,7 +36,6 @@ class OverriddenVarsResolver
|
||||
'h.id = hv.host_id',
|
||||
[]
|
||||
)->where('hv.varname = ?', $this->varName)->where('h.object_name IN (?)', $parents);
|
||||
$overrides = [];
|
||||
foreach ($this->db->fetchAll($query) as $row) {
|
||||
if ($row->varvalue === null) {
|
||||
continue;
|
||||
|
@ -105,12 +105,7 @@ class IcingaObjectHandler extends RequestHandler
|
||||
throw new NotFoundError('The host "%s" is not an agent', $host->getObjectName());
|
||||
}
|
||||
|
||||
$this->sendJson(
|
||||
Util::getIcingaTicket(
|
||||
$host->getObjectName(),
|
||||
$this->api->getTicketSalt()
|
||||
)
|
||||
);
|
||||
$this->sendJson($this->api->getTicket($host->getObjectName()));
|
||||
|
||||
// TODO: find a better way to shut down. Currently, this avoids
|
||||
// "not found" errors:
|
||||
|
@ -81,11 +81,6 @@ class Util
|
||||
return bin2hex(substr($out, 0, $length));
|
||||
}
|
||||
|
||||
public static function getIcingaTicket($certname, $salt)
|
||||
{
|
||||
return self::pbkdf2('sha1', $certname, $salt, 50000, 20);
|
||||
}
|
||||
|
||||
public static function auth()
|
||||
{
|
||||
if (self::$auth === null) {
|
||||
|
@ -216,7 +216,8 @@ abstract class ActionController extends Controller implements ControlsAndContent
|
||||
$viewRenderer = new SimpleViewRenderer();
|
||||
$viewRenderer->replaceZendViewRenderer();
|
||||
$this->view = $viewRenderer->view;
|
||||
if ($this->getOriginalUrl()->getParam('view') === 'compact') {
|
||||
// Hint -> $this->view->compact is the only way since v2.8.0
|
||||
if ($this->view->compact || $this->getOriginalUrl()->getParam('view') === 'compact') {
|
||||
if ($this->view->controls) {
|
||||
$this->controls()->getAttributes()->add('style', 'display: none;');
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ class CsrfToken
|
||||
return false;
|
||||
}
|
||||
|
||||
list($seed, $token) = explode('|', $elementValue);
|
||||
list($seed, $token) = explode('|', $token);
|
||||
|
||||
if (!is_numeric($seed)) {
|
||||
return false;
|
||||
|
@ -629,7 +629,7 @@ abstract class DirectorObjectForm extends DirectorForm
|
||||
if (is_bool($inherited)) {
|
||||
$inherited = $inherited ? 'y' : 'n';
|
||||
}
|
||||
if (array_key_exists($inherited, $multi)) {
|
||||
if (is_scalar($inherited) && array_key_exists($inherited, $multi)) {
|
||||
$multi[null] = $multi[$inherited] . sprintf($txtInherited, $inheritedFrom);
|
||||
} else {
|
||||
$multi[null] = $this->translate($this->translate('- inherited -'));
|
||||
|
@ -619,6 +619,10 @@ abstract class QuickForm extends QuickBaseForm
|
||||
$this->hasBeenSent = true;
|
||||
} elseif ($req->isPost()) {
|
||||
$post = $req->getPost();
|
||||
if (! CsrfToken::isValid($post[self::CSRF])) {
|
||||
throw new Exception('Invalid CSRF token provided');
|
||||
}
|
||||
|
||||
$this->hasBeenSent = array_key_exists(self::ID, $post) &&
|
||||
$post[self::ID] === $this->getName();
|
||||
} else {
|
||||
|
@ -141,9 +141,13 @@ class ObjectPreview
|
||||
return $match[1] . $match[2] . $match[3];
|
||||
}
|
||||
|
||||
$urlObjectType = $this->object->getShortTableName();
|
||||
if ($urlObjectType === 'service_set') {
|
||||
$urlObjectType = 'service';
|
||||
}
|
||||
return $match[1] . Link::create(
|
||||
$match[2],
|
||||
sprintf('director/' . $this->object->getShortTableName()),
|
||||
sprintf("director/$urlObjectType"),
|
||||
['name' => $match[2]]
|
||||
)->render() . $match[3];
|
||||
}
|
||||
|
@ -226,9 +226,9 @@ class SelfService
|
||||
$certname = $host->getObjectName();
|
||||
|
||||
try {
|
||||
$ticket = Util::getIcingaTicket($certname, $this->api->getTicketSalt());
|
||||
$ticket = $this->api->getTicket($host->getObjectName());
|
||||
$wizard = new AgentWizard($host);
|
||||
$wizard->setTicketSalt($this->api->getTicketSalt());
|
||||
$wizard->setTicket($ticket);
|
||||
} catch (Exception $e) {
|
||||
$c->add(Hint::error(sprintf(
|
||||
$this->translate(
|
||||
@ -278,7 +278,7 @@ class SelfService
|
||||
public function handleLegacyAgentDownloads($os)
|
||||
{
|
||||
$wizard = new AgentWizard($this->host);
|
||||
$wizard->setTicketSalt($this->api->getTicketSalt());
|
||||
$wizard->setTicket($this->api->getTicket($this->host->getObjectName()));
|
||||
|
||||
switch ($os) {
|
||||
case 'windows-kickstart':
|
||||
|
@ -5,9 +5,8 @@ namespace Icinga\Module\Director\Web\Table;
|
||||
use Icinga\Module\Director\Util;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use gipfl\IcingaWeb2\Link;
|
||||
use gipfl\IcingaWeb2\Table\ZfQueryBasedTable;
|
||||
|
||||
class ActivityLogTable extends ZfQueryBasedTable
|
||||
class ActivityLogTable extends IntlZfQueryBasedTable
|
||||
{
|
||||
protected $filters = [];
|
||||
|
||||
@ -55,7 +54,7 @@ class ActivityLogTable extends ZfQueryBasedTable
|
||||
|
||||
return $this::tr([
|
||||
$this::td($this->makeLink($row))->setSeparator(' '),
|
||||
$this::td(strftime('%H:%M:%S', $row->ts_change_time))
|
||||
$this::td($this->getTime($row->ts_change_time))
|
||||
])->addAttributes(['class' => $action]);
|
||||
}
|
||||
|
||||
|
@ -4,13 +4,12 @@ namespace Icinga\Module\Director\Web\Table;
|
||||
|
||||
use ipl\Html\Html;
|
||||
use gipfl\IcingaWeb2\Link;
|
||||
use gipfl\IcingaWeb2\Table\ZfQueryBasedTable;
|
||||
use Icinga\Date\DateFormatter;
|
||||
use Icinga\Module\Director\Core\Json;
|
||||
use Icinga\Module\Director\DirectorObject\Automation\Basket;
|
||||
use RuntimeException;
|
||||
|
||||
class BasketSnapshotTable extends ZfQueryBasedTable
|
||||
class BasketSnapshotTable extends IntlZfQueryBasedTable
|
||||
{
|
||||
use DbHelper;
|
||||
|
||||
|
101
library/Director/Web/Table/Dependency/DependencyInfoTable.php
Normal file
101
library/Director/Web/Table/Dependency/DependencyInfoTable.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Web\Table\Dependency;
|
||||
|
||||
use Icinga\Application\Modules\Module;
|
||||
use Icinga\Module\Director\Application\DependencyChecker;
|
||||
use Icinga\Web\Url;
|
||||
|
||||
class DependencyInfoTable
|
||||
{
|
||||
protected $module;
|
||||
|
||||
protected $checker;
|
||||
|
||||
public function __construct(DependencyChecker $checker, Module $module)
|
||||
{
|
||||
$this->module = $module;
|
||||
$this->checker = $checker;
|
||||
}
|
||||
|
||||
protected function linkToModule($name, $icon)
|
||||
{
|
||||
return Html::link(
|
||||
Html::escape($name),
|
||||
Html::webUrl('config/module', ['name' => $name]),
|
||||
[
|
||||
'class' => "icon-$icon"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$html = '<table class="common-table table-row-selectable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>' . Html::escape($this->translate('Module name')) . '</th>
|
||||
<th>' . Html::escape($this->translate('Required')) . '</th>
|
||||
<th>' . Html::escape($this->translate('Installed')) . '</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-base-target="_next">
|
||||
';
|
||||
foreach ($this->checker->getDependencies($this->module) as $dependency) {
|
||||
$name = $dependency->getName();
|
||||
$isLibrary = substr($name, 0, 11) === 'icinga-php-';
|
||||
$rowAttributes = $isLibrary ? ['data-base-target' => '_self'] : null;
|
||||
if ($dependency->isSatisfied()) {
|
||||
if ($dependency->isSatisfied()) {
|
||||
$icon = 'ok';
|
||||
} else {
|
||||
$icon = 'cancel';
|
||||
}
|
||||
$link = $isLibrary ? $this->noLink($name, $icon) : $this->linkToModule($name, $icon);
|
||||
$installed = $dependency->getInstalledVersion();
|
||||
} elseif ($dependency->isInstalled()) {
|
||||
$installed = sprintf('%s (%s)', $dependency->getInstalledVersion(), $this->translate('disabled'));
|
||||
$link = $this->linkToModule($name, 'cancel');
|
||||
} else {
|
||||
$installed = $this->translate('missing');
|
||||
$repository = $isLibrary ? $name : "icingaweb2-module-$name";
|
||||
$link = sprintf(
|
||||
'%s (%s)',
|
||||
$this->noLink($name, 'cancel'),
|
||||
Html::linkToGitHub(Html::escape($this->translate('more')), 'Icinga', $repository)
|
||||
);
|
||||
}
|
||||
|
||||
$html .= $this->htmlRow([
|
||||
$link,
|
||||
Html::escape($dependency->getRequirement()),
|
||||
Html::escape($installed)
|
||||
], $rowAttributes);
|
||||
}
|
||||
|
||||
return $html . '</tbody>
|
||||
</table>
|
||||
';
|
||||
}
|
||||
|
||||
protected function noLink($label, $icon)
|
||||
{
|
||||
return Html::link(Html::escape($label), Url::fromRequest()->with('rnd', rand(1, 100000)), [
|
||||
'class' => "icon-$icon"
|
||||
]);
|
||||
}
|
||||
|
||||
protected function translate($string)
|
||||
{
|
||||
return \mt('director', $string);
|
||||
}
|
||||
|
||||
protected function htmlRow(array $cols, $rowAttributes)
|
||||
{
|
||||
$content = '';
|
||||
foreach ($cols as $escapedContent) {
|
||||
$content .= Html::tag('td', null, $escapedContent);
|
||||
}
|
||||
return Html::tag('tr', $rowAttributes, $content);
|
||||
}
|
||||
}
|
74
library/Director/Web/Table/Dependency/Html.php
Normal file
74
library/Director/Web/Table/Dependency/Html.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Web\Table\Dependency;
|
||||
|
||||
use Icinga\Web\Url;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Minimal HTML helper, as we might be forced to run without ipl
|
||||
*/
|
||||
class Html
|
||||
{
|
||||
public static function tag($tag, $attributes = [], $escapedContent = null)
|
||||
{
|
||||
$result = "<$tag";
|
||||
if (! empty($attributes)) {
|
||||
foreach ($attributes as $name => $value) {
|
||||
if (! preg_match('/^[a-z][a-z0-9:-]*$/i', $name)) {
|
||||
throw new InvalidArgumentException("Invalid attribute name: '$name'");
|
||||
}
|
||||
|
||||
$result .= " $name=\"" . self::escapeAttributeValue($value) . '"';
|
||||
}
|
||||
}
|
||||
|
||||
return "$result>$escapedContent</$tag>";
|
||||
}
|
||||
|
||||
public static function webUrl($path, $params)
|
||||
{
|
||||
return Url::fromPath($path, $params);
|
||||
}
|
||||
|
||||
public static function link($escapedLabel, $url, $attributes = [])
|
||||
{
|
||||
return static::tag('a', [
|
||||
'href' => $url,
|
||||
] + $attributes, $escapedLabel);
|
||||
}
|
||||
|
||||
public static function linkToGitHub($escapedLabel, $namespace, $repository)
|
||||
{
|
||||
return static::link(
|
||||
$escapedLabel,
|
||||
'https://github.com/' . urlencode($namespace) . '/' . urlencode($repository),
|
||||
[
|
||||
'target' => '_blank',
|
||||
'rel' => 'noreferrer',
|
||||
'class' => 'icon-forward'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
protected static function escapeAttributeValue($value)
|
||||
{
|
||||
$value = str_replace('"', '"', $value);
|
||||
// Escape ambiguous ampersands
|
||||
return preg_replace_callback('/&[0-9A-Z]+;/i', function ($match) {
|
||||
$subject = $match[0];
|
||||
|
||||
if (htmlspecialchars_decode($subject, ENT_COMPAT | ENT_HTML5) === $subject) {
|
||||
// Ambiguous ampersand
|
||||
return str_replace('&', '&', $subject);
|
||||
}
|
||||
|
||||
return $subject;
|
||||
}, $value);
|
||||
}
|
||||
|
||||
public static function escape($any)
|
||||
{
|
||||
return htmlspecialchars($any);
|
||||
}
|
||||
}
|
@ -3,10 +3,9 @@
|
||||
namespace Icinga\Module\Director\Web\Table;
|
||||
|
||||
use gipfl\IcingaWeb2\Link;
|
||||
use gipfl\IcingaWeb2\Table\ZfQueryBasedTable;
|
||||
use Icinga\Date\DateFormatter;
|
||||
|
||||
class DeploymentLogTable extends ZfQueryBasedTable
|
||||
class DeploymentLogTable extends IntlZfQueryBasedTable
|
||||
{
|
||||
use DbHelper;
|
||||
|
||||
|
@ -105,10 +105,10 @@ class IcingaServiceSetServiceTable extends ZfQueryBasedTable
|
||||
{
|
||||
if ($this->readonly) {
|
||||
if ($this->highlightedService === $row->service) {
|
||||
return Html::tag('span', ['class' => 'icon-right-big'], $row->service);
|
||||
} else {
|
||||
return $row->service;
|
||||
return Html::tag('span', ['class' => 'ro-service icon-right-big'], $row->service);
|
||||
}
|
||||
|
||||
return Html::tag('span', ['class' => 'ro-service'], $row->service);
|
||||
}
|
||||
|
||||
if ($this->affectedHost) {
|
||||
|
51
library/Director/Web/Table/IntlZfQueryBasedTable.php
Normal file
51
library/Director/Web/Table/IntlZfQueryBasedTable.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Web\Table;
|
||||
|
||||
use DateTime;
|
||||
use gipfl\IcingaWeb2\Table\ZfQueryBasedTable;
|
||||
use IntlDateFormatter;
|
||||
use Locale;
|
||||
|
||||
abstract class IntlZfQueryBasedTable extends ZfQueryBasedTable
|
||||
{
|
||||
protected function getDateFormatter()
|
||||
{
|
||||
return (new IntlDateFormatter(
|
||||
Locale::getDefault(),
|
||||
IntlDateFormatter::FULL,
|
||||
IntlDateFormatter::NONE
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $timestamp
|
||||
*/
|
||||
protected function renderDayIfNew($timestamp)
|
||||
{
|
||||
$day = $this->getDateFormatter()->format((new DateTime())->setTimestamp($timestamp));
|
||||
|
||||
if ($this->lastDay !== $day) {
|
||||
$this->nextHeader()->add(
|
||||
$this::th($day, [
|
||||
'colspan' => 2,
|
||||
'class' => 'table-header-day'
|
||||
])
|
||||
);
|
||||
|
||||
$this->lastDay = $day;
|
||||
$this->nextBody();
|
||||
}
|
||||
}
|
||||
|
||||
protected function getTime(int $timeStamp)
|
||||
{
|
||||
$timeFormatter = $this->getDateFormatter();
|
||||
|
||||
$timeFormatter->setPattern(
|
||||
in_array(Locale::getDefault(), ['en_US', 'en_US.UTF-8']) ? 'h:mm:ss a': 'H:mm:ss'
|
||||
);
|
||||
|
||||
return $timeFormatter->format((new DateTime())->setTimestamp($timeStamp));
|
||||
}
|
||||
}
|
@ -4,9 +4,8 @@ namespace Icinga\Module\Director\Web\Table;
|
||||
|
||||
use Icinga\Module\Director\Objects\SyncRule;
|
||||
use gipfl\IcingaWeb2\Link;
|
||||
use gipfl\IcingaWeb2\Table\ZfQueryBasedTable;
|
||||
|
||||
class SyncRunTable extends ZfQueryBasedTable
|
||||
class SyncRunTable extends IntlZfQueryBasedTable
|
||||
{
|
||||
/** @var SyncRule */
|
||||
protected $rule;
|
||||
@ -28,7 +27,7 @@ class SyncRunTable extends ZfQueryBasedTable
|
||||
return $this::tr([
|
||||
$this::td($this->makeSummary($row)),
|
||||
$this::td(new Link(
|
||||
strftime('%H:%M:%S', $time),
|
||||
$this->getTime($time),
|
||||
'director/syncrule/history',
|
||||
[
|
||||
'id' => $row->rule_id,
|
||||
|
@ -1,6 +1,6 @@
|
||||
Name: Icinga Director
|
||||
Version: 1.8.0
|
||||
Depends: reactbundle (>=0.7.0), ipl (>=0.3.0), incubator (>=0.6.0)
|
||||
Version: 1.8.2
|
||||
Depends: reactbundle (>=0.7.0), ipl (>=0.3.0), incubator (>=0.21.0)
|
||||
Description: Director - Config tool for Icinga 2
|
||||
Icinga Director is a configuration tool that has been designed to make
|
||||
Icinga 2 configuration easy and understandable.
|
||||
|
@ -453,6 +453,9 @@ form dl {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
.strike-links span.ro-service {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
// TODO: figure out whether form.editor and filter-related CSS is still required
|
||||
div.filter > form.search, div.filter > a {
|
||||
|
17
run.php
17
run.php
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Icinga\Application\Modules\Module;
|
||||
use Icinga\Module\Director\Application\DependencyChecker;
|
||||
|
||||
if (version_compare(PHP_VERSION, '5.6.0') < 0) {
|
||||
include __DIR__ . '/run-php5.3.php';
|
||||
@ -8,20 +9,8 @@ if (version_compare(PHP_VERSION, '5.6.0') < 0) {
|
||||
}
|
||||
|
||||
/** @var Module $this */
|
||||
$modules = $this->app->getModuleManager();
|
||||
foreach ($this->getDependencies() as $module => $required) {
|
||||
if ($modules->hasEnabled($module)) {
|
||||
$installed = $modules->getModule($module, false)->getVersion();
|
||||
$installed = ltrim($installed, 'v'); // v0.6.0 VS 0.6.0
|
||||
if (preg_match('/^([<>=]+)\s*v?(\d+\.\d+\.\d+)$/', $required, $match)) {
|
||||
$operator = $match[1];
|
||||
$vRequired = $match[2];
|
||||
if (version_compare($installed, $vRequired, $operator)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$checker = new DependencyChecker($this->app);
|
||||
if (! $checker->satisfiesDependencies($this)) {
|
||||
include __DIR__ . '/run-missingdeps.php';
|
||||
return;
|
||||
}
|
||||
|
72
test/php/library/Director/Application/DependencyTest.php
Normal file
72
test/php/library/Director/Application/DependencyTest.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Icinga\Module\Director\Application;
|
||||
|
||||
use Icinga\Module\Director\Application\Dependency;
|
||||
use Icinga\Module\Director\Test\BaseTestCase;
|
||||
|
||||
class DependencyTest extends BaseTestCase
|
||||
{
|
||||
public function testIsNotInstalled()
|
||||
{
|
||||
$dependency = new Dependency('something', '>=0.3.0');
|
||||
$this->assertFalse($dependency->isInstalled());
|
||||
}
|
||||
|
||||
public function testNotSatisfiedWhenNotInstalled()
|
||||
{
|
||||
$dependency = new Dependency('something', '>=0.3.0');
|
||||
$this->assertFalse($dependency->isSatisfied());
|
||||
}
|
||||
|
||||
public function testIsInstalled()
|
||||
{
|
||||
$dependency = new Dependency('something', '>=0.3.0');
|
||||
$dependency->setInstalledVersion('1.10.0');
|
||||
$this->assertTrue($dependency->isInstalled());
|
||||
}
|
||||
|
||||
public function testNotEnabled()
|
||||
{
|
||||
$dependency = new Dependency('something', '>=0.3.0');
|
||||
$this->assertFalse($dependency->isEnabled());
|
||||
}
|
||||
|
||||
public function testIsEnabled()
|
||||
{
|
||||
$dependency = new Dependency('something', '>=0.3.0');
|
||||
$dependency->setEnabled();
|
||||
$this->assertTrue($dependency->isEnabled());
|
||||
}
|
||||
|
||||
public function testNotSatisfiedWhenNotEnabled()
|
||||
{
|
||||
$dependency = new Dependency('something', '>=0.3.0');
|
||||
$dependency->setInstalledVersion('1.10.0');
|
||||
$this->assertFalse($dependency->isSatisfied());
|
||||
}
|
||||
|
||||
public function testSatisfiedWhenEqual()
|
||||
{
|
||||
$dependency = new Dependency('something', '>=0.3.0');
|
||||
$dependency->setInstalledVersion('0.3.0');
|
||||
$dependency->setEnabled();
|
||||
$this->assertTrue($dependency->isSatisfied());
|
||||
}
|
||||
|
||||
public function testSatisfiedWhenGreater()
|
||||
{
|
||||
$dependency = new Dependency('something', '>=0.3.0');
|
||||
$dependency->setInstalledVersion('0.10.0');
|
||||
$dependency->setEnabled();
|
||||
$this->assertTrue($dependency->isSatisfied());
|
||||
}
|
||||
|
||||
public function testNotSatisfiedWhenSmaller()
|
||||
{
|
||||
$dependency = new Dependency('something', '>=20.3.0');
|
||||
$dependency->setInstalledVersion('4.999.999');
|
||||
$dependency->setEnabled();
|
||||
$this->assertFalse($dependency->isSatisfied());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user