Compare commits

..

No commits in common. "main" and "v2.12.2" have entirely different histories.

459 changed files with 46955 additions and 990 deletions

View File

@ -1,6 +0,0 @@
version: 2
updates:
- package-ecosystem: composer
directory: /
schedule:
interval: daily

View File

@ -6,6 +6,15 @@ on:
- main - main
jobs: jobs:
update: trigger-update:
uses: icinga/github-actions/.github/workflows/L10n-update.yml@main name: L10n Update Trigger
secrets: inherit runs-on: ubuntu-latest
steps:
- name: Repository dispatch
uses: peter-evans/repository-dispatch@v1
with:
token: ${{ secrets.ICINGABOT_TOKEN }}
repository: Icinga/L10n
event-type: update
client-payload: '{"origin": "${{ github.repository }}", "commit": "${{ github.sha }}"}'

View File

@ -1,33 +1,111 @@
name: CI name: PHP Tests
on: on:
push: push:
branches: branches:
- main - main
- release/* - release/*
- support/*
pull_request: pull_request:
branches: branches:
- main - main
jobs: jobs:
php: lint:
name: PHP name: Static analysis for php ${{ matrix.php }} on ${{ matrix.os }}
uses: Icinga/github-actions/.github/workflows/php.yml@main runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
os: ['ubuntu-latest']
steps:
- name: Checkout code base
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with: with:
databases: true php-version: ${{ matrix.php }}
php-extensions: ldap tools: phpcs
phpunit-version: 9.6
dependencies: | - name: Setup dependencies
{ run: composer require -n --no-progress overtrue/phplint
"./icinga-php/ipl" : "https://github.com/Icinga/icinga-php-library.git#snapshot/nightly",
"./icinga-php/vendor" : "https://github.com/Icinga/icinga-php-thirdparty.git#snapshot/nightly", - name: PHP Lint
"./icingaweb2-modules/icingadb" : "https://github.com/Icinga/icingadb-web.git", if: ${{ ! cancelled() }}
"./icingaweb2-modules/pdfexport" : "https://github.com/Icinga/icingaweb2-module-pdfexport.git", run: ./vendor/bin/phplint -n --exclude={^vendor/.*} --exclude=library/Icinga/Util/String.php ${{ matrix.phplint_options }} -- .
"./icingaweb2-modules/x509" : "https://github.com/Icinga/icingaweb2-module-x509.git"
} - name: PHP CodeSniffer
env: | if: ${{ ! cancelled() }}
{ run: phpcs
"ICINGAWEB_LIBDIR" : "./icinga-php",
"ICINGAWEB_MODULES_DIR" : "modules:./icingaweb2-modules" test:
} name: Unit tests with php ${{ matrix.php }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
env:
phpunit-version: 9.5
strategy:
fail-fast: false
matrix:
php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
os: ['ubuntu-latest']
include:
- php: '7.2'
phpunit-version: 8.5
services:
mysql:
image: mariadb
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: icinga_unittest
MYSQL_USER: icinga_unittest
MYSQL_PASSWORD: icinga_unittest
options: >-
--health-cmd "mariadb -s -uroot -proot -e'SHOW DATABASES;' 2> /dev/null | grep icinga_unittest > test"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 3306/tcp
pgsql:
image: postgres
env:
POSTGRES_USER: icinga_unittest
POSTGRES_PASSWORD: icinga_unittest
POSTGRES_DB: icinga_unittest
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432/tcp
steps:
- name: Checkout code base
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: phpunit:${{ matrix.phpunit-version || env.phpunit-version }}
extensions: mysql, pgsql, ldap
- name: Setup dependencies
run: |
composer require -n --no-progress mockery/mockery ipl/i18n:@dev ipl/web:@dev
git clone --depth 1 --branch snapshot/nightly https://github.com/Icinga/icinga-php-thirdparty.git vendor/icinga-php-thirdparty
- name: PHPUnit
env:
ICINGAWEB_TEST_MYSQL_PORT: ${{ job.services.mysql.ports['3306'] }}
ICINGAWEB_TEST_PGSQL_PORT: ${{ job.services.pgsql.ports['5432'] }}
ICINGAWEB_LIBDIR: vendor/
run: phpunit -c modules/test/phpunit.xml --verbose

18
.github/workflows/phpstan.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: PHPStan
on:
pull_request:
branches:
- main
jobs:
phpstan:
uses: icinga/github-actions/.github/workflows/phpstan.yml@main
with:
phpExtensions: ldap
dependencies: |
{
"/usr/share/icingaweb2-modules/x509" : "https://github.com/Icinga/icingaweb2-module-x509.git",
"/usr/share/icingaweb2-modules/icingadb" : "https://github.com/Icinga/icingadb-web.git",
"/usr/share/icingaweb2-modules/pdfexport" : "https://github.com/Icinga/icingaweb2-module-pdfexport.git"
}

8
.gitignore vendored
View File

@ -3,8 +3,12 @@
# Except those related to git # Except those related to git
!.git* !.git*
!.travis.yml
!.mailmap !.mailmap
# Testing - created by test/setup_vendor.sh
/vendor/
# Exclude application log files # Exclude application log files
var/log/* var/log/*
@ -16,7 +20,3 @@ build/*
# Exclude dompdf font cache # Exclude dompdf font cache
library/vendor/dompdf/lib/fonts/*.php library/vendor/dompdf/lib/fonts/*.php
library/vendor/dompdf/lib/fonts/log.htm library/vendor/dompdf/lib/fonts/log.htm
# Exclude files from composer install
vendor/
composer.lock

View File

@ -15,10 +15,8 @@ Eric Lippmann <eric.lippmann@icinga.com> <lippserd@googlemail.com>
Florian Strohmaier <florian.strohmaier@icinga.com> <florian.strohmaier@netways.de> Florian Strohmaier <florian.strohmaier@icinga.com> <florian.strohmaier@netways.de>
Florian Strohmaier <florian.strohmaier@icinga.com> <hello@florianstrohmaier.com> Florian Strohmaier <florian.strohmaier@icinga.com> <hello@florianstrohmaier.com>
Florian Strohmaier <florian.strohmaier@icinga.com> <florian.strohmaier@me.com> Florian Strohmaier <florian.strohmaier@icinga.com> <florian.strohmaier@me.com>
Gianluca Piccolo <gianluca.piccolo@wuerth-phoenix.com> <2795394+gianlucapiccolo@users.noreply.github.com>
Gunnar Beutner <gunnar.beutner@netways.de> <gunnar@beutner.name> Gunnar Beutner <gunnar.beutner@netways.de> <gunnar@beutner.name>
Jannis Moßhammer <jannis.mosshammer@netways.de> Jannis Moßhammer <jannis.mosshammer@netways.de>
Jan Schuppik <jan.schuppik@icinga.com> <114286749+Jan-Schuppik@users.noreply.github.com>
Johannes Meyer <johannes.meyer@icinga.com> <johannes.meyer@netways.de> Johannes Meyer <johannes.meyer@icinga.com> <johannes.meyer@netways.de>
Jennifer Mourek <jennifer.mourek@icinga.com> <jennifer.mourek@netways.de> Jennifer Mourek <jennifer.mourek@icinga.com> <jennifer.mourek@netways.de>
Marius Hein <marius.hein@netways.de> <mhein@itsocks.de> Marius Hein <marius.hein@netways.de> <mhein@itsocks.de>
@ -44,6 +42,5 @@ Thomas Gelf <thomas.gelf@icinga.com> <thomas@gelf.net>
Tobias Bauriedel <tobias.bauriedel@netways.de> <tobias@bauriedel.de> Tobias Bauriedel <tobias.bauriedel@netways.de> <tobias@bauriedel.de>
Yonas Habteab <yonas.habteab@icinga.com> <yonas.habteab@netways.de> Yonas Habteab <yonas.habteab@icinga.com> <yonas.habteab@netways.de>
Ravi Kumar Kempapura Srinivasa <ravi.srinivasa@icinga.com> <33730024+raviks789@users.noreply.github.com> Ravi Kumar Kempapura Srinivasa <ravi.srinivasa@icinga.com> <33730024+raviks789@users.noreply.github.com>
Ravi Kumar Kempapura Srinivasa <ravi.srinivasa@icinga.com> <raviks789@gmail.com>
Sukhwinder Dhillon <sukhwinder.dhillon@icinga.com> <54990055+sukhwinder33445@users.noreply.github.com> Sukhwinder Dhillon <sukhwinder.dhillon@icinga.com> <54990055+sukhwinder33445@users.noreply.github.com>
Sukhwinder Dhillon <sukhwinder.dhillon@icinga.com> <sukhwinder33445@gmail.com> Sukhwinder Dhillon <sukhwinder.dhillon@icinga.com> <sukhwinder33445@gmail.com>

View File

@ -1,5 +1,4 @@
Aaron Collins <acollins@chegg.com> Aaron Collins <acollins@chegg.com>
Alex <alexander.noack@web.de>
Alexander A. Klimov <alexander.klimov@icinga.com> Alexander A. Klimov <alexander.klimov@icinga.com>
Alexander Aleksandrovič Klimov <alexander.klimov@icinga.com> Alexander Aleksandrovič Klimov <alexander.klimov@icinga.com>
Alexander Fuhr <alexander.fuhr@netways.de> Alexander Fuhr <alexander.fuhr@netways.de>
@ -55,13 +54,10 @@ Ian Shearin <ishearin@womply.com>
ignasr <ignas.linux@gmail.com> ignasr <ignas.linux@gmail.com>
Janne Heß <janne@hess.ooo> Janne Heß <janne@hess.ooo>
Jannis Moßhammer <jannis.mosshammer@netways.de> Jannis Moßhammer <jannis.mosshammer@netways.de>
Jan Schuppik <jan.schuppik@icinga.com>
Jennifer Mourek <jennifer.mourek@icinga.com> Jennifer Mourek <jennifer.mourek@icinga.com>
Jiri Pejchal <jiri.pejchal@gmail.com> Jiri Pejchal <jiri.pejchal@gmail.com>
Joe Doherty <git@pjuu.com> Joe Doherty <git@pjuu.com>
Johannes Meyer <johannes.meyer@icinga.com> Johannes Meyer <johannes.meyer@icinga.com>
Johannes Rauh <johannes.rauh@icinga.com>
Jolien Trog <jolien.trog@icinga.com>
Joonas Kylmälä <joonas.kylmala@kirjastot.fi> Joonas Kylmälä <joonas.kylmala@kirjastot.fi>
Jorge Vallecillo <jorgevallecilloc@gmail.com> Jorge Vallecillo <jorgevallecilloc@gmail.com>
Jo Rhett <jo@chegg.com> Jo Rhett <jo@chegg.com>
@ -77,7 +73,6 @@ Marc DeTrano <marc@gridshield.net>
Marcel Weinberg <marcel.weinberg@secucloud.com> Marcel Weinberg <marcel.weinberg@secucloud.com>
Marcus Cobden <marcus@marcuscobden.co.uk> Marcus Cobden <marcus@marcuscobden.co.uk>
Marian Rainer-Harbach <marian@rainer-harbach.at> Marian Rainer-Harbach <marian@rainer-harbach.at>
marianrh <19990392+marianrh@users.noreply.github.com>
Mario Rimann <mario@rimann.org> Mario Rimann <mario@rimann.org>
Marius Hein <marius.hein@netways.de> Marius Hein <marius.hein@netways.de>
Markus Frosch <markus.frosch@icinga.com> Markus Frosch <markus.frosch@icinga.com>
@ -130,7 +125,6 @@ Rune Darrud <theflyingcorpse@gmail.com>
Russell Kubik <russkubik@3d-p.com> Russell Kubik <russkubik@3d-p.com>
Sander Ferdinand <sa.ferdinand@gmail.com> Sander Ferdinand <sa.ferdinand@gmail.com>
sant-swedge <simon.wedge@sant.ox.ac.uk> sant-swedge <simon.wedge@sant.ox.ac.uk>
Silas <67681686+Tqnsls@users.noreply.github.com>
Simone Orsi <simahawk@users.noreply.github.com> Simone Orsi <simahawk@users.noreply.github.com>
ss23 <stephen@zxsecurity.co.nz> ss23 <stephen@zxsecurity.co.nz>
Sukhwinder Dhillon <sukhwinder.dhillon@icinga.com> Sukhwinder Dhillon <sukhwinder.dhillon@icinga.com>

View File

@ -4,96 +4,9 @@ Please make sure to always read our [Upgrading](doc/80-Upgrading.md) documentati
## What's New ## What's New
### What's New in Version 2.12.6
You can find all issues related to this release on our [Roadmap](https://github.com/Icinga/icingaweb2/milestone/86?closed=1).
#### It's Like Fine Wine
Icinga Web 2.12 is now two years old. But like fine wine, it gets better with age. Each fix and improvement included
this time enhances your experience with Icinga Web. Even if just a little bit. Maybe a small annoyance is gone now, or
something you didn't even notice was broken is fixed.
But let's get to it, a small selection of fixes and improvements:
* Search box shows many magnifying glasses for some community themes #5395
* Authentication hooks are not called with external backends #5415
* Improve Minimal layout #5386
### What's New in Version 2.12.5
You can find all issues related to this release on our [Roadmap](https://github.com/Icinga/icingaweb2/milestone/85?closed=1).
#### PHP 8.4 Support
We're again a little behind schedule, but now we support PHP 8.4! This means that installations on Ubuntu 25.04 and
Fedora 42+ can now install Icinga Web without worrying about PHP related incompatibilities. Icinga packages will be
available in the next few days.
#### Good Things Take Time
There's only a single (notable) recent issue that is fixed with this release. All the others are a bit older.
* External URLs set up as dashlets are not *embedded* the same as navigation items [#5346](https://github.com/Icinga/icingaweb2/issues/5346)
But the team sat together a few weeks ago and fixed a bug here and there. And of course, also in Icinga Web!
* Users who are not allowed to change the theme, cannot change the theme mode either [#5385](https://github.com/Icinga/icingaweb2/issues/5385)
* Filtering for older-than events with relative time does not work [#5263](https://github.com/Icinga/icingaweb2/issues/5263)
* External logout not working from the navigation dashboard [#5000](https://github.com/Icinga/icingaweb2/issues/5000)
* Empty values are NULL in CSV exports [#5350](https://github.com/Icinga/icingaweb2/issues/5350)
#### Breaking, Somewhat
This is mainly for developers.
With the support of PHP 8.4, we introduced a new environment variable, `ICINGAWEB_ENVIRONMENT`. Unless set to `dev`,
Icinga Web will not show nor log deprecation notices anymore.
### What's New in Version 2.12.4
This is a hotfix release which fixes the following issue:
Database login broken after upgrade [#5343](https://github.com/Icinga/icingaweb2/issues/5343)
### What's New in Version 2.12.3
**Notice:** This is a security release. It is recommended to upgrade _immediately_.
You can find all issues related to this release on our [Roadmap](https://github.com/Icinga/icingaweb2/milestone/83?closed=1).
#### Vulnerabilities, Closed
Cross site scripting is one of the worst attacks on web based platforms. Especially, if carrying it out is as easy as
the first two mentioned here. You might recognize the open redirect on the login. You are correct, we attempted to fix
it already with v2.11.3 but underestimated PHP's quirks. The last is difficult to exploit, hence the lowest severity
of all, but don't be fooled by that!
All four of them are backported to v2.11.5.
* XSS in embedded content [CVE-2025-27405](https://github.com/Icinga/icingaweb2/security/advisories/GHSA-3x37-fjc3-ch8w)
* DOM-based XSS [CVE-2025-27404](https://github.com/Icinga/icingaweb2/security/advisories/GHSA-c6pg-h955-wf66)
* Open redirect on login page [CVE-2025-30164](https://github.com/Icinga/icingaweb2/security/advisories/GHSA-8r73-6686-wv8q)
* Reflected XSS [CVE-2025-27609](https://github.com/Icinga/icingaweb2/security/advisories/GHSA-5cjw-fwjc-8j38)
Big thanks to all finders / reporters! :+1:
#### Bugs, Exterminated
Did you know, that we started [Icinga Notifications](https://icinga.com/docs/icinga-notifications/latest/) with support
for PostgreSQL first? Reason for that is, we wanted to make sure we are fully compatible with it right away. To ensure
things like logging in with a PostgreSQL authentication/group backend is case-insensitive, like it was always the case
for MySQL. Now it **really** is case-insensitive! There are also two issues fixed, which many of you will probably have
noticed since v2.12.2, sorry that it took so long :)
* Login against Postgres DB is case-sensitive [#5223](https://github.com/Icinga/icingaweb2/issues/5223)
* Role list has no functioning quick search [#5300](https://github.com/Icinga/icingaweb2/issues/5300)
* After clicking on Check now, the page does not refresh itself [#5293](https://github.com/Icinga/icingaweb2/issues/5293)
* Service States display wrong since update to 2.12.2 [#5290](https://github.com/Icinga/icingaweb2/issues/5290)
### What's New in Version 2.12.2 ### What's New in Version 2.12.2
You can find all issues related to this release on our [Roadmap](https://github.com/Icinga/icingaweb2/milestone/81?closed=1). You can find all issues related to this release on our Roadmap.
#### General Fixes #### General Fixes
@ -149,7 +62,7 @@ but its now slightly improved.
### What's New in Version 2.12.1 ### What's New in Version 2.12.1
You can find all issues related to this release on our [Roadmap](https://github.com/Icinga/icingaweb2/milestone/80?closed=1). You can find all issues related to this release on our Roadmap.
#### PHP 8.3 Support #### PHP 8.3 Support

View File

@ -1 +1 @@
v2.12.6 v2.12.2

View File

@ -3,108 +3,18 @@
namespace Icinga\Controllers; namespace Icinga\Controllers;
use Icinga\Web\Session; use Icinga\Web\Controller;
use ipl\Html\BaseHtmlElement;
use ipl\Html\Html;
use ipl\Html\HtmlString;
use ipl\Html\Text;
use ipl\Web\Compat\CompatController;
use ipl\Web\Url;
use ipl\Web\Widget\Icon;
use ipl\Web\Widget\Tabs;
/** /**
* Display external or internal links within an iframe * Display external or internal links within an iframe
*/ */
class IframeController extends CompatController class IframeController extends Controller
{ {
/** /**
* Display iframe w/ the given URL * Display iframe w/ the given URL
*/ */
public function indexAction(): void public function indexAction()
{ {
$url = Url::fromPath($this->params->getRequired('url')); $this->view->url = $this->params->getRequired('url');
$urlHash = $this->getRequest()->getHeader('X-Icinga-URLHash');
$expectedHash = hash('sha256', $url->getAbsoluteUrl() . Session::getSession()->getId());
$iframeUrl = Url::fromPath('iframe', ['url' => $url->getAbsoluteUrl()]);
if (! in_array($url->getScheme(), ['http', 'https'], true)) {
$this->httpBadRequest('Invalid URL scheme');
}
$this->injectTabs();
$this->getTabs()->setRefreshUrl($iframeUrl);
if ($urlHash) {
if ($urlHash !== $expectedHash) {
$this->httpBadRequest('Invalid URL hash');
}
} else {
$this->addContent(Html::tag('div', ['class' => 'iframe-warning'], [
Html::tag('h2', $this->translate('Attention!')),
Html::tag('p', ['class' => 'note'], $this->translate(
'You are about to open untrusted content embedded in Icinga Web! Only proceed,'
.' by clicking the link below, if you recognize and trust the source!'
)),
Html::tag('a', ['data-url-hash' => $expectedHash, 'href' => Html::escape($iframeUrl)], $url),
Html::tag('p', ['class' => 'reason'], [
new Icon('circle-info'),
Text::create($this->translate(
'You see this warning because you do not seem to have followed a link in Icinga Web.'
. ' You can bypass this in the future by configuring a navigation item instead.'
))
])
]));
return;
}
$this->getTabs()->setHash($expectedHash);
$this->addContent(Html::tag(
'div',
['class' => 'iframe-container'],
Html::tag('iframe', [
'src' => $url,
'sandbox' => 'allow-same-origin allow-scripts allow-popups allow-forms',
])
));
}
private function injectTabs(): void
{
$this->tabs = new class extends Tabs {
private $hash;
public function setHash($hash)
{
$this->hash = $hash;
return $this;
}
protected function assemble()
{
$tabHtml = substr($this->tabs->render(), 34, -5);
if ($this->refreshUrl !== null) {
$tabHtml = preg_replace(
[
'/(?<=class="refresh-container-control spinner" href=")([^"]*)/',
'/(\s)(?=href)/'
],
[
$this->refreshUrl->getAbsoluteUrl(),
' data-url-hash="' . $this->hash . '" '
],
$tabHtml
);
}
BaseHtmlElement::add(HtmlString::create($tabHtml));
}
};
$this->controls->setTabs($this->tabs);
} }
} }

View File

@ -133,18 +133,6 @@ class MigrationForm extends CompatForm
. ' that has the appropriate credentials to resolve this issue.' . ' that has the appropriate credentials to resolve this issue.'
), ),
implode(', ', $mm->getRequiredDatabasePrivileges()) implode(', ', $mm->getRequiredDatabasePrivileges())
))),
new HtmlElement('br'),
new HtmlElement('br'),
new HtmlElement('span', null, Text::create(sprintf(
$this->translate(
'The database name may contain either an underscore or a percent sign.'
. ' In MySQL these characters represent a wildcard. If part of a database name,'
. ' they might not have been escaped when manually granting privileges.'
. ' Privileges might not be detected in this case. Check the documentation and'
. ' update your grants accordingly: %s'
),
'https://dev.mysql.com/doc/refman/8.0/en/grant.html#grant-quoting'
))) )))
) )
); );

View File

@ -184,6 +184,15 @@ use ipl\Web\Widget\StateBadge;
</div> </div>
<div class="about-social"> <div class="about-social">
<?= $this->qlink( <?= $this->qlink(
null,
'https://www.twitter.com/icinga',
null,
array(
'target' => '_blank',
'icon' => 'twitter',
'title' => $this->translate('Icinga on Twitter')
)
) ?> <?= $this->qlink(
null, null,
'https://www.facebook.com/icinga', 'https://www.facebook.com/icinga',
null, null,

View File

@ -28,6 +28,18 @@
</div> </div>
</div> </div>
<ul id="social"> <ul id="social">
<li>
<?= $this->qlink(
null,
'https://twitter.com/icinga',
null,
array(
'target' => '_blank',
'icon' => 'twitter',
'title' => $this->translate('Icinga on Twitter')
)
) ?>
</li>
<li> <li>
<?= $this->qlink( <?= $this->qlink(
null, null,

View File

@ -6,7 +6,7 @@
<?= $this->tabs->render($this); ?> <?= $this->tabs->render($this); ?>
<br/> <br/>
<div> <div>
<h1>Could not <?= $action; ?> module "<?= $this->escape($moduleName); ?>"</h1> <h1>Could not <?= $action; ?> module "<?= $moduleName; ?>"</h1>
<p> <p>
While operation the following error occurred: While operation the following error occurred:
<br /> <br />

View File

@ -23,7 +23,7 @@ $modReason = [];
if (isset($requiredVendor, $requiredProject) && $requiredVendor && $requiredProject) { if (isset($requiredVendor, $requiredProject) && $requiredVendor && $requiredProject) {
// TODO: I don't like this, can we define requirements somewhere else? // TODO: I don't like this, can we define requirements somewhere else?
$coreDeps = ['icinga-php-library' => '>= 0.14.2', 'icinga-php-thirdparty' => '>= 0.12']; $coreDeps = ['icinga-php-library' => '>= 0.13.2', 'icinga-php-thirdparty' => '>= 0.12'];
foreach ($coreDeps as $libraryName => $requiredVersion) { foreach ($coreDeps as $libraryName => $requiredVersion) {
if (! $libraries->has($libraryName)) { if (! $libraries->has($libraryName)) {

View File

@ -0,0 +1,8 @@
<?php if (! $compact): ?>
<div class="controls">
<?= $tabs ?>
</div>
<?php endif ?>
<div class="iframe-container">
<iframe src="<?= $this->escape($url) ?>" frameborder="no"></iframe>
</div>

View File

@ -8,7 +8,6 @@ $searchDashboard->setUser($this->Auth()->getUser());
if ($searchDashboard->search('dummy')->getPane('search')->hasDashlets()): ?> if ($searchDashboard->search('dummy')->getPane('search')->hasDashlets()): ?>
<form action="<?= $this->href('search') ?>" method="get" role="search" class="search-control"> <form action="<?= $this->href('search') ?>" method="get" role="search" class="search-control">
<i class="icon fa-search fa search-icon"></i>
<input type="text" name="q" id="search" class="search search-input" required <input type="text" name="q" id="search" class="search search-input" required
placeholder="<?= $this->translate('Search') ?> &hellip;" placeholder="<?= $this->translate('Search') ?> &hellip;"
autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"> autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">

View File

@ -21,5 +21,7 @@
<?= $this->translate('In case you can access the file by yourself, you can open it and insert the config manually:'); ?> <?= $this->translate('In case you can access the file by yourself, you can open it and insert the config manually:'); ?>
</p> </p>
<p> <p>
<pre><code><?= $this->escape($configString); ?></code></pre> <pre>
<code><?= $this->escape($configString); ?></code>
</pre>
</p> </p>

View File

@ -1,10 +0,0 @@
{
"config": {
"platform": {
"php": "8.2"
}
},
"require-dev": {
"mockery/mockery": "^1.6"
}
}

View File

@ -399,7 +399,7 @@ You will need to install certain dependencies depending on your setup:
monitor your infrastructure monitor your infrastructure
* A web server, e.g. Apache or Nginx * A web server, e.g. Apache or Nginx
* PHP version ≥ 7.2 * PHP version ≥ 7.2
* [Icinga PHP Library (ipl)](https://github.com/Icinga/icinga-php-library) (≥ 0.14.2) * [Icinga PHP Library (ipl)](https://github.com/Icinga/icinga-php-library) (≥ 0.13.2)
* [Icinga PHP Thirdparty](https://github.com/Icinga/icinga-php-thirdparty) (≥ 0.12) * [Icinga PHP Thirdparty](https://github.com/Icinga/icinga-php-thirdparty) (≥ 0.12)
* The following PHP modules must be installed: cURL, json, gettext, fileinfo, intl, dom, OpenSSL and xml * The following PHP modules must be installed: cURL, json, gettext, fileinfo, intl, dom, OpenSSL and xml
* The [pdfexport](https://github.com/Icinga/icingaweb2-module-pdfexport) module (≥0.10) is required for the * The [pdfexport](https://github.com/Icinga/icingaweb2-module-pdfexport) module (≥0.10) is required for the

View File

@ -59,7 +59,7 @@ sudo htpasswd -c /etc/icingaweb2/.http-users icingaadmin
#### Apache Configuration <a id="authentication-configuration-external-authentication-example-apache"></a> #### Apache Configuration <a id="authentication-configuration-external-authentication-example-apache"></a>
Add the following configuration to the `<Directory>` directive in the `icingaweb2.conf` web server Add the following configuration to the `&lt;Directory&gt;` directive in the `icingaweb2.conf` web server
configuration file. configuration file.
``` ```

View File

@ -157,7 +157,6 @@ Here is a list of all Icinga Web components that are capable of strict CSP.
| Icinga Web GenericTTS Integration | [v2.1.0](https://github.com/Icinga/icingaweb2-module-generictts/releases/tag/v2.1.0) | | Icinga Web GenericTTS Integration | [v2.1.0](https://github.com/Icinga/icingaweb2-module-generictts/releases/tag/v2.1.0) |
| Icinga Web Nagvis Integration | [v1.2.0](https://github.com/Icinga/icingaweb2-module-nagvis/releases/tag/v1.2.0) | | Icinga Web Nagvis Integration | [v1.2.0](https://github.com/Icinga/icingaweb2-module-nagvis/releases/tag/v1.2.0) |
| Icinga Web AWS Integration | [v1.1.0](https://github.com/Icinga/icingaweb2-module-aws/releases/tag/v1.1.0) | | Icinga Web AWS Integration | [v1.1.0](https://github.com/Icinga/icingaweb2-module-aws/releases/tag/v1.1.0) |
| Icinga Web vSphere Integration | [v1.8.0](https://github.com/Icinga/icingaweb2-module-vspheredb/releases/tag/v1.8.0) |
## Advanced Authentication Tips <a id="advanced-topics-authentication-tips"></a> ## Advanced Authentication Tips <a id="advanced-topics-authentication-tips"></a>

View File

@ -3,16 +3,6 @@
Specific version upgrades are described below. Please note that upgrades are incremental. An upgrade from Specific version upgrades are described below. Please note that upgrades are incremental. An upgrade from
v2.6 to v2.8 requires to follow the instructions for v2.7 too. v2.6 to v2.8 requires to follow the instructions for v2.7 too.
## Upgrading to Icinga Web 2.13
**Breaking changes**
* The following columns of the `Servicestatus` table, which previously displayed the date time (string) as a fetched value, now display the unix timestamp to support relative time filters:
* `service_last_time_ok`
* `service_last_time_unknown`
* `service_last_time_warning`
* `service_last_time_critical`
## Upgrading to Icinga Web 2.12.2 ## Upgrading to Icinga Web 2.12.2
**Framework changes affecting third-party code** **Framework changes affecting third-party code**

View File

@ -58,8 +58,8 @@ Disabled by default.
If you experience any problems while running SELinux in enforcing mode try to reproduce it in permissive mode. If the If you experience any problems while running SELinux in enforcing mode try to reproduce it in permissive mode. If the
problem persists, it is not related to SELinux because in permissive mode SELinux will not deny anything. problem persists, it is not related to SELinux because in permissive mode SELinux will not deny anything.
When filing a bug report please add the following information additionally to the common ones: When filing a bug report please add the following information additionally to the
[common ones](https://icinga.com/icinga/faq/):
* Output of `semodule -l | grep -e icinga2 -e icingaweb2 -e nagios -e apache` * Output of `semodule -l | grep -e icinga2 -e icingaweb2 -e nagios -e apache`
* Output of `semanage boolean -l | grep icinga` * Output of `semanage boolean -l | grep icinga`
* Output of `ps -eZ | grep httpd` * Output of `ps -eZ | grep httpd`

View File

@ -41,7 +41,7 @@
<exclude-pattern>*/application/views/helpers/*</exclude-pattern> <exclude-pattern>*/application/views/helpers/*</exclude-pattern>
<exclude-pattern>*/library/Icinga/Web/Paginator/ScrollingStyle/SlidingWithBorder.php</exclude-pattern> <exclude-pattern>*/library/Icinga/Web/Paginator/ScrollingStyle/SlidingWithBorder.php</exclude-pattern>
</rule> </rule>
<rule ref="Squiz.Classes.ValidClassName.NotPascalCase"> <rule ref="Squiz.Classes.ValidClassName.NotCamelCaps">
<exclude-pattern>*/application/views/helpers/*</exclude-pattern> <exclude-pattern>*/application/views/helpers/*</exclude-pattern>
<exclude-pattern>*/library/Icinga/Web/Paginator/ScrollingStyle/SlidingWithBorder.php</exclude-pattern> <exclude-pattern>*/library/Icinga/Web/Paginator/ScrollingStyle/SlidingWithBorder.php</exclude-pattern>
</rule> </rule>

View File

@ -568,7 +568,7 @@ abstract class ApplicationBootstrap
*/ */
protected function setupErrorHandling() protected function setupErrorHandling()
{ {
error_reporting(getenv('ICINGAWEB_ENVIRONMENT') === 'dev' ? E_ALL : E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED); error_reporting(E_ALL | E_STRICT);
ini_set('display_startup_errors', 1); ini_set('display_startup_errors', 1);
ini_set('display_errors', 1); ini_set('display_errors', 1);
set_error_handler(function ($errno, $errstr, $errfile, $errline) { set_error_handler(function ($errno, $errstr, $errfile, $errline) {
@ -579,6 +579,7 @@ abstract class ApplicationBootstrap
switch ($errno) { switch ($errno) {
case E_NOTICE: case E_NOTICE:
case E_WARNING: case E_WARNING:
case E_STRICT:
case E_RECOVERABLE_ERROR: case E_RECOVERABLE_ERROR:
throw new ErrorException($errstr, 0, $errno, $errfile, $errline); throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
} }

View File

@ -8,7 +8,7 @@ namespace Icinga\Application;
*/ */
class Version class Version
{ {
const VERSION = '2.12.6'; const VERSION = '2.12.2';
/** /**
* Get the version of this instance of Icinga Web 2 * Get the version of this instance of Icinga Web 2

View File

@ -8,7 +8,7 @@ use Icinga\Web\Controller\StaticController;
use Icinga\Web\JavaScript; use Icinga\Web\JavaScript;
use Icinga\Web\StyleSheet; use Icinga\Web\StyleSheet;
error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED); error_reporting(E_ALL | E_STRICT);
if (isset($_SERVER['REQUEST_URI'])) { if (isset($_SERVER['REQUEST_URI'])) {
$ruri = $_SERVER['REQUEST_URI']; $ruri = $_SERVER['REQUEST_URI'];
@ -17,8 +17,8 @@ if (isset($_SERVER['REQUEST_URI'])) {
} }
// Workaround, PHPs internal Webserver seems to mess up SCRIPT_FILENAME // Workaround, PHPs internal Webserver seems to mess up SCRIPT_FILENAME
// as it prefixes its absolute path with DOCUMENT_ROOT // as it prefixes it's absolute path with DOCUMENT_ROOT
if (preg_match('/^PHP.*Development Server/', $_SERVER['SERVER_SOFTWARE'])) { if (preg_match('/^PHP .* Development Server/', $_SERVER['SERVER_SOFTWARE'])) {
$script = basename($_SERVER['SCRIPT_FILENAME']); $script = basename($_SERVER['SCRIPT_FILENAME']);
$_SERVER['PHP_SELF'] = $_SERVER['SCRIPT_NAME'] = '/' . $script; $_SERVER['PHP_SELF'] = $_SERVER['SCRIPT_NAME'] = '/' . $script;
$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'] $_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT']

View File

@ -6,7 +6,6 @@ namespace Icinga\Authentication;
use Exception; use Exception;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Application\Hook\AuditHook; use Icinga\Application\Hook\AuditHook;
use Icinga\Application\Hook\AuthenticationHook;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Authentication\User\ExternalBackend; use Icinga\Authentication\User\ExternalBackend;
@ -247,8 +246,6 @@ class Auth
$user->setDomain(Config::app()->get('authentication', 'default_domain')); $user->setDomain(Config::app()->get('authentication', 'default_domain'));
} }
$this->setAuthenticated($user); $this->setAuthenticated($user);
AuthenticationHook::triggerLogin($user);
return true; return true;
} }
} }

View File

@ -42,9 +42,4 @@ class RolesConfig extends IniRepository
return $columns; return $columns;
} }
protected function initializeSearchColumns(): array
{
return ['name'];
}
} }

View File

@ -11,7 +11,6 @@ use Icinga\Exception\AuthenticationException;
use Icinga\Repository\DbRepository; use Icinga\Repository\DbRepository;
use Icinga\User; use Icinga\User;
use PDO; use PDO;
use Zend_Db_Expr;
class DbUserBackend extends DbRepository implements UserBackendInterface, Inspectable class DbUserBackend extends DbRepository implements UserBackendInterface, Inspectable
{ {
@ -180,28 +179,23 @@ class DbUserBackend extends DbRepository implements UserBackendInterface, Inspec
{ {
if ($this->ds->getDbType() === 'pgsql') { if ($this->ds->getDbType() === 'pgsql') {
// Since PostgreSQL version 9.0 the default value for bytea_output is 'hex' instead of 'escape' // Since PostgreSQL version 9.0 the default value for bytea_output is 'hex' instead of 'escape'
$columns = ['password_hash' => new Zend_Db_Expr('ENCODE(password_hash, \'escape\')')]; $columns = array('password_hash' => 'ENCODE(password_hash, \'escape\')');
} else { } else {
// password_hash is intentionally not a valid query column, $columns = array('password_hash');
// by wrapping it in an expression it is not validated
$columns = ['password_hash' => new Zend_Db_Expr('password_hash')];
} }
$query = $this $nameColumn = 'name';
->select()
->from('user', $columns)
->where('active', true);
if ($this->ds->getDbType() === 'mysql') { if ($this->ds->getDbType() === 'mysql') {
$username = strtolower($username); $username = strtolower($username);
$nameColumn = new Zend_Db_Expr('BINARY LOWER(name)'); $nameColumn = 'BINARY LOWER(name)';
$query->getQuery()->where($nameColumn, $username);
} else { // pgsql
$query->where('user', $username);
} }
$statement = $this->ds->getDbAdapter()->prepare($query->getQuery()->getSelectQuery()); $query = $this->ds->select()
->from($this->prependTablePrefix('user'), $columns)
->where($nameColumn, $username)
->where('active', true);
$statement = $this->ds->getDbAdapter()->prepare($query->getSelectQuery());
$statement->execute(); $statement->execute();
$statement->bindColumn(1, $lob, PDO::PARAM_LOB); $statement->bindColumn(1, $lob, PDO::PARAM_LOB);
$statement->fetch(PDO::FETCH_BOUND); $statement->fetch(PDO::FETCH_BOUND);

View File

@ -204,7 +204,7 @@ class DbUserGroupBackend extends DbRepository implements Inspectable, UserGroupB
$membershipQuery = $this $membershipQuery = $this
->select() ->select()
->from('group_membership', array('group_name')) ->from('group_membership', array('group_name'))
->where('user', $user->getUsername()); ->where('user_name', $user->getUsername());
$memberships = array(); $memberships = array();
foreach ($membershipQuery as $membership) { foreach ($membershipQuery as $membership) {

View File

@ -37,7 +37,7 @@ class Csv
} }
$out = array(); $out = array();
foreach ($row as & $val) { foreach ($row as & $val) {
$out[] = '"' . ($val == '0' ? '0' : ($val ? str_replace('"', '""', $val) : '')) . '"'; $out[] = '"' . ($val ? str_replace('"', '""', $val) : '') . '"';
} }
$csv .= implode(',', $out) . "\r\n"; $csv .= implode(',', $out) . "\r\n";
} }

View File

@ -5,11 +5,9 @@ namespace Icinga\File;
use Dompdf\Dompdf; use Dompdf\Dompdf;
use Dompdf\Options; use Dompdf\Options;
use Exception;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
use Icinga\Util\Environment; use Icinga\Util\Environment;
use Icinga\Web\FileCache;
use Icinga\Web\Hook; use Icinga\Web\Hook;
use Icinga\Web\Url; use Icinga\Web\Url;
@ -66,17 +64,8 @@ class Pdf
return; return;
} }
$tmpDir = FileCache::instance()->directory('legacy_pdf');
if ($tmpDir === false) {
throw new Exception('Could not create temporary directory for PDF rendering');
}
$options = new Options(); $options = new Options();
$options->set('defaultPaperSize', 'A4'); $options->set('defaultPaperSize', 'A4');
$options->set('fontDir', $tmpDir);
$options->set('fontCache', $tmpDir);
$options->set('tempDir', $tmpDir);
$options->set('chroot', $tmpDir);
$dompdf = new Dompdf($options); $dompdf = new Dompdf($options);
$dompdf->loadHtml($html); $dompdf->loadHtml($html);
$dompdf->render(); $dompdf->render();

View File

@ -292,5 +292,22 @@ namespace Icinga\Test {
$adapter->exec('DROP TABLE ' . $table . ';'); $adapter->exec('DROP TABLE ' . $table . ';');
} }
} }
/**
* Add assertMatchesRegularExpression() method for phpunit >= 8.0 < 9.0 for compatibility with PHP 7.2.
*
* @TODO Remove once PHP 7.2 support is not needed for testing anymore.
*/
public static function assertMatchesRegularExpression(
string $pattern,
string $string,
string $message = ''
): void {
if (method_exists(parent::class, 'assertMatchesRegularExpression')) {
parent::assertMatchesRegularExpression($pattern, $string, $message);
} else {
static::assertRegExp($pattern, $string, $message);
}
}
} }
} }

View File

@ -3,8 +3,6 @@
namespace Icinga\Util; namespace Icinga\Util;
use DateTimeZone;
/** /**
* Retrieve timezone information from cookie * Retrieve timezone information from cookie
*/ */
@ -17,6 +15,13 @@ class TimezoneDetect
*/ */
private static $success; private static $success;
/**
* Timezone offset in minutes
*
* @var int
*/
private static $offset = 0;
/** /**
* @var string * @var string
*/ */
@ -29,6 +34,13 @@ class TimezoneDetect
*/ */
public static $cookieName = 'icingaweb2-tzo'; public static $cookieName = 'icingaweb2-tzo';
/**
* Timezone name
*
* @var string
*/
private static $timezone;
/** /**
* Create new object and try to identify the timezone * Create new object and try to identify the timezone
*/ */
@ -38,13 +50,30 @@ class TimezoneDetect
return; return;
} }
if (in_array($_COOKIE[self::$cookieName] ?? null, DateTimeZone::listIdentifiers(), true)) { if (array_key_exists(self::$cookieName, $_COOKIE)) {
self::$timezoneName = $_COOKIE[self::$cookieName]; $matches = array();
self::$success = true; if (preg_match('/\A(-?\d+)[\-,](\d+)\z/', $_COOKIE[self::$cookieName], $matches)) {
} else { $offset = $matches[1];
self::$success = false; $timezoneName = timezone_name_from_abbr('', (int) $offset, (int) $matches[2]);
self::$success = (bool) $timezoneName;
if (self::$success) {
self::$offset = $offset;
self::$timezoneName = $timezoneName;
} }
} }
}
}
/**
* Get offset
*
* @return int
*/
public function getOffset()
{
return self::$offset;
}
/** /**
* Get timezone name * Get timezone name
@ -73,5 +102,6 @@ class TimezoneDetect
{ {
self::$success = null; self::$success = null;
self::$timezoneName = null; self::$timezoneName = null;
self::$offset = 0;
} }
} }

View File

@ -352,7 +352,7 @@ class Form extends Zend_Form
* *
* @return $this * @return $this
* *
* @throws ProgrammingError In case $url is neither a string nor an instance of Icinga\Web\Url * @throws ProgrammingError In case $url is neither a string nor a instance of Icinga\Web\Url
*/ */
public function setRedirectUrl($url) public function setRedirectUrl($url)
{ {

View File

@ -6,7 +6,6 @@ namespace Icinga\Web;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Authentication\Auth; use Icinga\Authentication\Auth;
use Icinga\Web\Navigation\Navigation; use Icinga\Web\Navigation\Navigation;
use Icinga\Web\Navigation\Renderer\RecursiveMenuNavigationRenderer;
/** /**
* Main menu for Icinga Web 2 * Main menu for Icinga Web 2
@ -150,14 +149,4 @@ class Menu extends Navigation
])); ]));
} }
} }
/**
* Create and return the renderer for this navigation
*
* @return RecursiveMenuNavigationRenderer
*/
public function getRenderer()
{
return new RecursiveMenuNavigationRenderer($this);
}
} }

View File

@ -7,7 +7,6 @@ use Icinga\Application\Icinga;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
use Icinga\Util\StringHelper; use Icinga\Util\StringHelper;
use Icinga\Web\Navigation\NavigationItem; use Icinga\Web\Navigation\NavigationItem;
use Icinga\Web\Session;
use Icinga\Web\Url; use Icinga\Web\Url;
use Icinga\Web\View; use Icinga\Web\View;
@ -191,10 +190,6 @@ class NavigationItemRenderer
$target = $item->getTarget(); $target = $item->getTarget();
if ($url->isExternal() && (!$target || in_array($target, $this->internalLinkTargets, true))) { if ($url->isExternal() && (!$target || in_array($target, $this->internalLinkTargets, true))) {
$item->setAttribute('data-url-hash', hash(
'sha256',
$url->getAbsoluteUrl() . Session::getSession()->getId()
));
$url = Url::fromPath('iframe', array('url' => $url)); $url = Url::fromPath('iframe', array('url' => $url));
} }

View File

@ -1,30 +0,0 @@
<?php
/* Icinga Web 2 | (c) 2025 Icinga GmbH | GPLv2+ */
namespace Icinga\Web\Navigation\Renderer;
use Icinga\Web\Navigation\NavigationItem;
/**
* Renderer for the multi level navigation in the sidebar menu
*/
class RecursiveMenuNavigationRenderer extends RecursiveNavigationRenderer
{
public function beginChildren(): void
{
parent::beginChildren();
$parentItem = $this->getInnerIterator()->current()->getParent();
$item = new NavigationItem($parentItem->getName());
$item->setLabel($parentItem->getLabel());
$item->setCssClass('nav-item-header');
$renderer = new NavigationItemRenderer();
$renderer->setEscapeLabel(false);
$content = $renderer->render($item);
$this->content[] = $this->getInnerIterator()->beginItemMarkup($item);
$this->content[] = $content;
$this->content[] = $this->getInnerIterator()->endItemMarkup();
}
}

View File

@ -4,10 +4,11 @@
namespace Icinga\Web\Navigation\Renderer; namespace Icinga\Web\Navigation\Renderer;
use Exception; use Exception;
use RecursiveIteratorIterator;
use Icinga\Exception\IcingaException; use Icinga\Exception\IcingaException;
use Icinga\Web\Navigation\Navigation; use Icinga\Web\Navigation\Navigation;
use Icinga\Web\Navigation\NavigationItem; use Icinga\Web\Navigation\NavigationItem;
use RecursiveIteratorIterator; use Icinga\Web\Navigation\Renderer\NavigationItemRenderer;
/** /**
* Renderer for multi level navigation * Renderer for multi level navigation

View File

@ -139,7 +139,6 @@ class StyleSheet
$this->lessCompiler->addLessFile($this->pubPath . '/' . $lessFile); $this->lessCompiler->addLessFile($this->pubPath . '/' . $lessFile);
} }
$auth = Auth::getInstance();
$mm = $this->app->getModuleManager(); $mm = $this->app->getModuleManager();
foreach ($mm->getLoadedModules() as $moduleName => $module) { foreach ($mm->getLoadedModules() as $moduleName => $module) {
@ -158,6 +157,7 @@ class StyleSheet
} }
if (! (bool) $themingConfig->get('disabled', false)) { if (! (bool) $themingConfig->get('disabled', false)) {
$auth = Auth::getInstance();
if ($auth->isAuthenticated()) { if ($auth->isAuthenticated()) {
$userTheme = $auth->getUser()->getPreferences()->getValue('icingaweb', 'theme'); $userTheme = $auth->getUser()->getPreferences()->getValue('icingaweb', 'theme');
if ($userTheme !== null) { if ($userTheme !== null) {
@ -174,7 +174,7 @@ class StyleSheet
Logger::warning(sprintf( Logger::warning(sprintf(
'Theme "%s" set by user "%s" has not been found.', 'Theme "%s" set by user "%s" has not been found.',
$theme, $theme,
$auth->isAuthenticated() ? $auth->getUser()->getUsername() : 'anonymous' ($user = Auth::getInstance()->getUser()) !== null ? $user->getUsername() : 'anonymous'
)); ));
} }
} }
@ -184,10 +184,10 @@ class StyleSheet
} }
$mode = 'none'; $mode = 'none';
if ($auth->isAuthenticated()) { if ($user = Auth::getInstance()->getUser()) {
$file = $themePath !== null ? @file_get_contents($themePath) : false; $file = $themePath !== null ? @file_get_contents($themePath) : false;
if (! $file || strpos($file, self::LIGHT_MODE_IDENTIFIER) !== false) { if (! $file || strpos($file, self::LIGHT_MODE_IDENTIFIER) !== false) {
$mode = $auth->getUser()->getPreferences()->getValue('icingaweb', 'theme_mode', self::DEFAULT_MODE); $mode = $user->getPreferences()->getValue('icingaweb', 'theme_mode', self::DEFAULT_MODE);
} }
} }

View File

@ -179,9 +179,10 @@ class Url
} }
$urlParts = parse_url($url); $urlParts = parse_url($url);
if ((isset($urlParts['scheme']) && $urlParts['scheme'] !== $request->getScheme()) if (isset($urlParts['scheme']) && (
$urlParts['scheme'] !== $request->getScheme()
|| (isset($urlParts['host']) && $urlParts['host'] !== $request->getServer('SERVER_NAME')) || (isset($urlParts['host']) && $urlParts['host'] !== $request->getServer('SERVER_NAME'))
|| (isset($urlParts['port']) && $urlParts['port'] != $request->getServer('SERVER_PORT')) || (isset($urlParts['port']) && $urlParts['port'] != $request->getServer('SERVER_PORT')))
) { ) {
$urlObject->setIsExternal(); $urlObject->setIsExternal();
} }

View File

@ -205,8 +205,7 @@ class View extends Zend_View_Abstract
'th-thumb-empty' => true, 'th-thumb-empty' => true,
'github-circled' => true, 'github-circled' => true,
'history' => true, 'history' => true,
'binoculars' => true, 'binoculars' => true
'letter' => true
//</editor-fold> //</editor-fold>
]; ];

View File

@ -3,9 +3,9 @@
namespace Icinga\Web\Widget\Dashboard; namespace Icinga\Web\Widget\Dashboard;
use Icinga\Web\Session;
use Icinga\Web\Url; use Icinga\Web\Url;
use Icinga\Data\ConfigObject; use Icinga\Data\ConfigObject;
use Icinga\Exception\IcingaException;
/** /**
* A dashboard pane dashlet * A dashboard pane dashlet
@ -57,15 +57,18 @@ class Dashlet extends UserWidget
*/ */
private $template =<<<'EOD' private $template =<<<'EOD'
<div class="container" data-icinga-url="{URL}" data-url-hash="{URL_HASH}"> <div class="container" data-icinga-url="{URL}">
<h1><a <h1><a href="{FULL_URL}" aria-label="{TOOLTIP}" title="{TOOLTIP}" data-base-target="col1">{TITLE}</a></h1>
href="{FULL_URL}"
aria-label="{TOOLTIP}"
title="{TOOLTIP}"
data-url-hash="{FULL_URL_HASH}"
data-base-target="col1"
>{TITLE}</a></h1>
<p class="progress-label">{PROGRESS_LABEL}<span>.</span><span>.</span><span>.</span></p> <p class="progress-label">{PROGRESS_LABEL}<span>.</span><span>.</span><span>.</span></p>
<noscript>
<div class="iframe-container">
<iframe
src="{IFRAME_URL}"
frameborder="no"
title="{TITLE_PREFIX}{TITLE}">
</iframe>
</div>
</noscript>
</div> </div>
EOD; EOD;
@ -247,22 +250,13 @@ EOD;
$url = $this->getUrl(); $url = $this->getUrl();
$url->setParam('showCompact', true); $url->setParam('showCompact', true);
$fullUrl = $url->getUrlWithout(['showCompact', 'limit', 'view']); $iframeUrl = clone $url;
$iframeUrl->setParam('isIframe');
$urlHash = '';
$fullUrlHash = '';
if ($url->getPath() === 'iframe') {
$urlHash = hash('sha256', Url::fromPath($url->getParam('url'))->getAbsoluteUrl()
. Session::getSession()->getId());
$fullUrlHash = hash('sha256', Url::fromPath($fullUrl->getParam('url'))->getAbsoluteUrl()
. Session::getSession()->getId());
}
$searchTokens = array( $searchTokens = array(
'{URL}', '{URL}',
'{URL_HASH}', '{IFRAME_URL}',
'{FULL_URL}', '{FULL_URL}',
'{FULL_URL_HASH}',
'{TOOLTIP}', '{TOOLTIP}',
'{TITLE}', '{TITLE}',
'{TITLE_PREFIX}', '{TITLE_PREFIX}',
@ -271,9 +265,8 @@ EOD;
$replaceTokens = array( $replaceTokens = array(
$url, $url,
$urlHash, $iframeUrl,
$fullUrl, $url->getUrlWithout(['showCompact', 'limit', 'view']),
$fullUrlHash,
sprintf($view->translate('Show %s', 'dashboard.dashlet.tooltip'), $view->escape($this->getTitle())), sprintf($view->translate('Show %s', 'dashboard.dashlet.tooltip'), $view->escape($this->getTitle())),
$view->escape($this->getTitle()), $view->escape($this->getTitle()),
$view->translate('Dashlet') . ': ', $view->translate('Dashlet') . ': ',

View File

@ -3,17 +3,17 @@
namespace Icinga\Web\Widget; namespace Icinga\Web\Widget;
use Exception;
use Icinga\Application\Icinga;
use Icinga\Data\Filter\Filter;
use Icinga\Data\Filter\FilterChain;
use Icinga\Data\Filter\FilterExpression;
use Icinga\Data\Filter\FilterOr;
use Icinga\Data\Filterable; use Icinga\Data\Filterable;
use Icinga\Data\FilterColumns; use Icinga\Data\FilterColumns;
use Icinga\Data\Filter\Filter;
use Icinga\Data\Filter\FilterExpression;
use Icinga\Data\Filter\FilterChain;
use Icinga\Data\Filter\FilterOr;
use Icinga\Web\Url;
use Icinga\Application\Icinga;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
use Icinga\Web\Notification; use Icinga\Web\Notification;
use Icinga\Web\Url; use Exception;
/** /**
* Filter * Filter
@ -738,8 +738,7 @@ class FilterEditor extends AbstractWidget
$html = ' <form method="post" class="search inline" action="' $html = ' <form method="post" class="search inline" action="'
. $preservedUrl . $preservedUrl
. '"><i class="icon fa-search fa search-icon"></i>' . '"><input type="text" name="q" class="search search-input" value="" placeholder="'
. '<input type="text" name="q" class="search search-input" value="" placeholder="'
. t('Search...') . t('Search...')
. '" /></form>'; . '" /></form>';

View File

@ -10,7 +10,6 @@ use ipl\Html\FormElement\InputElement;
use ipl\Html\HtmlElement; use ipl\Html\HtmlElement;
use ipl\Web\Control\SearchBar\Suggestions; use ipl\Web\Control\SearchBar\Suggestions;
use ipl\Web\Url; use ipl\Web\Url;
use ipl\Web\Widget\Icon;
class SingleValueSearchControl extends Form class SingleValueSearchControl extends Form
{ {
@ -107,8 +106,6 @@ class SingleValueSearchControl extends Form
{ {
$suggestionsId = Icinga::app()->getRequest()->protectId('single-value-suggestions'); $suggestionsId = Icinga::app()->getRequest()->protectId('single-value-suggestions');
$this->addHtml(new Icon('search', Attributes::create(['class' => 'search-icon'])));
$this->addElement( $this->addElement(
'text', 'text',
$this->searchParameter, $this->searchParameter,

View File

@ -112,7 +112,7 @@ class Window
{ {
if (! isset(static::$window)) { if (! isset(static::$window)) {
$id = Icinga::app()->getRequest()->getHeader('X-Icinga-WindowId'); $id = Icinga::app()->getRequest()->getHeader('X-Icinga-WindowId');
if (empty($id) || $id === static::UNDEFINED || ! preg_match('/^\w+$/', $id)) { if (empty($id) || $id === static::UNDEFINED) {
Icinga::app()->getResponse()->setOverrideWindowId(); Icinga::app()->getResponse()->setOverrideWindowId();
$id = static::generateId(); $id = static::generateId();
} }

View File

@ -1,4 +1,4 @@
Module: doc Module: doc
Version: 2.12.6 Version: 2.12.2
Description: Documentation module Description: Documentation module
Extracts, shows and exports documentation for Icinga Web 2 and its modules. Extracts, shows and exports documentation for Icinga Web 2 and its modules.

View File

@ -1,5 +1,17 @@
/*! Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */ /*! Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
// Mixins
.gradient(@a: @gray-lighter; @b: @gray-lightest) {
background: @a;
background: -webkit-gradient(linear, left top, left bottom, from(@a), to(@b));
background: -webkit-linear-gradient(top, @a, @b);
background: -moz-linear-gradient(top, @a, @b);
background: -ms-linear-gradient(top, @a, @b);
background: -o-linear-gradient(top, @a, @b);
background: linear-gradient(to bottom, @a, @b);
}
// General styles // General styles
code { code {
@ -72,7 +84,7 @@ table {
} }
tbody > tr:nth-child(odd) { tbody > tr:nth-child(odd) {
background: @gray-light; .gradient()
} }
tbody > tr:nth-child(even) { tbody > tr:nth-child(even) {

View File

@ -1,5 +1,5 @@
Module: migrate Module: migrate
Version: 2.12.6 Version: 2.12.2
Description: Migrate module Description: Migrate module
This module was introduced with the domain-aware authentication feature in version 2.5.0. This module was introduced with the domain-aware authentication feature in version 2.5.0.
It helps you migrating users and user configurations according to a given domain. It helps you migrating users and user configurations according to a given domain.

View File

@ -0,0 +1,400 @@
<?php
/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Clicommands;
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
use Icinga\Module\Monitoring\Cli\CliUtils;
use Icinga\Date\DateFormatter;
use Icinga\Cli\Command;
use Icinga\File\Csv;
use Icinga\Module\Monitoring\Plugin\PerfdataSet;
use Exception;
use Icinga\Util\Json;
/**
* Icinga monitoring objects
*
* This module is your interface to the Icinga monitoring application.
*/
class ListCommand extends Command
{
protected $backend;
protected $dumpSql;
protected $defaultActionName = 'status';
public function init()
{
$this->backend = MonitoringBackend::instance($this->params->shift('backend'));
$this->dumpSql = $this->params->shift('showsql');
}
protected function getQuery($table, $columns)
{
$limit = $this->params->shift('limit');
$format = $this->params->shift('format');
if ($format !== null) {
if ($this->params->has('columns')) {
$columnParams = preg_split(
'/,/',
$this->params->shift('columns')
);
$columns = array();
foreach ($columnParams as $col) {
if (false !== ($pos = strpos($col, '='))) {
$columns[substr($col, 0, $pos)] = substr($col, $pos + 1);
} else {
$columns[] = $col;
}
}
}
}
$query = $this->backend->select()->from($table, $columns);
if ($limit) {
$query->limit($limit, $this->params->shift('offset'));
}
foreach ($this->params->getParams() as $col => $filter) {
$query->where($col, $filter);
}
// $query->applyFilters($this->params->getParams());
if ($this->dumpSql) {
echo wordwrap($query->dump(), 72);
exit;
}
if ($format !== null) {
$this->showFormatted($query, $format, $columns);
}
return $query;
}
protected function showFormatted($query, $format, $columns)
{
$query = $query->getQuery();
switch ($format) {
case 'json':
echo Json::sanitize($query->fetchAll());
break;
case 'csv':
Csv::fromQuery($query)->dump();
break;
default:
preg_match_all('~\$([a-z0-9_-]+)\$~', $format, $m);
$words = array();
foreach ($columns as $key => $col) {
if (is_numeric($key)) {
if (in_array($col, $m[1])) {
$words[] = $col;
}
} else {
if (in_array($key, $m[1])) {
$words[] = $key;
}
}
}
foreach ($query->fetchAll() as $row) {
$output = $format;
foreach ($words as $word) {
$output = preg_replace(
'~\$' . $word . '\$~',
$row->{$word},
$output
);
}
echo $output . "\n";
}
}
exit;
}
/**
* List and filter hosts
*
* This command allows you to search and visualize your hosts in
* different ways.
*
* USAGE
*
* icingacli monitoring list hosts [options]
*
* OPTIONS
*
* --verbose Show detailled output
* --showsql Dump generated SQL query (DB backend only)
*
* --format=<csv|json|<custom>>
* Dump columns in the given format. <custom> format allows $column$
* placeholders, e.g. --format='$host$: $service$'. This requires
* that the columns are specified within the --columns parameter.
*
* --<column>[=filter]
* Filter given column by optional filter. Boolean (1/0) columns are
* true if no filter value is given.
*
* --problems
* Only show unhandled problems (HARD state and not acknowledged/in downtime).
*
* --columns='<comma separated list of host/service columns>'
* Add a limited set of columns to the output. The following host
* attributes can be fetched: state, handled, output, acknowledged, in_downtime, perfdata last_state_change
*
* EXAMPLES
*
* icingacli monitoring list hosts --problems
* icingacli monitoring list hosts --problems --host_state_type 0
* icingacli monitoring list hosts --host=local*
* icingacli monitoring list hosts --columns 'host,host_output' \
* --format='$host$ ($host_output$)'
*/
public function hostsAction()
{
$columns = array(
'host_name',
'host_state',
'host_output',
'host_handled',
'host_acknowledged',
'host_in_downtime'
);
$query = $this->getQuery('hoststatus', $columns)
->order('host_name');
echo $this->renderStatusQuery($query);
}
/**
* List and filter services
*
* This command allows you to search and visualize your services in
* different ways.
*
* USAGE
*
* icingacli monitoring list services [options]
*
* OPTIONS
*
* --verbose Show detailled output
* --showsql Dump generated SQL query (DB backend only)
*
* --format=<csv|json|<custom>>
* Dump columns in the given format. <custom> format allows $column$
* placeholders, e.g. --format='$host$: $service$'. This requires
* that the columns are specified within the --columns parameter.
*
* --<column>[=filter]
* Filter given column by optional filter. Boolean (1/0) columns are
* true if no filter value is given.
*
* --problems
* Only show unhandled problems (HARD state and not acknowledged/in downtime).
*
* --columns='<comma separated list of host/service columns>'
* Add a limited set of columns to the output. The following service
* attributes can be fetched: state, handled, output, acknowledged, in_downtime, perfdata last_state_change
*
* EXAMPLES
*
* icingacli monitoring list services --problems
* icingacli monitoring list services --problems --service_state_type 0
* icingacli monitoring list services --host=local* --service=*disk*
* icingacli monitoring list services --columns 'host,service,service_output' \
* --format='$host$: $service$ ($service_output$)'
*/
public function servicesAction()
{
$columns = array(
'host_name',
'host_state',
'host_output',
'host_handled',
'host_acknowledged',
'host_in_downtime',
'service_description',
'service_state',
'service_acknowledged',
'service_in_downtime',
'service_handled',
'service_output',
'service_perfdata',
'service_last_state_change'
);
$query = $this->getQuery('servicestatus', $columns)
->order('host_name');
echo $this->renderStatusQuery($query);
}
protected function renderStatusQuery($query)
{
$out = '';
$last_host = null;
$screen = $this->screen;
$utils = new CliUtils($screen);
$maxCols = $screen->getColumns();
$query = $query->getQuery();
$rows = $query->fetchAll();
$count = $query->count();
$count = count($rows);
for ($i = 0; $i < $count; $i++) {
$row = & $rows[$i];
$utils->setHostState($row->host_state);
if (! array_key_exists($i + 1, $rows)
|| $row->host_name !== $rows[$i + 1]->host_name
) {
$lastService = true;
} else {
$lastService = false;
}
$hostUnhandled = ! ($row->host_state == 0 || $row->host_handled);
if ($row->host_name !== $last_host) {
if (isset($row->service_description)) {
$out .= "\n";
}
$hostTxt = $utils->shortHostState();
if ($hostUnhandled) {
$out .= $utils->hostStateBackground(
sprintf(' %s ', $utils->shortHostState())
);
} else {
$out .= sprintf(
'%s %s ',
$utils->hostStateBackground(' '),
$utils->shortHostState()
);
}
$out .= sprintf(
" %s%s: %s\n",
$screen->underline($row->host_name),
$screen->colorize($utils->objectStateFlags('host', $row), 'lightblue'),
$row->host_output
);
if (isset($row->services_ok)) {
$out .= sprintf(
"%d services, %d problems (%d unhandled), %d OK\n",
$row->services_cnt,
$row->services_problem,
$row->services_problem_unhandled,
$row->services_ok
);
}
}
$last_host = $row->host_name;
if (! isset($row->service_description)) {
continue;
}
$utils->setServiceState($row->service_state);
$serviceUnhandled = ! (
$row->service_state == 0 || $row->service_handled
);
if ($lastService) {
$straight = ' ';
$leaf = '└';
} else {
$straight = '│';
$leaf = '├';
}
$out .= $utils->hostStateBackground(' ');
if ($serviceUnhandled) {
$out .= $utils->serviceStateBackground(
sprintf(' %s ', $utils->shortServiceState())
);
$emptyBg = ' ';
$emptySpace = '';
} else {
$out .= sprintf(
'%s %s ',
$utils->serviceStateBackground(' '),
$utils->shortServiceState()
);
$emptyBg = ' ';
$emptySpace = ' ';
}
$emptyLine = "\n"
. $utils->hostStateBackground(' ')
. $utils->serviceStateBackground($emptyBg)
. $emptySpace
. ' ' . $straight . ' ';
$perf = '';
try {
$pset = PerfdataSet::fromString($row->service_perfdata);
$perfs = array();
foreach ($pset as $p) {
if ($percent = $p->getPercentage()) {
if ($percent < 0 || $percent > 100) {
continue;
}
$perfs[] = ' '
. $p->getLabel()
. ': '
. $this->getPercentageSign($percent)
. ' '
. number_format($percent, 2, ',', '.')
. '%';
}
}
if (! empty($perfs)) {
$perf = ', ' . implode($perfs);
}
// TODO: fix wordwarp, then remove this line:
$perf = '';
} catch (Exception $e) {
// Ignoring perfdata errors right now, we could show some hint
}
$wrappedOutput = wordwrap(
preg_replace('~\@{3,}~', '@@@', $row->service_output),
$maxCols - 13
) . "\n";
$out .= sprintf(
" %1s─ %s%s (%s)",
$leaf,
$screen->underline($row->service_description),
$screen->colorize($utils->objectStateFlags('service', $row) . $perf, 'lightblue'),
ucfirst(DateFormatter::timeSince($row->service_last_state_change))
);
if ($this->isVerbose) {
$out .= $emptyLine . preg_replace(
'/\n/',
$emptyLine,
$wrappedOutput
) . "\n";
} else {
$out .= "\n";
}
}
$out .= "\n";
return $out;
}
protected function getPercentageSign($percent)
{
$circles = array(
0 => '○',
15 => '◔',
40 => '◑',
65 => '◕',
90 => '●',
);
$last = $circles[0];
foreach ($circles as $cur => $circle) {
if ($percent < $cur) {
return $last;
}
$last = $circle;
}
}
}

View File

@ -0,0 +1,58 @@
<?php
/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Clicommands;
use Icinga\Protocol\Nrpe\Connection;
use Icinga\Cli\Command;
use Exception;
/**
* NRPE
*/
class NrpeCommand extends Command
{
protected $defaultActionName = 'check';
/**
* Execute an NRPE command
*
* This command will execute an NRPE check, fire it against the given host
* and also pass through all your parameters. Output will be shown, exit
* code respected.
*
* USAGE
*
* icingacli monitoring nrpe <host> <command> [--ssl] [nrpe options]
*
* EXAMPLE
*
* icingacli monitoring nrpe 127.0.0.1 CheckMEM --ssl --MaxWarn=80% \
* --MaxCrit=90% --type=physical
*/
public function checkAction()
{
$host = $this->params->shift();
if (! $host) {
echo $this->showUsage();
exit(3);
}
$command = $this->params->shift(null, '_NRPE_CHECK');
$port = $this->params->shift('port', 5666);
try {
$nrpe = new Connection($host, $port);
if ($this->params->shift('ssl')) {
$nrpe->useSsl();
}
$args = array();
foreach ($this->params->getParams() as $k => $v) {
$args[] = $k . '=' . $v;
}
echo $nrpe->sendCommand($command, $args) . "\n";
exit($nrpe->getLastReturnCode());
} catch (Exception $e) {
echo $e->getMessage() . "\n";
exit(3);
}
}
}

View File

@ -0,0 +1,135 @@
<?php
/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Data\Filter\Filter;
use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimesCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostDowntimeCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceDowntimeCommandForm;
use Icinga\Module\Monitoring\Object\HostList;
use Icinga\Module\Monitoring\Object\ServiceList;
/**
* Monitoring API
*/
class ActionsController extends Controller
{
/**
* Get the filter from URL parameters or exit immediately if the filter is empty
*
* @return Filter
*/
protected function getFilterOrExitIfEmpty()
{
$filter = Filter::fromQueryString((string) $this->params);
if ($filter->isEmpty()) {
$this->getResponse()->json()
->setFailData(array('filter' => 'Filter is required and must not be empty'))
->sendResponse();
}
return $filter;
}
/**
* Schedule host downtimes
*/
public function scheduleHostDowntimeAction()
{
$filter = $this->getFilterOrExitIfEmpty();
$hostList = new HostList($this->backend);
$hostList
->applyFilter($this->getRestriction('monitoring/filter/objects'))
->applyFilter($filter);
if (! $hostList->count()) {
$this->getResponse()->json()
->setFailData(array('filter' => 'No hosts found matching the filter'))
->sendResponse();
}
$form = new ScheduleHostDowntimeCommandForm();
$form
->setIsApiTarget(true)
->setBackend($this->backend)
->setObjects($hostList->fetch())
->handleRequest($this->getRequest());
}
/**
* Remove host downtimes
*/
public function removeHostDowntimeAction()
{
$filter = $this->getFilterOrExitIfEmpty();
$downtimes = $this->backend
->select()
->from('downtime', array('host_name', 'id' => 'downtime_internal_id', 'name' => 'downtime_name'))
->where('object_type', 'host')
->applyFilter($this->getRestriction('monitoring/filter/objects'))
->applyFilter($filter);
if (! $downtimes->count()) {
$this->getResponse()->json()
->setFailData(array('filter' => 'No downtimes found matching the filter'))
->sendResponse();
}
$form = new DeleteDowntimesCommandForm();
$form
->setIsApiTarget(true)
->setDowntimes($downtimes->fetchAll())
->handleRequest($this->getRequest());
// @TODO(el): Respond w/ the downtimes deleted instead of the notifiaction added by
// DeleteDowntimesCommandForm::onSuccess().
}
/**
* Schedule service downtimes
*/
public function scheduleServiceDowntimeAction()
{
$filter = $this->getFilterOrExitIfEmpty();
$serviceList = new ServiceList($this->backend);
$serviceList
->applyFilter($this->getRestriction('monitoring/filter/objects'))
->applyFilter($filter);
if (! $serviceList->count()) {
$this->getResponse()->json()
->setFailData(array('filter' => 'No services found matching the filter'))
->sendResponse();
}
$form = new ScheduleServiceDowntimeCommandForm();
$form
->setIsApiTarget(true)
->setBackend($this->backend)
->setObjects($serviceList->fetch())
->handleRequest($this->getRequest());
}
/**
* Remove service downtimes
*/
public function removeServiceDowntimeAction()
{
$filter = $this->getFilterOrExitIfEmpty();
$downtimes = $this->backend
->select()
->from(
'downtime',
array('host_name', 'service_description', 'id' => 'downtime_internal_id', 'name' => 'downtime_name')
)
->where('object_type', 'service')
->applyFilter($this->getRestriction('monitoring/filter/objects'))
->applyFilter($filter);
if (! $downtimes->count()) {
$this->getResponse()->json()
->setFailData(array('filter' => 'No downtimes found matching the filter'))
->sendResponse();
}
$form = new DeleteDowntimesCommandForm();
$form
->setIsApiTarget(true)
->setDowntimes($downtimes->fetchAll())
->handleRequest($this->getRequest());
// @TODO(el): Respond w/ the downtimes deleted instead of the notifiaction added by
// DeleteDowntimesCommandForm::onSuccess().
}
}

View File

@ -0,0 +1,91 @@
<?php
/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Application\Hook;
use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentCommandForm;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\MenuAction;
/**
* Display detailed information about a comment
*/
class CommentController extends Controller
{
/**
* The fetched comment
*
* @var object
*/
protected $comment;
/**
* Fetch the first comment with the given id and add tabs
*/
public function init()
{
$commentId = $this->params->getRequired('comment_id');
$query = $this->backend->select()->from('comment', array(
'id' => 'comment_internal_id',
'objecttype' => 'object_type',
'comment' => 'comment_data',
'author' => 'comment_author_name',
'timestamp' => 'comment_timestamp',
'type' => 'comment_type',
'persistent' => 'comment_is_persistent',
'expiration' => 'comment_expiration',
'name' => 'comment_name',
'host_name',
'service_description',
'host_display_name',
'service_display_name'
))->where('comment_internal_id', $commentId);
$this->applyRestriction('monitoring/filter/objects', $query);
if (false === $this->comment = $query->fetchRow()) {
$this->httpNotFound($this->translate('Comment not found'));
}
$this->getTabs()->add(
'comment',
array(
'icon' => 'comment-empty',
'label' => $this->translate('Comment'),
'title' => $this->translate('Display detailed information about a comment.'),
'url' =>'monitoring/comments/show'
)
)->activate('comment')->extend(new DashboardAction())->extend(new MenuAction());
if (Hook::has('ticket')) {
$this->view->tickets = Hook::first('ticket');
}
}
/**
* Display comment detail view
*/
public function showAction()
{
$this->view->comment = $this->comment;
$this->view->title = $this->translate('Comments');
if ($this->hasPermission('monitoring/command/comment/delete')) {
$listUrl = Url::fromPath('monitoring/list/comments')
->setQueryString('comment_type=comment|comment_type=ack');
$form = new DeleteCommentCommandForm();
$form
->populate(array(
'comment_id' => $this->comment->id,
'comment_is_service' => isset($this->comment->service_description),
'comment_name' => $this->comment->name,
'redirect' => $listUrl
))
->handleRequest();
$this->view->delCommentForm = $form;
}
}
}

View File

@ -0,0 +1,108 @@
<?php
/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Data\Filter\Filter;
use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentsCommandForm;
use Icinga\Web\Url;
/**
* Display detailed information about comments
*/
class CommentsController extends Controller
{
/**
* The comments view
*
* @var \Icinga\Module\Monitoring\DataView\Comment
*/
protected $comments;
/**
* Filter from request
*
* @var Filter
*/
protected $filter;
/**
* Fetch all comments matching the current filter and add tabs
*/
public function init()
{
$this->filter = Filter::fromQueryString(str_replace(
'comment_id',
'comment_internal_id',
(string) $this->params
));
$query = $this->backend->select()->from('comment', array(
'id' => 'comment_internal_id',
'objecttype' => 'object_type',
'comment' => 'comment_data',
'author' => 'comment_author_name',
'timestamp' => 'comment_timestamp',
'type' => 'comment_type',
'persistent' => 'comment_is_persistent',
'expiration' => 'comment_expiration',
'name' => 'comment_name',
'host_name',
'service_description',
'host_display_name',
'service_display_name'
))->addFilter($this->filter);
$this->applyRestriction('monitoring/filter/objects', $query);
$this->comments = $query;
$this->view->title = $this->translate('Comments');
$this->getTabs()->add(
'comments',
array(
'icon' => 'comment-empty',
'label' => $this->translate('Comments') . sprintf(' (%d)', $query->count()),
'title' => $this->translate(
'Display detailed information about multiple comments.'
),
'url' =>'monitoring/comments/show'
)
)->activate('comments');
}
/**
* Display the detail view for a comment list
*/
public function showAction()
{
$this->view->comments = $this->comments;
$this->view->listAllLink = Url::fromPath('monitoring/list/comments')
->setQueryString($this->filter->toQueryString());
$this->view->removeAllLink = Url::fromPath('monitoring/comments/delete-all')
->setParams($this->params);
}
/**
* Display the form for removing a comment list
*/
public function deleteAllAction()
{
$this->assertPermission('monitoring/command/comment/delete');
$listCommentsLink = Url::fromPath('monitoring/list/comments')
->setQueryString('comment_type=(comment|ack)');
$delCommentForm = new DeleteCommentsCommandForm();
$delCommentForm->setTitle($this->view->translate('Remove all Comments'));
$delCommentForm->addDescription(sprintf(
$this->translate('Confirm removal of %d comments.'),
$this->comments->count()
));
$delCommentForm->setComments($this->comments->fetchAll())
->setRedirectUrl($listCommentsLink)
->handleRequest();
$this->view->delCommentForm = $delCommentForm;
$this->view->comments = $this->comments;
$this->view->listAllLink = Url::fromPath('monitoring/list/comments')
->setQueryString($this->filter->toQueryString());
}
}

View File

@ -0,0 +1,298 @@
<?php
/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Exception;
use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfirmRemovalForm;
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
use Icinga\Module\Monitoring\Forms\Config\TransportReorderForm;
use Icinga\Web\Controller;
use Icinga\Web\Notification;
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
*/
class ConfigController extends Controller
{
/**
* {@inheritdoc}
*/
public function init()
{
$this->assertPermission('config/modules');
$this->view->title = $this->translate('Backends');
$this->view->defaultTitle = 'monitoring :: ' . $this->view->defaultTitle;
parent::init();
}
/**
* Display a list of available backends and command transports
*/
public function indexAction()
{
$this->view->commandTransportReorderForm = $form = new TransportReorderForm();
$form->handleRequest();
$this->view->backendsConfig = $this->Config('backends');
$this->view->tabs = $this->Module()->getConfigTabs()->activate('backends');
}
/**
* Edit a monitoring backend
*/
public function editbackendAction()
{
$backendName = $this->params->getRequired('backend-name');
$form = new BackendConfigForm();
$form->setRedirectUrl('monitoring/config');
$form->setTitle(sprintf($this->translate('Edit Monitoring Backend %s'), $backendName));
$form->setIniConfig($this->Config('backends'));
$form->setResourceConfig(ResourceFactory::getResourceConfigs());
$form->setOnSuccess(function (BackendConfigForm $form) use ($backendName) {
try {
$form->edit($backendName, array_map(
function ($v) {
return $v !== '' ? $v : null;
},
$form->getValues()
));
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($form->save()) {
Notification::success(sprintf(t('Monitoring backend "%s" successfully updated'), $backendName));
return true;
}
return false;
});
try {
$form->load($backendName);
$form->handleRequest();
} catch (NotFoundError $_) {
$this->httpNotFound(sprintf($this->translate('Monitoring backend "%s" not found'), $backendName));
}
$this->view->form = $form;
$this->render('form');
}
/**
* Create a new monitoring backend
*/
public function createbackendAction()
{
$form = new BackendConfigForm();
$form->setRedirectUrl('monitoring/config');
$form->setTitle($this->translate('Create New Monitoring Backend'));
$form->setIniConfig($this->Config('backends'));
try {
$form->setResourceConfig(ResourceFactory::getResourceConfigs());
} catch (ConfigurationError $e) {
if ($this->hasPermission('config/resources')) {
Notification::error($e->getMessage());
$this->redirectNow('config/createresource');
}
throw $e; // No permission for resource configuration, show the error
}
$form->setOnSuccess(function (BackendConfigForm $form) {
try {
$form->add($form::transformEmptyValuesToNull($form->getValues()));
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($form->save()) {
Notification::success(t('Monitoring backend successfully created'));
return true;
}
return false;
});
$form->handleRequest();
$this->view->form = $form;
$this->render('form');
}
/**
* Display a confirmation form to remove the backend identified by the 'backend' parameter
*/
public function removebackendAction()
{
$backendName = $this->params->getRequired('backend-name');
$backendForm = new BackendConfigForm();
$backendForm->setIniConfig($this->Config('backends'));
$form = new ConfirmRemovalForm();
$form->setRedirectUrl('monitoring/config');
$form->setTitle(sprintf($this->translate('Remove Monitoring Backend %s'), $backendName));
$form->setOnSuccess(function (ConfirmRemovalForm $form) use ($backendName, $backendForm) {
try {
$backendForm->delete($backendName);
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($backendForm->save()) {
Notification::success(sprintf(t('Monitoring backend "%s" successfully removed'), $backendName));
return true;
}
return false;
});
$form->handleRequest();
$this->view->form = $form;
$this->render('form');
}
/**
* Remove a command transport
*/
public function removetransportAction()
{
$transportName = $this->params->getRequired('transport');
$transportForm = new TransportConfigForm();
$transportForm->setIniConfig($this->Config('commandtransports'));
$form = new ConfirmRemovalForm();
$form->setRedirectUrl('monitoring/config');
$form->setTitle(sprintf($this->translate('Remove Command Transport %s'), $transportName));
$form->info(
$this->translate(
'If you still have 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 {
$transportForm->delete($transportName);
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($transportForm->save()) {
Notification::success(sprintf(t('Command transport "%s" successfully removed'), $transportName));
return true;
}
return false;
});
$form->handleRequest();
$this->view->form = $form;
$this->render('form');
}
/**
* Edit a command transport
*/
public function edittransportAction()
{
$transportName = $this->params->getRequired('transport');
$form = new TransportConfigForm();
$form->setRedirectUrl('monitoring/config');
$form->setTitle(sprintf($this->translate('Edit Command Transport %s'), $transportName));
$form->setIniConfig($this->Config('commandtransports'));
$form->setInstanceNames(
MonitoringBackend::instance()->select()->from('instance', array('instance_name'))->fetchColumn()
);
$form->setOnSuccess(function (TransportConfigForm $form) use ($transportName) {
try {
$form->edit($transportName, array_map(
function ($v) {
return $v !== '' ? $v : null;
},
$form->getValues()
));
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($form->save()) {
Notification::success(sprintf(t('Command transport "%s" successfully updated'), $transportName));
return true;
}
return false;
});
try {
$form->load($transportName);
$form->handleRequest();
} catch (NotFoundError $_) {
$this->httpNotFound(sprintf($this->translate('Command transport "%s" not found'), $transportName));
}
$this->view->form = $form;
$this->render('form');
}
/**
* Create a new command transport
*/
public function createtransportAction()
{
$form = new TransportConfigForm();
$form->setRedirectUrl('monitoring/config');
$form->setTitle($this->translate('Create New Command Transport'));
$form->setIniConfig($this->Config('commandtransports'));
$form->setInstanceNames(
MonitoringBackend::instance()->select()->from('instance', array('instance_name'))->fetchColumn()
);
$form->setOnSuccess(function (TransportConfigForm $form) {
try {
$form->add($form::transformEmptyValuesToNull($form->getValues()));
} catch (Exception $e) {
$form->error($e->getMessage());
return false;
}
if ($form->save()) {
Notification::success(t('Command transport successfully created'));
return true;
}
return false;
});
$form->handleRequest();
$this->view->form = $form;
$this->render('form');
}
/**
* Display a form to adjust security relevant settings
*/
public function securityAction()
{
$form = new SecurityConfigForm();
$form->setIniConfig($this->Config());
$form->handleRequest();
$this->view->form = $form;
$this->view->title = $this->translate('Security');
$this->view->tabs = $this->Module()->getConfigTabs()->activate('security');
}
}

View File

@ -0,0 +1,108 @@
<?php
/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Application\Hook;
use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimeCommandForm;
use Icinga\Module\Monitoring\Object\Host;
use Icinga\Module\Monitoring\Object\Service;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\MenuAction;
/**
* Display detailed information about a downtime
*/
class DowntimeController extends Controller
{
/**
* The fetched downtime
*
* @var object
*/
protected $downtime;
/**
* Fetch the downtime matching the given id and add tabs
*/
public function init()
{
$downtimeId = $this->params->getRequired('downtime_id');
$query = $this->backend->select()->from('downtime', array(
'id' => 'downtime_internal_id',
'objecttype' => 'object_type',
'comment' => 'downtime_comment',
'author_name' => 'downtime_author_name',
'start' => 'downtime_start',
'scheduled_start' => 'downtime_scheduled_start',
'scheduled_end' => 'downtime_scheduled_end',
'end' => 'downtime_end',
'duration' => 'downtime_duration',
'is_flexible' => 'downtime_is_flexible',
'is_fixed' => 'downtime_is_fixed',
'is_in_effect' => 'downtime_is_in_effect',
'entry_time' => 'downtime_entry_time',
'name' => 'downtime_name',
'host_state',
'service_state',
'host_name',
'service_description',
'host_display_name',
'service_display_name'
))->where('downtime_internal_id', $downtimeId);
$this->applyRestriction('monitoring/filter/objects', $query);
if (false === $this->downtime = $query->fetchRow()) {
$this->httpNotFound($this->translate('Downtime not found'));
}
$this->getTabs()->add(
'downtime',
array(
'icon' => 'plug',
'label' => $this->translate('Downtime'),
'title' => $this->translate('Display detailed information about a downtime.'),
'url' =>'monitoring/downtimes/show'
)
)->activate('downtime')->extend(new DashboardAction())->extend(new MenuAction());
if (Hook::has('ticket')) {
$this->view->tickets = Hook::first('ticket');
}
}
/**
* Display the detail view for a downtime
*/
public function showAction()
{
$isService = isset($this->downtime->service_description);
$this->view->downtime = $this->downtime;
$this->view->isService = $isService;
$this->view->listAllLink = Url::fromPath('monitoring/list/downtimes');
$this->view->showHostLink = Url::fromPath('monitoring/host/show')->setParam('host', $this->downtime->host_name);
$this->view->showServiceLink = Url::fromPath('monitoring/service/show')
->setParam('host', $this->downtime->host_name)
->setParam('service', $this->downtime->service_description);
$this->view->stateName = $isService ? Service::getStateText($this->downtime->service_state)
: Host::getStateText($this->downtime->host_state);
$this->view->title = $this->translate('Downtimes');
if ($this->hasPermission('monitoring/command/downtime/delete')) {
$form = new DeleteDowntimeCommandForm();
$form
->populate(array(
'downtime_id' => $this->downtime->id,
'downtime_is_service' => $isService,
'downtime_name' => $this->downtime->name,
'redirect' => Url::fromPath('monitoring/list/downtimes'),
))
->handleRequest();
$this->view->delDowntimeForm = $form;
}
}
}

View File

@ -0,0 +1,108 @@
<?php
/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Data\Filter\Filter;
use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimesCommandForm;
use Icinga\Web\Url;
/**
* Display detailed information about downtimes
*/
class DowntimesController extends Controller
{
/**
* The downtimes view
*
* @var \Icinga\Module\Monitoring\DataView\Downtime
*/
protected $downtimes;
/**
* Filter from request
*
* @var Filter
*/
protected $filter;
/**
* Fetch all downtimes matching the current filter and add tabs
*/
public function init()
{
$this->filter = Filter::fromQueryString(str_replace(
'downtime_id',
'downtime_internal_id',
(string) $this->params
));
$query = $this->backend->select()->from('downtime', array(
'id' => 'downtime_internal_id',
'objecttype' => 'object_type',
'comment' => 'downtime_comment',
'author_name' => 'downtime_author_name',
'start' => 'downtime_start',
'scheduled_start' => 'downtime_scheduled_start',
'scheduled_end' => 'downtime_scheduled_end',
'end' => 'downtime_end',
'duration' => 'downtime_duration',
'is_flexible' => 'downtime_is_flexible',
'is_fixed' => 'downtime_is_fixed',
'is_in_effect' => 'downtime_is_in_effect',
'entry_time' => 'downtime_entry_time',
'name' => 'downtime_name',
'host_state',
'service_state',
'host_name',
'service_description',
'host_display_name',
'service_display_name'
))->addFilter($this->filter);
$this->applyRestriction('monitoring/filter/objects', $query);
$this->downtimes = $query;
$this->view->title = $this->translate('Downtimes');
$this->getTabs()->add(
'downtimes',
array(
'icon' => 'plug',
'label' => $this->translate('Downtimes') . sprintf(' (%d)', $query->count()),
'title' => $this->translate('Display detailed information about multiple downtimes.'),
'url' =>'monitoring/downtimes/show'
)
)->activate('downtimes');
}
/**
* Display the detail view for a downtime list
*/
public function showAction()
{
$this->view->downtimes = $this->downtimes;
$this->view->listAllLink = Url::fromPath('monitoring/list/downtimes')
->setQueryString($this->filter->toQueryString());
$this->view->removeAllLink = Url::fromPath('monitoring/downtimes/delete-all')->setParams($this->params);
}
/**
* Display the form for removing a downtime list
*/
public function deleteAllAction()
{
$this->assertPermission('monitoring/command/downtime/delete');
$this->view->downtimes = $this->downtimes;
$this->view->listAllLink = Url::fromPath('monitoring/list/downtimes')
->setQueryString($this->filter->toQueryString());
$delDowntimeForm = new DeleteDowntimesCommandForm();
$delDowntimeForm->setTitle($this->view->translate('Remove all Downtimes'));
$delDowntimeForm->addDescription(sprintf(
$this->translate('Confirm removal of %d downtimes.'),
$this->downtimes->count()
));
$delDowntimeForm->setRedirectUrl(Url::fromPath('monitoring/list/downtimes'));
$delDowntimeForm->setDowntimes($this->downtimes->fetchAll())->handleRequest();
$this->view->delAllDowntimeForm = $delDowntimeForm;
}
}

View File

@ -0,0 +1,551 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use DateTime;
use DateTimeZone;
use Icinga\Module\Monitoring\Hook\EventDetailsExtensionHook;
use Icinga\Application\Hook;
use InvalidArgumentException;
use Icinga\Data\Queryable;
use Icinga\Date\DateFormatter;
use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Object\Host;
use Icinga\Module\Monitoring\Object\Service;
use Icinga\Util\TimezoneDetect;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\MenuAction;
use Icinga\Web\Widget\Tabextension\OutputFormat;
class EventController extends Controller
{
/**
* @var string[]
*/
protected $dataViewsByType = array(
'notify' => 'notificationevent',
'comment' => 'commentevent',
'comment_deleted' => 'commentevent',
'ack' => 'commentevent',
'ack_deleted' => 'commentevent',
'dt_comment' => 'commentevent',
'dt_comment_deleted' => 'commentevent',
'flapping' => 'flappingevent',
'flapping_deleted' => 'flappingevent',
'hard_state' => 'statechangeevent',
'soft_state' => 'statechangeevent',
'dt_start' => 'downtimeevent',
'dt_end' => 'downtimeevent'
);
public function init()
{
if (Hook::has('ticket')) {
$this->view->tickets = Hook::first('ticket');
}
}
public function showAction()
{
$type = $this->params->shiftRequired('type');
$id = $this->params->shiftRequired('id');
if (! isset($this->dataViewsByType[$type])
|| $this->applyRestriction(
'monitoring/filter/objects',
$this->backend->select()->from('eventhistory', array('id'))->where('id', $id)
)->fetchRow() === false
) {
$this->httpNotFound($this->translate('Event not found'));
}
$event = $this->query($type, $id)->fetchRow();
if ($event === false) {
$this->httpNotFound($this->translate('Event not found'));
}
$this->view->object = $object = $event->service_description === null
? new Host($this->backend, $event->host_name)
: new Service($this->backend, $event->host_name, $event->service_description);
$object->fetch();
list($icon, $label) = $this->getIconAndLabel($type);
$this->view->details = array_merge(
array(array($this->view->escape($this->translate('Type')), $label)),
$this->getDetails($type, $event)
);
$this->view->extensionsHtml = array();
/** @var EventDetailsExtensionHook $hook */
foreach (Hook::all('Monitoring\\EventDetailsExtension') as $hook) {
try {
$html = $hook->getHtmlForEvent($event);
} catch (\Exception $e) {
$html = $this->view->escape($e->getMessage());
}
if ($html) {
$module = $this->view->escape($hook->getModule()->getName());
$this->view->extensionsHtml[] =
'<div class="icinga-module module-' . $module . '" data-icinga-module="' . $module . '">'
. $html
. '</div>';
}
}
$this->view->title = $this->translate('Event Overview');
$this->getTabs()
->add('event', array(
'title' => $label,
'label' => $label,
'url' => Url::fromRequest(),
'active' => true
))
->extend(new OutputFormat())
->extend(new DashboardAction())
->extend(new MenuAction());
}
/**
* Return translated and escaped 'Yes' if the given condition is true, 'No' otherwise, 'N/A' if NULL
*
* @param bool|null $condition
*
* @return string
*/
protected function yesOrNo($condition)
{
if ($condition === null) {
return $this->view->escape($this->translate('N/A'));
}
return $this->view->escape($condition ? $this->translate('Yes') : $this->translate('No'));
}
/**
* Render the given duration in seconds as human readable HTML or 'N/A' if NULL
*
* @param int|null $seconds
*
* @return string
*/
protected function duration($seconds)
{
return $this->view->escape(
$seconds === null ? $this->translate('N/A') : DateFormatter::formatDuration($seconds)
);
}
/**
* Render the given percent number as human readable HTML or 'N/A' if NULL
*
* @param float|null $percent
*
* @return string
*/
protected function percent($percent)
{
return $this->view->escape(
$percent === null ? $this->translate('N/A') : sprintf($this->translate('%.2f%%'), $percent)
);
}
/**
* Render the given comment message as HTML or 'N/A' if NULL
*
* @param string|null $message
*
* @return string
*/
protected function comment($message)
{
return $this->view->nl2br($this->view->createTicketLinks($this->view->markdown($message)));
}
/**
* Render a link to the given contact or 'N/A' if NULL
*
* @param string|null $name
*
* @return string
*/
protected function contact($name)
{
return $name === null
? $this->view->escape($this->translate('N/A'))
: $this->view->qlink($name, Url::fromPath('monitoring/show/contact', array('contact_name' => $name)));
}
/**
* Render the given monitored object state as human readable HTML or 'N/A' if NULL
*
* @param bool $isService
* @param int|null $state
*
* @return string
*/
protected function state($isService, $state)
{
if ($state === null) {
return $this->view->escape($this->translate('N/A'));
}
try {
$stateText = $isService
? Service::getStateText($state, true)
: Host::getStateText($state, true);
} catch (InvalidArgumentException $e) {
return $this->view->escape($this->translate('N/A'));
}
return '<span class="badge state-' . ($isService ? Service::getStateText($state) : Host::getStateText($state))
. '">&nbsp;</span><span class="state-label">' . $this->view->escape($stateText) . '</span>';
}
/**
* Render the given plugin output as human readable HTML
*
* @param string $output
*
* @return string
*/
protected function pluginOutput($output)
{
return $this->view->getHelper('PluginOutput')->pluginOutput($output);
}
/**
* Return the icon and the label for the given event type
*
* @param string $eventType
*
* @return ?string[]
*/
protected function getIconAndLabel($eventType)
{
switch ($eventType) {
case 'notify':
return array('bell', $this->translate('Notification', 'tooltip'));
case 'comment':
return array('comment-empty', $this->translate('Comment', 'tooltip'));
case 'comment_deleted':
return array('cancel', $this->translate('Comment removed', 'tooltip'));
case 'ack':
return array('ok', $this->translate('Acknowledged', 'tooltip'));
case 'ack_deleted':
return array('ok', $this->translate('Acknowledgement removed', 'tooltip'));
case 'dt_comment':
return array('plug', $this->translate('Downtime scheduled', 'tooltip'));
case 'dt_comment_deleted':
return array('plug', $this->translate('Downtime removed', 'tooltip'));
case 'flapping':
return array('flapping', $this->translate('Flapping started', 'tooltip'));
case 'flapping_deleted':
return array('flapping', $this->translate('Flapping stopped', 'tooltip'));
case 'hard_state':
return array('warning-empty', $this->translate('Hard state change'));
case 'soft_state':
return array('spinner', $this->translate('Soft state change'));
case 'dt_start':
return array('plug', $this->translate('Downtime started', 'tooltip'));
case 'dt_end':
return array('plug', $this->translate('Downtime ended', 'tooltip'));
}
}
/**
* Return a query for the given event ID of the given type
*
* @param string $type
* @param int $id
*
* @return ?Queryable
*/
protected function query($type, $id)
{
switch ($this->dataViewsByType[$type]) {
case 'downtimeevent':
return $this->backend->select()
->from('downtimeevent', array(
'entry_time' => 'downtimeevent_entry_time',
'author_name' => 'downtimeevent_author_name',
'comment_data' => 'downtimeevent_comment_data',
'is_fixed' => 'downtimeevent_is_fixed',
'scheduled_start_time' => 'downtimeevent_scheduled_start_time',
'scheduled_end_time' => 'downtimeevent_scheduled_end_time',
'was_started' => 'downtimeevent_was_started',
'actual_start_time' => 'downtimeevent_actual_start_time',
'actual_end_time' => 'downtimeevent_actual_end_time',
'was_cancelled' => 'downtimeevent_was_cancelled',
'is_in_effect' => 'downtimeevent_is_in_effect',
'trigger_time' => 'downtimeevent_trigger_time',
'host_name',
'service_description'
))
->where('downtimeevent_id', $id);
case 'commentevent':
return $this->backend->select()
->from('commentevent', array(
'entry_type' => 'commentevent_entry_type',
'comment_time' => 'commentevent_comment_time',
'author_name' => 'commentevent_author_name',
'comment_data' => 'commentevent_comment_data',
'is_persistent' => 'commentevent_is_persistent',
'comment_source' => 'commentevent_comment_source',
'expires' => 'commentevent_expires',
'expiration_time' => 'commentevent_expiration_time',
'deletion_time' => 'commentevent_deletion_time',
'host_name',
'service_description'
))
->where('commentevent_id', $id);
case 'flappingevent':
return $this->backend->select()
->from('flappingevent', array(
'event_time' => 'flappingevent_event_time',
'reason_type' => 'flappingevent_reason_type',
'percent_state_change' => 'flappingevent_percent_state_change',
'low_threshold' => 'flappingevent_low_threshold',
'high_threshold' => 'flappingevent_high_threshold',
'host_name',
'service_description'
))
->where('flappingevent_id', $id)
->where('flappingevent_event_type', $type);
case 'notificationevent':
return $this->backend->select()
->from('notificationevent', array(
'notification_reason' => 'notificationevent_reason',
'start_time' => 'notificationevent_start_time',
'end_time' => 'notificationevent_end_time',
'state' => 'notificationevent_state',
'output' => 'notificationevent_output',
'long_output' => 'notificationevent_long_output',
'escalated' => 'notificationevent_escalated',
'contacts_notified' => 'notificationevent_contacts_notified',
'host_name',
'service_description'
))
->where('notificationevent_id', $id);
case 'statechangeevent':
return $this->backend->select()
->from('statechangeevent', array(
'state_time' => 'statechangeevent_state_time',
'state' => 'statechangeevent_state',
'current_check_attempt' => 'statechangeevent_current_check_attempt',
'max_check_attempts' => 'statechangeevent_max_check_attempts',
'last_state' => 'statechangeevent_last_state',
'last_hard_state' => 'statechangeevent_last_hard_state',
'output' => 'statechangeevent_output',
'long_output' => 'statechangeevent_long_output',
'check_source' => 'statechangeevent_check_source',
'host_name',
'service_description'
))
->where('statechangeevent_id', $id)
->where('statechangeevent_state_change', 1)
->where('statechangeevent_state_type', $type);
}
}
/**
* Return the given event's data prepared for a name-value table
*
* @param string $type
* @param \stdClass $event
*
* @return ?string[][]
*/
protected function getDetails($type, $event)
{
switch ($type) {
case 'dt_start':
case 'dt_end':
$details = array(array(
array($this->translate('Entry time'), DateFormatter::formatDateTime($event->entry_time)),
array($this->translate('Is fixed'), $this->yesOrNo($event->is_fixed)),
array($this->translate('Is in effect'), $this->yesOrNo($event->is_in_effect)),
array($this->translate('Was started'), $this->yesOrNo($event->was_started))
));
if ($type === 'dt_end') {
$details[] = array(
array($this->translate('Was cancelled'), $this->yesOrNo($event->was_cancelled))
);
}
$details[] = array(
array($this->translate('Trigger time'), DateFormatter::formatDateTime($event->trigger_time)),
array(
$this->translate('Scheduled start time'),
DateFormatter::formatDateTime($event->scheduled_start_time)
),
array(
$this->translate('Actual start time'),
DateFormatter::formatDateTime($event->actual_start_time)
),
array(
$this->translate('Scheduled end time'),
DateFormatter::formatDateTime($event->scheduled_end_time)
)
);
if ($type === 'dt_end') {
$details[] = array(
array(
$this->translate('Actual end time'),
DateFormatter::formatDateTime($event->actual_end_time)
)
);
}
$details[] = array(
array($this->translate('Author'), $this->contact($event->author_name)),
array($this->translate('Comment'), $this->comment($event->comment_data))
);
return call_user_func_array('array_merge', $details);
case 'comment':
case 'comment_deleted':
case 'ack':
case 'ack_deleted':
case 'dt_comment':
case 'dt_comment_deleted':
switch ($event->entry_type) {
case 'comment':
$entryType = $this->translate('User comment');
break;
case 'downtime':
$entryType = $this->translate('Scheduled downtime');
break;
case 'flapping':
$entryType = $this->translate('Flapping');
break;
case 'ack':
$entryType = $this->translate('Acknowledgement');
break;
default:
$entryType = $this->translate('N/A');
}
switch ($event->comment_source) {
case 'icinga':
$commentSource = $this->translate('Icinga');
break;
case 'user':
$commentSource = $this->translate('User');
break;
default:
$commentSource = $this->translate('N/A');
}
return array(
array($this->translate('Time'), DateFormatter::formatDateTime($event->comment_time)),
array($this->translate('Source'), $this->view->escape($commentSource)),
array($this->translate('Entry type'), $this->view->escape($entryType)),
array($this->translate('Author'), $this->contact($event->author_name)),
array($this->translate('Is persistent'), $this->yesOrNo($event->is_persistent)),
array($this->translate('Expires'), $this->yesOrNo($event->expires)),
array($this->translate('Expiration time'), DateFormatter::formatDateTime($event->expiration_time)),
array($this->translate('Deletion time'), DateFormatter::formatDateTime($event->deletion_time)),
array($this->translate('Message'), $this->comment($event->comment_data))
);
case 'flapping':
case 'flapping_deleted':
switch ($event->reason_type) {
case 'stopped':
$reasonType = $this->translate('Flapping stopped normally');
break;
case 'disabled':
$reasonType = $this->translate('Flapping was disabled');
break;
default:
$reasonType = $this->translate('N/A');
}
return array(
array($this->translate('Event time'), DateFormatter::formatDateTime($event->event_time)),
array($this->translate('Reason'), $this->view->escape($reasonType)),
array($this->translate('State change'), $this->percent($event->percent_state_change)),
array($this->translate('Low threshold'), $this->percent($event->low_threshold)),
array($this->translate('High threshold'), $this->percent($event->high_threshold))
);
case 'notify':
switch ($event->notification_reason) {
case 'normal_notification':
$notificationReason = $this->translate('Normal notification');
break;
case 'ack':
$notificationReason = $this->translate('Problem acknowledgement');
break;
case 'flapping_started':
$notificationReason = $this->translate('Flapping started');
break;
case 'flapping_stopped':
$notificationReason = $this->translate('Flapping stopped');
break;
case 'flapping_disabled':
$notificationReason = $this->translate('Flapping was disabled');
break;
case 'dt_start':
$notificationReason = $this->translate('Downtime started');
break;
case 'dt_end':
$notificationReason = $this->translate('Downtime ended');
break;
case 'dt_cancel':
$notificationReason = $this->translate('Downtime was cancelled');
break;
case 'custom_notification':
$notificationReason = $this->translate('Custom notification');
break;
default:
$notificationReason = $this->translate('N/A');
}
$details = array(
array($this->translate('Start time'), DateFormatter::formatDateTime($event->start_time)),
array($this->translate('End time'), DateFormatter::formatDateTime($event->end_time)),
array($this->translate('Reason'), $this->view->escape($notificationReason)),
array(
$this->translate('State'),
$this->state($event->service_description !== null, $event->state)
),
array($this->translate('Escalated'), $this->yesOrNo($event->escalated)),
array($this->translate('Contacts notified'), (int) $event->contacts_notified),
array(
$this->translate('Output'),
$this->pluginOutput($event->output) . $this->pluginOutput($event->long_output)
)
);
return $details;
case 'hard_state':
case 'soft_state':
$isService = $event->service_description !== null;
$details = array(
array($this->translate('State time'), DateFormatter::formatDateTime($event->state_time)),
array($this->translate('State'), $this->state($isService, $event->state)),
array($this->translate('Check source'), $event->check_source),
array($this->translate('Check attempt'), $this->view->escape(sprintf(
$this->translate('%d of %d'),
(int) $event->current_check_attempt,
(int) $event->max_check_attempts
))),
array($this->translate('Last state'), $this->state($isService, $event->last_state)),
array($this->translate('Last hard state'), $this->state($isService, $event->last_hard_state)),
array(
$this->translate('Output'),
$this->pluginOutput($event->output) . $this->pluginOutput($event->long_output)
)
);
return $details;
}
}
}

View File

@ -0,0 +1,197 @@
<?php
/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Forms\Command\Instance\DisableNotificationsExpireCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Instance\ToggleInstanceFeaturesCommandForm;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\MenuAction;
/**
* Display process and performance information of the monitoring host and program-wide commands
*/
class HealthController extends Controller
{
/**
* Add tabs
*
* @see \Icinga\Web\Controller\ActionController::init()
*/
public function init()
{
$this
->getTabs()
->add(
'info',
array(
'title' => $this->translate(
'Show information about the current monitoring instance\'s process'
. ' and it\'s performance as well as available features'
),
'label' => $this->translate('Process Information'),
'url' =>'monitoring/health/info'
)
)
->add(
'stats',
array(
'title' => $this->translate(
'Show statistics about the monitored objects'
),
'label' => $this->translate('Stats'),
'url' =>'monitoring/health/stats'
)
)
->extend(new DashboardAction())->extend(new MenuAction());
}
/**
* Display process information and program-wide commands
*/
public function infoAction()
{
$this->view->title = $this->translate('Process Information');
$this->getTabs()->activate('info');
$this->setAutorefreshInterval(10);
$this->view->backendName = $this->backend->getName();
$programStatus = $this->backend
->select()
->from(
'programstatus',
array(
'is_currently_running',
'process_id',
'endpoint_name',
'program_start_time',
'status_update_time',
'program_version',
'last_command_check',
'last_log_rotation',
'global_service_event_handler',
'global_host_event_handler',
'notifications_enabled',
'disable_notif_expire_time',
'active_service_checks_enabled',
'passive_service_checks_enabled',
'active_host_checks_enabled',
'passive_host_checks_enabled',
'event_handlers_enabled',
'obsess_over_services',
'obsess_over_hosts',
'flap_detection_enabled',
'process_performance_data'
)
)
->getQuery();
$this->handleFormatRequest($programStatus);
$programStatus = $programStatus->fetchRow();
if ($programStatus === false) {
$this->render('not-running', true, null);
return;
}
$this->view->programStatus = $programStatus;
$toggleFeaturesForm = new ToggleInstanceFeaturesCommandForm();
$toggleFeaturesForm
->setBackend($this->backend)
->setStatus($programStatus)
->load($programStatus)
->handleRequest();
$this->view->toggleFeaturesForm = $toggleFeaturesForm;
$this->view->runtimevariables = (object) $this->backend->select()
->from('runtimevariables', array('varname', 'varvalue'))
->getQuery()->fetchPairs();
$this->view->checkperformance = $this->backend->select()
->from('runtimesummary')
->getQuery()->fetchAll();
}
/**
* Display stats about current checks and monitored objects
*/
public function statsAction()
{
$this->view->title = $this->translate('Stats');
$this->getTabs()->activate('stats');
$servicestats = $this->backend->select()->from('servicestatussummary', array(
'services_critical',
'services_critical_handled',
'services_critical_unhandled',
'services_ok',
'services_pending',
'services_total',
'services_unknown',
'services_unknown_handled',
'services_unknown_unhandled',
'services_warning',
'services_warning_handled',
'services_warning_unhandled'
));
$this->applyRestriction('monitoring/filter/objects', $servicestats);
$this->view->servicestats = $servicestats->fetchRow();
$this->view->unhandledServiceProblems = $this->view->servicestats->services_critical_unhandled
+ $this->view->servicestats->services_unknown_unhandled
+ $this->view->servicestats->services_warning_unhandled;
$hoststats = $this->backend->select()->from('hoststatussummary', array(
'hosts_total',
'hosts_up',
'hosts_down',
'hosts_down_handled',
'hosts_down_unhandled',
'hosts_unreachable',
'hosts_unreachable_handled',
'hosts_unreachable_unhandled',
'hosts_pending',
));
$this->applyRestriction('monitoring/filter/objects', $hoststats);
$this->view->hoststats = $hoststats->fetchRow();
$this->view->unhandledhostProblems = $this->view->hoststats->hosts_down_unhandled
+ $this->view->hoststats->hosts_unreachable_unhandled;
$this->view->unhandledProblems = $this->view->unhandledhostProblems
+ $this->view->unhandledServiceProblems;
$this->view->runtimevariables = (object) $this->backend->select()
->from('runtimevariables', array('varname', 'varvalue'))
->getQuery()->fetchPairs();
$this->view->checkperformance = $this->backend->select()
->from('runtimesummary')
->getQuery()->fetchAll();
}
/**
* Disable notifications w/ an optional expire time
*/
public function disableNotificationsAction()
{
$this->assertPermission('monitoring/command/feature/instance');
$this->view->title = $this->translate('Disable Notifications');
$programStatus = $this->backend
->select()
->from(
'programstatus',
array(
'notifications_enabled',
'disable_notif_expire_time'
)
)
->getQuery()
->fetchRow();
$this->view->programStatus = $programStatus;
if ((bool) $programStatus->notifications_enabled === false) {
return;
} else {
$form = new DisableNotificationsExpireCommandForm();
$form
->setRedirectUrl('monitoring/health/info')
->handleRequest();
$this->view->form = $form;
}
}
}

View File

@ -0,0 +1,185 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostCheckCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostDowntimeCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\SendCustomNotificationCommandForm;
use Icinga\Module\Monitoring\Object\Host;
use Icinga\Module\Monitoring\Web\Controller\MonitoredObjectController;
use Icinga\Web\Hook;
use Icinga\Web\Navigation\Navigation;
class HostController extends MonitoredObjectController
{
/**
* {@inheritdoc}
*/
protected $commandRedirectUrl = 'monitoring/host/show';
/**
* Fetch the requested host from the monitoring backend
*/
public function init()
{
$host = new Host($this->backend, $this->params->getRequired('host'));
$this->applyRestriction('monitoring/filter/objects', $host);
if ($host->fetch() === false) {
$this->httpNotFound($this->translate('Host not found'));
}
$this->object = $host;
$this->createTabs();
$this->getTabs()->activate('host');
$this->view->title = $host->host_display_name;
$this->view->defaultTitle = $this->translate('Hosts') . ' :: ' . $this->view->defaultTitle;
}
/**
* Get host actions from hook
*
* @return Navigation
*/
protected function getHostActions()
{
$navigation = new Navigation();
foreach (Hook::all('Monitoring\\HostActions') as $hook) {
$navigation->merge($hook->getNavigation($this->object));
}
return $navigation;
}
/**
* Show a host
*/
public function showAction()
{
$this->view->actions = $this->getHostActions();
parent::showAction();
}
/**
* List a host's services
*/
public function servicesAction()
{
$this->setAutorefreshInterval(10);
$this->getTabs()->activate('services');
$query = $this->backend->select()->from('servicestatus', array(
'host_name',
'host_display_name',
'host_state',
'host_state_type',
'host_last_state_change',
'host_address',
'host_address6',
'host_handled',
'service_description',
'service_display_name',
'service_state',
'service_in_downtime',
'service_acknowledged',
'service_handled',
'service_output',
'service_perfdata',
'service_attempt',
'service_last_state_change',
'service_icon_image',
'service_icon_image_alt',
'service_is_flapping',
'service_state_type',
'service_handled',
'service_severity',
'service_last_check',
'service_notifications_enabled',
'service_action_url',
'service_notes_url',
'service_active_checks_enabled',
'service_passive_checks_enabled',
'current_check_attempt' => 'service_current_check_attempt',
'max_check_attempts' => 'service_max_check_attempts',
'service_check_command',
'service_next_update'
));
$this->applyRestriction('monitoring/filter/objects', $query);
$this->view->services = $query->where('host_name', $this->object->getName());
$this->view->object = $this->object;
}
/**
* Acknowledge a host problem
*/
public function acknowledgeProblemAction()
{
$this->assertPermission('monitoring/command/acknowledge-problem');
$form = new AcknowledgeProblemCommandForm();
$form->setTitle($this->translate('Acknowledge Host Problem'));
$this->handleCommandForm($form);
}
/**
* Add a host comment
*/
public function addCommentAction()
{
$this->assertPermission('monitoring/command/comment/add');
$form = new AddCommentCommandForm();
$form->setTitle($this->translate('Add Host Comment'));
$this->handleCommandForm($form);
}
/**
* Reschedule a host check
*/
public function rescheduleCheckAction()
{
$this->assertPermission('monitoring/command/schedule-check');
$form = new ScheduleHostCheckCommandForm();
$form->setTitle($this->translate('Reschedule Host Check'));
$this->handleCommandForm($form);
}
/**
* Schedule a host downtime
*/
public function scheduleDowntimeAction()
{
$this->assertPermission('monitoring/command/downtime/schedule');
$form = new ScheduleHostDowntimeCommandForm();
$form->setTitle($this->translate('Schedule Host Downtime'));
$this->handleCommandForm($form);
}
/**
* Submit a passive host check result
*/
public function processCheckResultAction()
{
$this->assertPermission('monitoring/command/process-check-result');
$form = new ProcessCheckResultCommandForm();
$form->setTitle($this->translate('Submit Passive Host Check Result'));
$this->handleCommandForm($form);
}
/**
* Send a custom notification for host
*/
public function sendCustomNotificationAction()
{
$this->assertPermission('monitoring/command/send-custom-notification');
$form = new SendCustomNotificationCommandForm();
$form->setTitle($this->translate('Send Custom Host Notification'));
$this->handleCommandForm($form);
}
}

View File

@ -0,0 +1,260 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Exception;
use Icinga\Data\Filter\Filter;
use Icinga\Data\Filter\FilterEqual;
use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\CheckNowCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ObjectsCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\RemoveAcknowledgementCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostCheckCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleHostDowntimeCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\SendCustomNotificationCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ToggleObjectFeaturesCommandForm;
use Icinga\Module\Monitoring\Hook\DetailviewExtensionHook;
use Icinga\Module\Monitoring\Object\HostList;
use Icinga\Web\Hook;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\MenuAction;
class HostsController extends Controller
{
/**
* @var HostList
*/
protected $hostList;
public function init()
{
$hostList = new HostList($this->backend);
$this->applyRestriction('monitoring/filter/objects', $hostList);
$hostList->addFilter(Filter::fromQueryString((string) $this->params));
$this->hostList = $hostList;
$this->hostList->setColumns(array(
'host_acknowledged',
'host_active_checks_enabled',
'host_display_name',
'host_event_handler_enabled',
'host_flap_detection_enabled',
'host_handled',
'host_in_downtime',
'host_is_flapping',
'host_last_state_change',
'host_name',
'host_notifications_enabled',
'host_obsessing',
'host_passive_checks_enabled',
'host_problem',
'host_state',
'instance_name'
));
$this->view->baseFilter = $this->hostList->getFilter();
$this->getTabs()->add(
'show',
array(
'label' => $this->translate('Hosts') . sprintf(' (%d)', count($this->hostList)),
'title' => sprintf(
$this->translate('Show summarized information for %u hosts'),
count($this->hostList)
),
'url' => Url::fromRequest()
)
)->extend(new DashboardAction())->extend(new MenuAction())->activate('show');
$this->view->listAllLink = Url::fromRequest()->setPath('monitoring/list/hosts');
$this->view->title = $this->translate('Hosts');
}
protected function handleCommandForm(ObjectsCommandForm $form)
{
$form
->setBackend($this->backend)
->setObjects($this->hostList)
->setRedirectUrl(Url::fromPath('monitoring/hosts/show')->setParams(
$this->params->without('host_active_checks_enabled')
))
->handleRequest();
$this->view->form = $form;
$this->view->objects = $this->hostList;
$this->view->stats = $this->hostList->getStateSummary();
$this->_helper->viewRenderer('partials/command/objects-command-form', null, true);
return $form;
}
public function showAction()
{
$this->setAutorefreshInterval(15);
$activeChecksEnabled = $this->hostList->getFeatureStatus()['active_checks_enabled'] !== 0;
if ($this->Auth()->hasPermission('monitoring/command/schedule-check')
|| ($this->Auth()->hasPermission('monitoring/command/schedule-check/active-only')
&& $activeChecksEnabled
)
) {
$checkNowForm = new CheckNowCommandForm();
$checkNowForm
->setObjects($this->hostList)
->handleRequest();
$this->view->checkNowForm = $checkNowForm;
}
$acknowledgedObjects = $this->hostList->getAcknowledgedObjects();
if ($acknowledgedObjects->count()) {
$removeAckForm = new RemoveAcknowledgementCommandForm();
$removeAckForm
->setObjects($acknowledgedObjects)
->handleRequest();
$this->view->removeAckForm = $removeAckForm;
}
$featureStatus = $this->hostList->getFeatureStatus();
$toggleFeaturesForm = new ToggleObjectFeaturesCommandForm(array(
'backend' => $this->backend,
'objects' => $this->hostList
));
$toggleFeaturesForm
->load((object) $featureStatus)
->handleRequest();
$this->view->toggleFeaturesForm = $toggleFeaturesForm;
$hostStates = $this->hostList->getStateSummary();
if ($activeChecksEnabled) {
$this->view->rescheduleAllLink = Url::fromRequest()
->setPath('monitoring/hosts/reschedule-check')
->addParams(['host_active_checks_enabled' => true]);
}
$this->view->downtimeAllLink = Url::fromRequest()->setPath('monitoring/hosts/schedule-downtime');
$this->view->processCheckResultAllLink = Url::fromRequest()->setPath('monitoring/hosts/process-check-result');
$this->view->addCommentLink = Url::fromRequest()->setPath('monitoring/hosts/add-comment');
$this->view->stats = $hostStates;
$this->view->objects = $this->hostList;
$this->view->unhandledObjects = $this->hostList->getUnhandledObjects();
$this->view->problemObjects = $this->hostList->getProblemObjects();
$this->view->acknowledgeUnhandledLink = Url::fromPath('monitoring/hosts/acknowledge-problem')
->setQueryString($this->hostList->getUnhandledObjects()->objectsFilter()->toQueryString());
$this->view->downtimeUnhandledLink = Url::fromPath('monitoring/hosts/schedule-downtime')
->setQueryString($this->hostList->getUnhandledObjects()->objectsFilter()->toQueryString());
$this->view->downtimeLink = Url::fromPath('monitoring/hosts/schedule-downtime')
->setQueryString($this->hostList->getProblemObjects()->objectsFilter()->toQueryString());
$this->view->acknowledgedObjects = $this->hostList->getAcknowledgedObjects();
$this->view->acknowledgeLink = Url::fromPath('monitoring/hosts/acknowledge-problem')
->setQueryString($this->hostList->getUnacknowledgedObjects()->objectsFilter()->toQueryString());
$this->view->unacknowledgedObjects = $this->hostList->getUnacknowledgedObjects();
$this->view->objectsInDowntime = $this->hostList->getObjectsInDowntime();
$this->view->inDowntimeLink = Url::fromPath('monitoring/list/hosts')
->setQueryString(
$this->hostList
->getObjectsInDowntime()
->objectsFilter()
->toQueryString()
);
$this->view->showDowntimesLink = Url::fromPath('monitoring/list/downtimes')
->setQueryString(
$this->hostList
->objectsFilter()
->andFilter(FilterEqual::where('object_type', 'host'))
->toQueryString()
);
$this->view->commentsLink = Url::fromRequest()->setPath('monitoring/list/comments');
$this->view->sendCustomNotificationLink = Url::fromRequest()
->setPath('monitoring/hosts/send-custom-notification');
$this->view->extensionsHtml = array();
foreach (Hook::all('Monitoring\DetailviewExtension') as $hook) {
/** @var DetailviewExtensionHook $hook */
try {
$html = $hook->setView($this->view)->getHtmlForObjects($this->hostList);
} catch (Exception $e) {
$html = $this->view->escape($e->getMessage());
}
if ($html) {
$module = $this->view->escape($hook->getModule()->getName());
$this->view->extensionsHtml[] =
'<div class="icinga-module module-' . $module . '" data-icinga-module="' . $module . '">'
. $html
. '</div>';
}
}
}
/**
* Add a host comments
*/
public function addCommentAction()
{
$this->assertPermission('monitoring/command/comment/add');
$form = new AddCommentCommandForm();
$form->setTitle($this->translate('Add Host Comments'));
$this->handleCommandForm($form);
}
/**
* Acknowledge host problems
*/
public function acknowledgeProblemAction()
{
$this->assertPermission('monitoring/command/acknowledge-problem');
$form = new AcknowledgeProblemCommandForm();
$form->setTitle($this->translate('Acknowledge Host Problems'));
$this->handleCommandForm($form);
}
/**
* Reschedule host checks
*/
public function rescheduleCheckAction()
{
$this->assertPermission('monitoring/command/schedule-check');
$form = new ScheduleHostCheckCommandForm();
$form->setTitle($this->translate('Reschedule Host Checks'));
$this->handleCommandForm($form);
}
/**
* Schedule host downtimes
*/
public function scheduleDowntimeAction()
{
$this->assertPermission('monitoring/command/downtime/schedule');
$form = new ScheduleHostDowntimeCommandForm();
$form->setTitle($this->translate('Schedule Host Downtimes'));
$this->handleCommandForm($form);
}
/**
* Submit passive host check results
*/
public function processCheckResultAction()
{
$this->assertPermission('monitoring/command/process-check-result');
$form = new ProcessCheckResultCommandForm();
$form->setTitle($this->translate('Submit Passive Host Check Results'));
$this->handleCommandForm($form);
}
/**
* Send a custom notification for hosts
*/
public function sendCustomNotificationAction()
{
$this->assertPermission('monitoring/command/send-custom-notification');
$form = new SendCustomNotificationCommandForm();
$form->setTitle($this->translate('Send Custom Host Notification'));
$this->handleCommandForm($form);
}
}

View File

@ -0,0 +1,808 @@
<?php
/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
use Icinga\Security\SecurityException;
use Icinga\Util\GlobFilter;
use Icinga\Web\Form;
use Zend_Form;
use Icinga\Data\Filter\Filter;
use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\DataView\DataView;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteCommentCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\DeleteDowntimeCommandForm;
use Icinga\Module\Monitoring\Forms\StatehistoryForm;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\MenuAction;
use Icinga\Web\Widget\Tabextension\OutputFormat;
use Icinga\Web\Widget\Tabs;
class ListController extends Controller
{
/**
* @see ActionController::init
*/
public function init()
{
parent::init();
$this->createTabs();
}
/**
* Overwrite the backend to use (used for testing)
*
* @param MonitoringBackend $backend The Backend that should be used for querying
*/
public function setBackend($backend)
{
$this->backend = $backend;
}
/**
* List hosts
*/
public function hostsAction()
{
$this->addTitleTab(
'hosts',
$this->translate('Hosts'),
$this->translate('List hosts')
);
$this->setAutorefreshInterval(10);
// Handle soft and hard states
if (strtolower($this->params->shift('stateType', 'soft')) === 'hard') {
$stateColumn = 'host_hard_state';
$stateChangeColumn = 'host_last_hard_state_change';
} else {
$stateColumn = 'host_state';
$stateChangeColumn = 'host_last_state_change';
}
$hosts = $this->backend->select()->from('hoststatus', array_merge(array(
'host_icon_image',
'host_icon_image_alt',
'host_name',
'host_display_name',
'host_state' => $stateColumn,
'host_acknowledged',
'host_output',
'host_attempt',
'host_in_downtime',
'host_is_flapping',
'host_state_type',
'host_handled',
'host_last_state_change' => $stateChangeColumn,
'host_notifications_enabled',
'host_active_checks_enabled',
'host_passive_checks_enabled',
'host_check_command',
'host_next_update'
), $this->addColumns()));
$this->setupPaginationControl($hosts);
$this->setupSortControl(array(
'host_severity' => $this->translate('Severity'),
'host_state' => $this->translate('Current State'),
'host_display_name' => $this->translate('Hostname'),
'host_address' => $this->translate('Address'),
'host_last_check' => $this->translate('Last Check'),
'host_last_state_change' => $this->translate('Last State Change')
), $hosts);
$this->filterQuery($hosts);
$this->setupLimitControl();
$stats = $this->backend->select()->from('hoststatussummary', array(
'hosts_total',
'hosts_up',
'hosts_down',
'hosts_down_handled',
'hosts_down_unhandled',
'hosts_unreachable',
'hosts_unreachable_handled',
'hosts_unreachable_unhandled',
'hosts_pending',
));
$this->applyRestriction('monitoring/filter/objects', $stats);
$this->view->hosts = $hosts;
$this->view->stats = $stats;
}
/**
* List services
*/
public function servicesAction()
{
$this->addTitleTab(
'services',
$this->translate('Services'),
$this->translate('List services')
);
// Handle soft and hard states
if (strtolower($this->params->shift('stateType', 'soft')) === 'hard') {
$stateColumn = 'service_hard_state';
$stateChangeColumn = 'service_last_hard_state_change';
} else {
$stateColumn = 'service_state';
$stateChangeColumn = 'service_last_state_change';
}
$this->setAutorefreshInterval(10);
$services = $this->backend->select()->from('servicestatus', array_merge(array(
'host_name',
'host_display_name',
'host_state',
'service_description',
'service_display_name',
'service_state' => $stateColumn,
'service_in_downtime',
'service_acknowledged',
'service_handled',
'service_output',
'service_perfdata',
'service_attempt',
'service_last_state_change' => $stateChangeColumn,
'service_icon_image',
'service_icon_image_alt',
'service_is_flapping',
'service_state_type',
'service_handled',
'service_severity',
'service_notifications_enabled',
'service_active_checks_enabled',
'service_passive_checks_enabled',
'service_check_command',
'service_next_update'
), $this->addColumns()));
$this->setupPaginationControl($services);
$this->setupSortControl(array(
'service_severity' => $this->translate('Service Severity'),
'service_state' => $this->translate('Current Service State'),
'service_display_name' => $this->translate('Service Name'),
'service_last_check' => $this->translate('Last Service Check'),
'service_last_state_change' => $this->translate('Last State Change'),
'host_severity' => $this->translate('Host Severity'),
'host_state' => $this->translate('Current Host State'),
'host_display_name' => $this->translate('Hostname'),
'host_address' => $this->translate('Host Address'),
'host_last_check' => $this->translate('Last Host Check')
), $services);
$this->filterQuery($services);
$this->setupLimitControl();
$stats = $this->backend->select()->from('servicestatussummary', array(
'services_critical',
'services_critical_handled',
'services_critical_unhandled',
'services_ok',
'services_pending',
'services_total',
'services_unknown',
'services_unknown_handled',
'services_unknown_unhandled',
'services_warning',
'services_warning_handled',
'services_warning_unhandled'
));
$this->applyRestriction('monitoring/filter/objects', $stats);
$this->view->services = $services;
$this->view->stats = $stats;
if (strpos($this->params->get('host_name', '*'), '*') === false) {
$this->view->showHost = false;
} else {
$this->view->showHost = true;
}
}
/**
* List downtimes
*/
public function downtimesAction()
{
$this->addTitleTab(
'downtimes',
$this->translate('Downtimes'),
$this->translate('List downtimes')
);
$this->setAutorefreshInterval(12);
$downtimes = $this->backend->select()->from('downtime', array(
'id' => 'downtime_internal_id',
'objecttype' => 'object_type',
'comment' => 'downtime_comment',
'author_name' => 'downtime_author_name',
'start' => 'downtime_start',
'scheduled_start' => 'downtime_scheduled_start',
'scheduled_end' => 'downtime_scheduled_end',
'end' => 'downtime_end',
'duration' => 'downtime_duration',
'is_flexible' => 'downtime_is_flexible',
'is_fixed' => 'downtime_is_fixed',
'is_in_effect' => 'downtime_is_in_effect',
'entry_time' => 'downtime_entry_time',
'name' => 'downtime_name',
'host_state',
'service_state',
'host_name',
'service_description',
'host_display_name',
'service_display_name'
));
$this->setupPaginationControl($downtimes);
$this->setupSortControl(array(
'downtime_is_in_effect' => $this->translate('Is In Effect'),
'host_display_name' => $this->translate('Host'),
'service_display_name' => $this->translate('Service'),
'downtime_entry_time' => $this->translate('Entry Time'),
'downtime_author' => $this->translate('Author'),
'downtime_start' => $this->translate('Start Time'),
'downtime_end' => $this->translate('End Time'),
'downtime_scheduled_start' => $this->translate('Scheduled Start'),
'downtime_scheduled_end' => $this->translate('Scheduled End'),
'downtime_duration' => $this->translate('Duration')
), $downtimes);
$this->filterQuery($downtimes);
$this->setupLimitControl();
$this->view->downtimes = $downtimes;
if ($this->Auth()->hasPermission('monitoring/command/downtime/delete')) {
$this->view->delDowntimeForm = new DeleteDowntimeCommandForm();
$this->view->delDowntimeForm->handleRequest();
}
}
/**
* List notifications
*/
public function notificationsAction()
{
$this->addTitleTab(
'notifications',
$this->translate('Notifications'),
$this->translate('List notifications')
);
$this->setAutorefreshInterval(15);
$notifications = $this->backend->select()->from('notification', array(
'id',
'host_display_name',
'host_name',
'notification_contact_name',
'notification_output',
'notification_state',
'notification_timestamp',
'service_description',
'service_display_name'
));
$this->setupPaginationControl($notifications);
$this->setupSortControl(array(
'notification_timestamp' => $this->translate('Notification Start')
), $notifications);
$this->filterQuery($notifications);
$this->setupLimitControl();
$this->view->notifications = $notifications;
}
/**
* List contacts
*/
public function contactsAction()
{
if (! $this->hasPermission('*') && $this->hasPermission('no-monitoring/contacts')) {
throw new SecurityException('No permission for %s', 'monitoring/contacts');
}
$this->addTitleTab(
'contacts',
$this->translate('Contacts'),
$this->translate('List contacts')
);
$contacts = $this->backend->select()->from('contact', array(
'contact_name',
'contact_alias',
'contact_email',
'contact_pager',
'contact_notify_service_timeperiod',
'contact_notify_host_timeperiod'
));
$this->setupPaginationControl($contacts);
$this->setupSortControl(array(
'contact_name' => $this->translate('Name'),
'contact_alias' => $this->translate('Alias'),
'contact_email' => $this->translate('Email'),
'contact_pager' => $this->translate('Pager Address / Number')
), $contacts);
$this->filterQuery($contacts);
$this->setupLimitControl();
$this->view->contacts = $contacts;
}
public function eventgridAction()
{
$this->addTitleTab('eventgrid', $this->translate('Event Grid'), $this->translate('Show the Event Grid'));
$form = new StatehistoryForm();
$form->setEnctype(Zend_Form::ENCTYPE_URLENCODED);
$form->setMethod('get');
$form->setTokenDisabled();
$form->setUidDisabled();
$form->render();
$this->view->form = $form;
$this->params
->remove('showCompact')
->remove('format');
$orientation = $this->params->shift('vertical', 0) ? 'vertical' : 'horizontal';
/*
$orientationBox = new SelectBox(
'orientation',
array(
'0' => mt('monitoring', 'Vertical'),
'1' => mt('monitoring', 'Horizontal')
),
mt('monitoring', 'Orientation'),
'horizontal'
);
$orientationBox->applyRequest($this->getRequest());
*/
$objectType = $form->getValue('objecttype');
$from = $form->getValue('from');
$query = $this->backend->select()->from(
'eventgrid' . $objectType,
array('day', $form->getValue('state'))
);
$this->params->remove(array('objecttype', 'from', 'to', 'state', 'btn_submit'));
$this->view->filter = Filter::fromQueryString((string) $this->params);
$query->applyFilter($this->view->filter);
$query->applyFilter(Filter::fromQueryString('timestamp>=' . $from));
$this->applyRestriction('monitoring/filter/objects', $query);
$this->view->summary = $query;
$this->view->column = $form->getValue('state');
// $this->view->orientationBox = $orientationBox;
$this->view->orientation = $orientation;
}
/**
* List contact groups
*/
public function contactgroupsAction()
{
if (! $this->hasPermission('*') && $this->hasPermission('no-monitoring/contacts')) {
throw new SecurityException('No permission for %s', 'monitoring/contacts');
}
$this->addTitleTab(
'contactgroups',
$this->translate('Contact Groups'),
$this->translate('List contact groups')
);
$contactGroups = $this->backend->select()->from('contactgroup', array(
'contactgroup_name',
'contactgroup_alias',
'contact_count'
));
$this->setupPaginationControl($contactGroups);
$this->setupSortControl(array(
'contactgroup_name' => $this->translate('Contactgroup Name'),
'contactgroup_alias' => $this->translate('Contactgroup Alias')
), $contactGroups);
$this->filterQuery($contactGroups);
$this->setupLimitControl();
$this->view->contactGroups = $contactGroups;
}
/**
* List all comments
*/
public function commentsAction()
{
$this->addTitleTab(
'comments',
$this->translate('Comments'),
$this->translate('List comments')
);
$this->setAutorefreshInterval(12);
$comments = $this->backend->select()->from('comment', array(
'id' => 'comment_internal_id',
'objecttype' => 'object_type',
'comment' => 'comment_data',
'author' => 'comment_author_name',
'timestamp' => 'comment_timestamp',
'type' => 'comment_type',
'persistent' => 'comment_is_persistent',
'expiration' => 'comment_expiration',
'name' => 'comment_name',
'host_name',
'service_description',
'host_display_name',
'service_display_name'
));
$this->setupPaginationControl($comments);
$this->setupSortControl(
array(
'comment_timestamp' => $this->translate('Comment Timestamp'),
'host_display_name' => $this->translate('Host'),
'service_display_name' => $this->translate('Service'),
'comment_type' => $this->translate('Comment Type'),
'comment_expiration' => $this->translate('Expiration')
),
$comments
);
$this->filterQuery($comments);
$this->setupLimitControl();
$this->view->comments = $comments;
if ($this->Auth()->hasPermission('monitoring/command/comment/delete')) {
$this->view->delCommentForm = new DeleteCommentCommandForm();
$this->view->delCommentForm->handleRequest();
}
}
/**
* List service groups
*/
public function servicegroupsAction()
{
$this->addTitleTab(
'servicegroups',
$this->translate('Service Groups'),
$this->translate('List service groups')
);
$this->setAutorefreshInterval(12);
$serviceGroups = $this->backend->select()->from('servicegroupsummary', array(
'servicegroup_alias',
'servicegroup_name',
'services_critical_handled',
'services_critical_unhandled',
'services_ok',
'services_pending',
'services_total',
'services_unknown_handled',
'services_unknown_unhandled',
'services_warning_handled',
'services_warning_unhandled'
));
$this->setupPaginationControl($serviceGroups);
$this->setupSortControl(array(
'servicegroup_alias' => $this->translate('Service Group Name'),
'services_severity' => $this->translate('Severity'),
'services_total' => $this->translate('Total Services')
), $serviceGroups);
$this->filterQuery($serviceGroups);
$this->setupLimitControl();
$this->view->serviceGroups = $serviceGroups;
}
/**
* List service groups
*/
public function servicegroupGridAction()
{
$this->addTitleTab(
'servicegroup-grid',
$this->translate('Service Group Grid'),
$this->translate('Show the Service Group Grid')
);
$this->setAutorefreshInterval(15);
$serviceGroups = $this->backend->select()->from('servicegroupsummary', array(
'servicegroup_alias',
'servicegroup_name',
'services_critical_handled',
'services_critical_unhandled',
'services_ok',
'services_pending',
'services_total',
'services_unknown_handled',
'services_unknown_unhandled',
'services_warning_handled',
'services_warning_unhandled'
));
$this->filterQuery($serviceGroups);
$this->setupSortControl(array(
'servicegroup_alias' => $this->translate('Service Group Name'),
'services_severity' => $this->translate('Severity'),
'services_total' => $this->translate('Total Services')
), $serviceGroups, ['services_severity' => 'desc']);
$this->view->serviceGroups = $serviceGroups;
}
/**
* List host groups
*/
public function hostgroupsAction()
{
$this->addTitleTab(
'hostgroups',
$this->translate('Host Groups'),
$this->translate('List host groups')
);
$this->setAutorefreshInterval(12);
$hostGroups = $this->backend->select()->from('hostgroupsummary', array(
'hostgroup_alias',
'hostgroup_name',
'hosts_down_handled',
'hosts_down_unhandled',
'hosts_pending',
'hosts_total',
'hosts_unreachable_handled',
'hosts_unreachable_unhandled',
'hosts_up',
'services_critical_handled',
'services_critical_unhandled',
'services_ok',
'services_pending',
'services_total',
'services_unknown_handled',
'services_unknown_unhandled',
'services_warning_handled',
'services_warning_unhandled'
));
$this->setupPaginationControl($hostGroups);
$this->setupSortControl(array(
'hostgroup_alias' => $this->translate('Host Group Name'),
'hosts_severity' => $this->translate('Severity'),
'hosts_total' => $this->translate('Total Hosts'),
'services_total' => $this->translate('Total Services')
), $hostGroups);
$this->filterQuery($hostGroups);
$this->setupLimitControl();
$this->view->hostGroups = $hostGroups;
}
/**
* List host groups
*/
public function hostgroupGridAction()
{
$this->addTitleTab(
'hostgroup-grid',
$this->translate('Host Group Grid'),
$this->translate('Show the Host Group Grid')
);
$this->setAutorefreshInterval(15);
$hostGroups = $this->backend->select()->from('hostgroupsummary', [
'hostgroup_alias',
'hostgroup_name',
'hosts_down_handled',
'hosts_down_unhandled',
'hosts_pending',
'hosts_total',
'hosts_unreachable_handled',
'hosts_unreachable_unhandled',
'hosts_up'
]);
$this->filterQuery($hostGroups);
$this->setupSortControl([
'hosts_severity' => $this->translate('Severity'),
'hostgroup_alias' => $this->translate('Host Group Name'),
'hosts_total' => $this->translate('Total Hosts'),
'services_total' => $this->translate('Total Services')
], $hostGroups, ['hosts_severity' => 'desc']);
$this->view->hostGroups = $hostGroups;
}
public function eventhistoryAction()
{
$this->addTitleTab(
'eventhistory',
$this->translate('Event Overview'),
$this->translate('List event records')
);
$query = $this->backend->select()->from('eventhistory', array(
'id',
'host_name',
'host_display_name',
'service_description',
'service_display_name',
'object_type',
'timestamp',
'state',
'output',
'type'
));
$this->view->history = $query;
$this->setupSortControl(array(
'timestamp' => $this->translate('Occurence')
), $query);
$this->filterQuery($query);
$this->setupLimitControl();
}
public function servicegridAction()
{
if ($this->params->has('noscript_apply')) {
$this->redirectNow($this->getRequest()->getUrl()->without('noscript_apply'));
}
$this->addTitleTab('servicegrid', $this->translate('Service Grid'), $this->translate('Show the Service Grid'));
$this->setAutorefreshInterval(15);
$query = $this->backend->select()->from('servicestatus', array(
'host_display_name',
'host_name',
'service_description',
'service_display_name',
'service_handled',
'service_output',
'service_state'
));
$this->filterQuery($query);
$filter = (bool) $this->params->shift('problems', false) ? Filter::where('service_problem', 1) : null;
$this->view->problemToggle = $problemToggle = new Form(['method' => 'GET']);
$problemToggle->setUidDisabled();
$problemToggle->setTokenDisabled();
$problemToggle->setAttrib('class', 'filter-toggle inline icinga-controls');
$problemToggle->addElement('checkbox', 'problems', [
'disableHidden' => true,
'autosubmit' => true,
'value' => $filter !== null,
'label' => $this->translate('Problems Only'),
'decorators' => ['ViewHelper', ['Label', ['placement' => 'APPEND']]]
]);
if ($this->params->get('flipped', false)) {
$pivot = $query
->pivot(
'host_name',
'service_description',
$filter,
$filter ? clone $filter : null
)
->setYAxisHeader('service_display_name')
->setXAxisHeader('host_display_name');
} else {
$pivot = $query
->pivot(
'service_description',
'host_name',
$filter,
$filter ? clone $filter : null
)
->setXAxisHeader('service_display_name')
->setYAxisHeader('host_display_name');
}
$this->setupSortControl(array(
'host_display_name' => $this->translate('Hostname'),
'service_display_name' => $this->translate('Service Name')
), $pivot);
$this->view->horizontalPaginator = $pivot->paginateXAxis();
$this->view->verticalPaginator = $pivot->paginateYAxis();
list($pivotData, $pivotHeader) = $pivot->toArray();
$this->view->pivotData = $pivotData;
$this->view->pivotHeader = $pivotHeader;
if ($this->params->get('flipped', false)) {
$this->render('servicegrid-flipped');
}
}
/**
* Apply filters on a DataView
*
* @param DataView $dataView The DataView to apply filters on
*
* @return DataView $dataView
*/
protected function filterQuery(DataView $dataView)
{
$this->setupFilterControl($dataView, null, null, array(
'format', // handleFormatRequest()
'stateType', // hostsAction() and servicesAction()
'addColumns', // addColumns()
'problems', // servicegridAction()
'flipped' // servicegridAction()
));
if ($this->params->get('format') !== 'sql' || $this->hasPermission('config/authentication/roles/show')) {
$this->applyRestriction('monitoring/filter/objects', $dataView);
}
$this->handleFormatRequest($dataView);
return $dataView;
}
/**
* Get columns to be added from URL parameter 'addColumns'
* and assign to $this->view->addColumns (as array)
*
* @return array
*/
protected function addColumns()
{
$columns = preg_split(
'~,~',
$this->params->shift('addColumns', ''),
-1,
PREG_SPLIT_NO_EMPTY
);
$customVars = [];
$additionalCols = [];
foreach ($columns as $column) {
if (preg_match('~^_(host|service)_([a-zA-Z0-9_]+)$~', $column, $m)) {
$customVars[$m[1]]['vars'][$m[2]] = null;
} else {
$additionalCols[] = $column;
}
}
if (! empty($customVars)) {
$blacklistedProperties = new GlobFilter(
$this->getRestrictions('monitoring/blacklist/properties')
);
$customVars = $blacklistedProperties->removeMatching($customVars);
foreach ($customVars as $type => $vars) {
foreach ($vars['vars'] as $var => $_) {
$additionalCols[] = '_' . $type . '_' . $var;
}
}
}
$this->view->addColumns = $additionalCols;
return $additionalCols;
}
protected function addTitleTab($action, $title, $tip)
{
$this->getTabs()->add($action, array(
'title' => $tip,
'label' => $title,
'url' => Url::fromRequest()
))->activate($action);
$this->view->title = $title;
}
/**
* Return all tabs for this controller
*
* @return Tabs
*/
private function createTabs()
{
return $this->getTabs()->extend(new OutputFormat())->extend(new DashboardAction())->extend(new MenuAction());
}
}

View File

@ -0,0 +1,147 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceCheckCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceDowntimeCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\SendCustomNotificationCommandForm;
use Icinga\Module\Monitoring\Object\Service;
use Icinga\Module\Monitoring\Web\Controller\MonitoredObjectController;
use Icinga\Web\Hook;
use Icinga\Web\Navigation\Navigation;
class ServiceController extends MonitoredObjectController
{
/**
* {@inheritdoc}
*/
protected $commandRedirectUrl = 'monitoring/service/show';
/**
* Fetch the requested service from the monitoring backend
*/
public function init()
{
$service = new Service(
$this->backend,
$this->params->getRequired('host'),
$this->params->getRequired('service')
);
$this->applyRestriction('monitoring/filter/objects', $service);
if ($service->fetch() === false) {
$this->httpNotFound($this->translate('Service not found'));
}
$this->object = $service;
$this->createTabs();
$this->getTabs()->activate('service');
$this->view->title = $service->service_display_name;
$this->view->defaultTitle = join(' :: ', [
$service->host_display_name,
$this->translate('Services'),
$this->view->defaultTitle
]);
}
/**
* Get service actions from hook
*
* @return Navigation
*/
protected function getServiceActions()
{
$navigation = new Navigation();
foreach (Hook::all('Monitoring\\ServiceActions') as $hook) {
$navigation->merge($hook->getNavigation($this->object));
}
return $navigation;
}
/**
* Show a service
*/
public function showAction()
{
$this->view->actions = $this->getServiceActions();
parent::showAction();
}
/**
* Acknowledge a service problem
*/
public function acknowledgeProblemAction()
{
$this->assertPermission('monitoring/command/acknowledge-problem');
$form = new AcknowledgeProblemCommandForm();
$form->setTitle($this->translate('Acknowledge Service Problem'));
$this->handleCommandForm($form);
}
/**
* Add a service comment
*/
public function addCommentAction()
{
$this->assertPermission('monitoring/command/comment/add');
$form = new AddCommentCommandForm();
$form->setTitle($this->translate('Add Service Comment'));
$this->handleCommandForm($form);
}
/**
* Reschedule a service check
*/
public function rescheduleCheckAction()
{
$this->assertPermission('monitoring/command/schedule-check');
$form = new ScheduleServiceCheckCommandForm();
$form->setTitle($this->translate('Reschedule Service Check'));
$this->handleCommandForm($form);
}
/**
* Schedule a service downtime
*/
public function scheduleDowntimeAction()
{
$this->assertPermission('monitoring/command/downtime/schedule');
$form = new ScheduleServiceDowntimeCommandForm();
$form->setTitle($this->translate('Schedule Service Downtime'));
$this->handleCommandForm($form);
}
/**
* Submit a passive service check result
*/
public function processCheckResultAction()
{
$this->assertPermission('monitoring/command/process-check-result');
$form = new ProcessCheckResultCommandForm();
$form->setTitle($this->translate('Submit Passive Service Check Result'));
$this->handleCommandForm($form);
}
/**
* Send a custom notification for a service
*/
public function sendCustomNotificationAction()
{
$this->assertPermission('monitoring/command/send-custom-notification');
$form = new SendCustomNotificationCommandForm();
$form->setTitle($this->translate('Send Custom Service Notification'));
$this->handleCommandForm($form);
}
}

View File

@ -0,0 +1,262 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Exception;
use Icinga\Data\Filter\Filter;
use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Forms\Command\Object\AcknowledgeProblemCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\AddCommentCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\CheckNowCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ObjectsCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ProcessCheckResultCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\RemoveAcknowledgementCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceCheckCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ScheduleServiceDowntimeCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\SendCustomNotificationCommandForm;
use Icinga\Module\Monitoring\Forms\Command\Object\ToggleObjectFeaturesCommandForm;
use Icinga\Module\Monitoring\Hook\DetailviewExtensionHook;
use Icinga\Module\Monitoring\Object\ServiceList;
use Icinga\Web\Hook;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\MenuAction;
class ServicesController extends Controller
{
/**
* @var ServiceList
*/
protected $serviceList;
public function init()
{
$serviceList = new ServiceList($this->backend);
$this->applyRestriction('monitoring/filter/objects', $serviceList);
$serviceList->addFilter(Filter::fromQueryString(
(string) $this->params->without(array('service_problem', 'service_handled', 'showCompact'))
));
$this->serviceList = $serviceList;
$this->serviceList->setColumns(array(
'host_display_name',
'host_handled',
'host_name',
'host_problem',
'host_state',
'instance_name',
'service_acknowledged',
'service_active_checks_enabled',
'service_description',
'service_display_name',
'service_event_handler_enabled',
'service_flap_detection_enabled',
'service_handled',
'service_in_downtime',
'service_is_flapping',
'service_last_state_change',
'service_notifications_enabled',
'service_obsessing',
'service_passive_checks_enabled',
'service_problem',
'service_state'
));
$this->view->baseFilter = $this->serviceList->getFilter();
$this->view->listAllLink = Url::fromRequest()->setPath('monitoring/list/services');
$this->getTabs()->add(
'show',
array(
'label' => $this->translate('Services') . sprintf(' (%d)', count($this->serviceList)),
'title' => sprintf(
$this->translate('Show summarized information for %u services'),
count($this->serviceList)
),
'url' => Url::fromRequest()
)
)->extend(new DashboardAction())->extend(new MenuAction())->activate('show');
$this->view->title = $this->translate('Services');
}
protected function handleCommandForm(ObjectsCommandForm $form)
{
$form
->setBackend($this->backend)
->setObjects($this->serviceList)
->setRedirectUrl(Url::fromPath('monitoring/services/show')->setParams(
$this->params->without('service_active_checks_enabled')
))
->handleRequest();
$this->view->form = $form;
$this->view->objects = $this->serviceList;
$this->view->stats = $this->serviceList->getServiceStateSummary();
$this->view->serviceStates = true;
$this->_helper->viewRenderer('partials/command/objects-command-form', null, true);
return $form;
}
public function showAction()
{
$this->setAutorefreshInterval(15);
$activeChecksEnabled = $this->serviceList->getFeatureStatus()['active_checks_enabled'] !== 0;
if ($this->Auth()->hasPermission('monitoring/command/schedule-check')
|| ($this->Auth()->hasPermission('monitoring/command/schedule-check/active-only')
&& $activeChecksEnabled
)
) {
$checkNowForm = new CheckNowCommandForm();
$checkNowForm
->setObjects($this->serviceList)
->handleRequest();
$this->view->checkNowForm = $checkNowForm;
}
$acknowledgedObjects = $this->serviceList->getAcknowledgedObjects();
if ($acknowledgedObjects->count()) {
$removeAckForm = new RemoveAcknowledgementCommandForm();
$removeAckForm
->setObjects($acknowledgedObjects)
->handleRequest();
$this->view->removeAckForm = $removeAckForm;
}
$featureStatus = $this->serviceList->getFeatureStatus();
$toggleFeaturesForm = new ToggleObjectFeaturesCommandForm(array(
'backend' => $this->backend,
'objects' => $this->serviceList
));
$toggleFeaturesForm
->load((object) $featureStatus)
->handleRequest();
$this->view->toggleFeaturesForm = $toggleFeaturesForm;
if ($activeChecksEnabled) {
$this->view->rescheduleAllLink = Url::fromRequest()
->setPath('monitoring/services/reschedule-check')
->addParams(['service_active_checks_enabled' => true]);
}
$this->view->downtimeAllLink = Url::fromRequest()->setPath('monitoring/services/schedule-downtime');
$this->view->processCheckResultAllLink = Url::fromRequest()->setPath(
'monitoring/services/process-check-result'
);
$this->view->addCommentLink = Url::fromRequest()->setPath('monitoring/services/add-comment');
$this->view->deleteCommentLink = Url::fromRequest()->setPath('monitoring/services/delete-comment');
$this->view->stats = $this->serviceList->getServiceStateSummary();
$this->view->objects = $this->serviceList;
$this->view->unhandledObjects = $this->serviceList->getUnhandledObjects();
$this->view->problemObjects = $this->serviceList->getProblemObjects();
$this->view->downtimeUnhandledLink = Url::fromPath('monitoring/services/schedule-downtime')
->setQueryString($this->serviceList->getUnhandledObjects()->objectsFilter()->toQueryString());
$this->view->downtimeLink = Url::fromPath('monitoring/services/schedule-downtime')
->setQueryString($this->serviceList->getProblemObjects()->objectsFilter()->toQueryString());
$this->view->acknowledgedObjects = $acknowledgedObjects;
$this->view->acknowledgeLink = Url::fromPath('monitoring/services/acknowledge-problem')
->setQueryString($this->serviceList->getUnacknowledgedObjects()->objectsFilter()->toQueryString());
$this->view->unacknowledgedObjects = $this->serviceList->getUnacknowledgedObjects();
$this->view->objectsInDowntime = $this->serviceList->getObjectsInDowntime();
$this->view->inDowntimeLink = Url::fromPath('monitoring/list/services')
->setQueryString($this->serviceList->getObjectsInDowntime()
->objectsFilter(array('host' => 'host_name', 'service' => 'service_description'))->toQueryString());
$this->view->showDowntimesLink = Url::fromPath('monitoring/downtimes/show')
->setQueryString(
$this->serviceList->getObjectsInDowntime()
->objectsFilter()->andFilter(Filter::where('object_type', 'service'))->toQueryString()
);
$this->view->commentsLink = Url::fromRequest()
->setPath('monitoring/list/comments');
$this->view->sendCustomNotificationLink = Url::fromRequest()->setPath(
'monitoring/services/send-custom-notification'
);
$this->view->extensionsHtml = array();
foreach (Hook::all('Monitoring\DetailviewExtension') as $hook) {
/** @var DetailviewExtensionHook $hook */
try {
$html = $hook->setView($this->view)->getHtmlForObjects($this->serviceList);
} catch (Exception $e) {
$html = $this->view->escape($e->getMessage());
}
if ($html) {
$module = $this->view->escape($hook->getModule()->getName());
$this->view->extensionsHtml[] =
'<div class="icinga-module module-' . $module . '" data-icinga-module="' . $module . '">'
. $html
. '</div>';
}
}
}
/**
* Add a service comment
*/
public function addCommentAction()
{
$this->assertPermission('monitoring/command/comment/add');
$form = new AddCommentCommandForm();
$form->setTitle($this->translate('Add Service Comments'));
$this->handleCommandForm($form);
}
/**
* Acknowledge service problems
*/
public function acknowledgeProblemAction()
{
$this->assertPermission('monitoring/command/acknowledge-problem');
$form = new AcknowledgeProblemCommandForm();
$form->setTitle($this->translate('Acknowledge Service Problems'));
$this->handleCommandForm($form);
}
/**
* Reschedule service checks
*/
public function rescheduleCheckAction()
{
$this->assertPermission('monitoring/command/schedule-check');
$form = new ScheduleServiceCheckCommandForm();
$form->setTitle($this->translate('Reschedule Service Checks'));
$this->handleCommandForm($form);
}
/**
* Schedule service downtimes
*/
public function scheduleDowntimeAction()
{
$this->assertPermission('monitoring/command/downtime/schedule');
$form = new ScheduleServiceDowntimeCommandForm();
$form->setTitle($this->translate('Schedule Service Downtimes'));
$this->handleCommandForm($form);
}
/**
* Submit passive service check results
*/
public function processCheckResultAction()
{
$this->assertPermission('monitoring/command/process-check-result');
$form = new ProcessCheckResultCommandForm();
$form->setTitle($this->translate('Submit Passive Service Check Results'));
$this->handleCommandForm($form);
}
/**
* Send a custom notification for services
*/
public function sendCustomNotificationAction()
{
$this->assertPermission('monitoring/command/send-custom-notification');
$form = new SendCustomNotificationCommandForm();
$form->setTitle($this->translate('Send Custom Service Notification'));
$this->handleCommandForm($form);
}
}

View File

@ -0,0 +1,101 @@
<?php
/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Data\Filter\FilterEqual;
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
use Icinga\Module\Monitoring\Controller;
use Icinga\Security\SecurityException;
use Icinga\Web\Url;
/**
* Class Monitoring_ShowController
*
* Actions for show context
*/
class ShowController extends Controller
{
/**
* @var MonitoringBackend
*/
protected $backend;
public function init()
{
$this->view->defaultTitle = $this->translate('Contacts') . ' :: ' . $this->view->defaultTitle;
parent::init();
}
public function contactAction()
{
if (! $this->hasPermission('*') && $this->hasPermission('no-monitoring/contacts')) {
throw new SecurityException('No permission for %s', 'monitoring/contacts');
}
$contactName = $this->params->getRequired('contact_name');
$this->getTabs()->add('contact-detail', [
'title' => $this->translate('Contact details'),
'label' => $this->translate('Contact'),
'url' => Url::fromRequest(),
'active' => true
]);
$query = $this->backend->select()->from('contact', array(
'contact_name',
'contact_id',
'contact_alias',
'contact_email',
'contact_pager',
'contact_notify_service_timeperiod',
'contact_notify_service_recovery',
'contact_notify_service_warning',
'contact_notify_service_critical',
'contact_notify_service_unknown',
'contact_notify_service_flapping',
'contact_notify_service_downtime',
'contact_notify_host_timeperiod',
'contact_notify_host_recovery',
'contact_notify_host_down',
'contact_notify_host_unreachable',
'contact_notify_host_flapping',
'contact_notify_host_downtime',
));
$this->applyRestriction('monitoring/filter/objects', $query);
$query->whereEx(new FilterEqual('contact_name', '=', $contactName));
$contact = $query->getQuery()->fetchRow();
if ($contact) {
$commands = $this->backend->select()->from('command', array(
'command_line',
'command_name'
))->where('contact_id', $contact->contact_id);
$this->view->commands = $commands;
$notifications = $this->backend->select()->from('notification', array(
'id',
'host_name',
'service_description',
'notification_output',
'notification_contact_name',
'notification_timestamp',
'notification_state',
'host_display_name',
'service_display_name'
));
$notifications->where('notification_contact_name', $contactName);
$this->applyRestriction('monitoring/filter/objects', $notifications);
$this->view->notifications = $notifications;
$this->setupLimitControl();
$this->setupPaginationControl($this->view->notifications);
$this->view->title = $contact->contact_name;
}
$this->view->contact = $contact;
$this->view->contactName = $contactName;
}
}

View File

@ -0,0 +1,128 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use Icinga\Chart\Donut;
use Icinga\Module\Monitoring\Controller;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\MenuAction;
class TacticalController extends Controller
{
public function indexAction()
{
$this->setAutorefreshInterval(15);
$this->view->title = $this->translate('Tactical Overview');
$this->getTabs()->add(
'tactical_overview',
array(
'title' => $this->translate(
'Show an overview of all hosts and services, their current'
. ' states and monitoring feature utilisation'
),
'label' => $this->translate('Tactical Overview'),
'url' => Url::fromRequest()
)
)->extend(new DashboardAction())->extend(new MenuAction())->activate('tactical_overview');
$stats = $this->backend->select()->from(
'statussummary',
array(
'hosts_up',
'hosts_down_handled',
'hosts_down_unhandled',
'hosts_unreachable_handled',
'hosts_unreachable_unhandled',
'hosts_pending',
'hosts_pending_not_checked',
'hosts_not_checked',
'services_ok',
'services_warning_handled',
'services_warning_unhandled',
'services_critical_handled',
'services_critical_unhandled',
'services_unknown_handled',
'services_unknown_unhandled',
'services_pending',
'services_pending_not_checked',
'services_not_checked',
)
);
$this->applyRestriction('monitoring/filter/objects', $stats);
$this->setupFilterControl($stats, null, ['host', 'service'], ['format']);
$this->view->setHelperFunction('filteredUrl', function ($path, array $params) {
$filter = clone $this->view->filterEditor->getFilter();
return $this->view->url($path)->setParams($params)->addFilter($filter);
});
$this->handleFormatRequest($stats);
$summary = $stats->fetchRow();
// Correct pending counts. Done here instead of in the query for compatibility reasons.
$summary->hosts_pending -= $summary->hosts_pending_not_checked;
$summary->services_pending -= $summary->services_pending_not_checked;
$hostSummaryChart = new Donut();
$hostSummaryChart
->addSlice($summary->hosts_up, array('class' => 'slice-state-ok'))
->addSlice($summary->hosts_down_handled, array('class' => 'slice-state-critical-handled'))
->addSlice($summary->hosts_down_unhandled, array('class' => 'slice-state-critical'))
->addSlice($summary->hosts_unreachable_handled, array('class' => 'slice-state-unreachable-handled'))
->addSlice($summary->hosts_unreachable_unhandled, array('class' => 'slice-state-unreachable'))
->addSlice($summary->hosts_pending, array('class' => 'slice-state-pending'))
->addSlice($summary->hosts_pending_not_checked, array('class' => 'slice-state-not-checked'))
->setLabelBig($summary->hosts_down_unhandled)
->setLabelBigEyeCatching($summary->hosts_down_unhandled > 0)
->setLabelSmall($this->translate('Hosts Down'));
$serviceSummaryChart = new Donut();
$serviceSummaryChart
->addSlice($summary->services_ok, array('class' => 'slice-state-ok'))
->addSlice($summary->services_warning_handled, array('class' => 'slice-state-warning-handled'))
->addSlice($summary->services_warning_unhandled, array('class' => 'slice-state-warning'))
->addSlice($summary->services_critical_handled, array('class' => 'slice-state-critical-handled'))
->addSlice($summary->services_critical_unhandled, array('class' => 'slice-state-critical'))
->addSlice($summary->services_unknown_handled, array('class' => 'slice-state-unknown-handled'))
->addSlice($summary->services_unknown_unhandled, array('class' => 'slice-state-unknown'))
->addSlice($summary->services_pending, array('class' => 'slice-state-pending'))
->addSlice($summary->services_pending_not_checked, array('class' => 'slice-state-not-checked'))
->setLabelBig($summary->services_critical_unhandled ?: $summary->services_unknown_unhandled)
->setLabelBigState($summary->services_critical_unhandled > 0 ? 'critical' : (
$summary->services_unknown_unhandled > 0 ? 'unknown' : null
))
->setLabelSmall($summary->services_critical_unhandled > 0 || $summary->services_unknown_unhandled < 1
? $this->translate('Services Critical')
: $this->translate('Services Unknown'));
$this->view->hostStatusSummaryChart = $hostSummaryChart
->setLabelBigUrl($this->view->filteredUrl(
'monitoring/list/hosts',
array(
'host_state' => 1,
'host_handled' => 0,
'sort' => 'host_last_check',
'dir' => 'asc'
)
))
->render();
$this->view->serviceStatusSummaryChart = $serviceSummaryChart
->setLabelBigUrl($this->view->filteredUrl(
'monitoring/list/services',
array(
'service_state' => $summary->services_critical_unhandled > 0
|| ! $summary->services_unknown_unhandled ? 2 : 3,
'service_handled' => 0,
'sort' => 'service_last_check',
'dir' => 'asc'
)
))
->render();
$this->view->statusSummary = $summary;
}
}

View File

@ -0,0 +1,325 @@
<?php
/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Controllers;
use DateInterval;
use DateTime;
use Icinga\Module\Monitoring\Controller;
use Icinga\Module\Monitoring\Timeline\TimeLine;
use Icinga\Module\Monitoring\Timeline\TimeRange;
use Icinga\Module\Monitoring\Web\Widget\SelectBox;
use Icinga\Util\Format;
use Icinga\Web\Url;
use Icinga\Web\Widget\Tabextension\DashboardAction;
use Icinga\Web\Widget\Tabextension\MenuAction;
class TimelineController extends Controller
{
public function indexAction()
{
$this->getTabs()->add(
'timeline',
array(
'title' => $this->translate('Show the number of historical event records grouped by time and type'),
'label' => $this->translate('Timeline'),
'url' => Url::fromRequest()
)
)->extend(new DashboardAction())->extend(new MenuAction())->activate('timeline');
$this->view->title = $this->translate('Timeline');
// TODO: filter for hard_states (precedence adjustments necessary!)
$this->setupIntervalBox();
list($displayRange, $forecastRange) = $this->buildTimeRanges();
$detailUrl = Url::fromPath('monitoring/list/eventhistory');
$timeline = new TimeLine(
$this->applyRestriction(
'monitoring/filter/objects',
$this->backend->select()->from(
'eventhistory',
array(
'name' => 'type',
'time' => 'timestamp'
)
)
),
array(
'notification_ack' => array(
'class' => 'timeline-notification',
'detailUrl' => $detailUrl,
'label' => mt('monitoring', 'Notifications'),
'groupBy' => 'notification_*'
),
'notification_flapping' => array(
'class' => 'timeline-notification',
'detailUrl' => $detailUrl,
'label' => mt('monitoring', 'Notifications'),
'groupBy' => 'notification_*'
),
'notification_flapping_end' => array(
'class' => 'timeline-notification',
'detailUrl' => $detailUrl,
'label' => mt('monitoring', 'Notifications'),
'groupBy' => 'notification_*'
),
'notification_dt_start' => array(
'class' => 'timeline-notification',
'detailUrl' => $detailUrl,
'label' => mt('monitoring', 'Notifications'),
'groupBy' => 'notification_*'
),
'notification_dt_end' => array(
'class' => 'timeline-notification',
'detailUrl' => $detailUrl,
'label' => mt('monitoring', 'Notifications'),
'groupBy' => 'notification_*'
),
'notification_custom' => array(
'class' => 'timeline-notification',
'detailUrl' => $detailUrl,
'label' => mt('monitoring', 'Notifications'),
'groupBy' => 'notification_*'
),
'notification_state' => array(
'class' => 'timeline-notification',
'detailUrl' => $detailUrl,
'label' => mt('monitoring', 'Notifications'),
'groupBy' => 'notification_*'
),
'hard_state' => array(
'class' => 'timeline-hard-state',
'detailUrl' => $detailUrl,
'label' => mt('monitoring', 'Hard state changes')
),
'comment' => array(
'class' => 'timeline-comment',
'detailUrl' => $detailUrl,
'label' => mt('monitoring', 'Comments')
),
'ack' => array(
'class' => 'timeline-ack',
'detailUrl' => $detailUrl,
'label' => mt('monitoring', 'Acknowledgements')
),
'dt_start' => array(
'class' => 'timeline-downtime-start',
'detailUrl' => $detailUrl,
'label' => mt('monitoring', 'Started downtimes')
),
'dt_end' => array(
'class' => 'timeline-downtime-end',
'detailUrl' => $detailUrl,
'label' => mt('monitoring', 'Ended downtimes')
)
)
);
$timeline->setMaximumCircleWidth('6em');
$timeline->setMinimumCircleWidth('0.3em');
$timeline->setDisplayRange($displayRange);
$timeline->setForecastRange($forecastRange);
$beingExtended = $this->getRequest()->getParam('extend') == 1;
$timeline->setSession($this->Window()->getSessionNamespace('timeline', !$beingExtended));
$this->view->timeline = $timeline;
$this->view->nextRange = $forecastRange;
$this->view->beingExtended = $beingExtended;
$this->view->intervalFormat = $this->getIntervalFormat();
$oldBase = $timeline->getCalculationBase(false);
$this->view->switchedContext = $oldBase !== null && $oldBase !== $timeline->getCalculationBase(true);
}
/**
* Create a select box the user can choose the timeline interval from
*/
private function setupIntervalBox()
{
$box = new SelectBox(
'intervalBox',
array(
'4h' => mt('monitoring', '4 Hours'),
'1d' => mt('monitoring', 'One day'),
'1w' => mt('monitoring', 'One week'),
'1m' => mt('monitoring', 'One month'),
'1y' => mt('monitoring', 'One year')
),
mt('monitoring', 'TimeLine interval'),
'interval'
);
$box->applyRequest($this->getRequest());
$this->view->intervalBox = $box;
}
/**
* Return the chosen interval
*
* @return DateInterval The chosen interval
*/
private function getTimelineInterval()
{
switch ($this->view->intervalBox->getInterval()) {
case '1d':
return new DateInterval('P1D');
case '1w':
return new DateInterval('P1W');
case '1m':
return new DateInterval('P1M');
case '1y':
return new DateInterval('P1Y');
default:
return new DateInterval('PT4H');
}
}
/**
* Get an appropriate datetime format string for the chosen interval
*
* @return string
*/
private function getIntervalFormat()
{
switch ($this->view->intervalBox->getInterval()) {
case '1d':
return $this->getDateFormat();
case '1w':
return '\W\e\ek W\<b\r\>\of Y';
case '1m':
return 'F Y';
case '1y':
return 'Y';
default:
return $this->getDateFormat() . '\<b\r\>' . $this->getTimeFormat();
}
}
/**
* Return a preload interval based on the chosen timeline interval and the given date and time
*
* @param DateTime $dateTime The date and time to use
*
* @return DateInterval The interval to pre-load
*/
private function getPreloadInterval(DateTime $dateTime)
{
switch ($this->view->intervalBox->getInterval()) {
case '1d':
return DateInterval::createFromDateString('1 week -1 second');
case '1w':
return DateInterval::createFromDateString('8 weeks -1 second');
case '1m':
$dateCopy = clone $dateTime;
for ($i = 0; $i < 6; $i++) {
$dateCopy->sub(new DateInterval('PT' . Format::secondsByMonth($dateCopy) . 'S'));
}
return $dateCopy->add(new DateInterval('PT1S'))->diff($dateTime);
case '1y':
$dateCopy = clone $dateTime;
for ($i = 0; $i < 4; $i++) {
$dateCopy->sub(new DateInterval('PT' . Format::secondsByYear($dateCopy) . 'S'));
}
return $dateCopy->add(new DateInterval('PT1S'))->diff($dateTime);
default:
return DateInterval::createFromDateString('1 day -1 second');
}
}
/**
* Extrapolate the given datetime based on the chosen timeline interval
*
* @param DateTime $dateTime The datetime to extrapolate
*/
private function extrapolateDateTime(DateTime &$dateTime)
{
switch ($this->view->intervalBox->getInterval()) {
case '1d':
$dateTime->setTimestamp(strtotime('tomorrow', $dateTime->getTimestamp()) - 1);
break;
case '1w':
$dateTime->setTimestamp(strtotime('next monday', $dateTime->getTimestamp()) - 1);
break;
case '1m':
$dateTime->setTimestamp(
strtotime(
'last day of this month',
strtotime(
'tomorrow',
$dateTime->getTimestamp()
) - 1
)
);
break;
case '1y':
$dateTime->setTimestamp(strtotime('1 january next year', $dateTime->getTimestamp()) - 1);
break;
default:
$hour = $dateTime->format('G');
$end = $hour < 4 ? 4 : ($hour < 8 ? 8 : ($hour < 12 ? 12 : ($hour < 16 ? 16 : ($hour < 20 ? 20 : 24))));
$dateTime = DateTime::createFromFormat(
'd/m/y G:i:s',
$dateTime->format('d/m/y') . ($end - 1) . ':59:59'
);
}
}
/**
* Return a display- and forecast time range
*
* Assembles a time range each for display and forecast purposes based on the start- and
* end time if given in the current request otherwise based on the current time and a
* end time that is calculated based on the chosen timeline interval.
*
* @return array The resulting time ranges
*/
private function buildTimeRanges()
{
$startTime = new DateTime();
$startParam = $this->_request->getParam('start');
$startTimestamp = is_numeric($startParam) ? intval($startParam) : strtotime($startParam ?? '');
if ($startTimestamp !== false) {
$startTime->setTimestamp($startTimestamp);
} else {
$this->extrapolateDateTime($startTime);
}
$endTime = clone $startTime;
$endParam = $this->_request->getParam('end');
$endTimestamp = is_numeric($endParam) ? intval($endParam) : strtotime($endParam ?? '');
if ($endTimestamp !== false) {
$endTime->setTimestamp($endTimestamp);
} else {
$endTime->sub($this->getPreloadInterval($startTime));
}
$forecastStart = clone $endTime;
$forecastStart->sub(new DateInterval('PT1S'));
$forecastEnd = clone $forecastStart;
$forecastEnd->sub($this->getPreloadInterval($forecastStart));
$timelineInterval = $this->getTimelineInterval();
return array(
new TimeRange($startTime, $endTime, $timelineInterval),
new TimeRange($forecastStart, $forecastEnd, $timelineInterval)
);
}
/**
* Get the user's preferred time format or the application's default
*
* @return string
*/
private function getTimeFormat()
{
return 'H:i';
}
/**
* Get the user's preferred date format or the application's default
*
* @return string
*/
private function getDateFormat()
{
return 'Y-m-d';
}
}

View File

@ -0,0 +1,92 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command;
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
*/
abstract class CommandForm extends Form
{
/**
* Monitoring backend
*
* @var MonitoringBackend
*/
protected $backend;
/**
* Set the monitoring backend
*
* @param MonitoringBackend $backend
*
* @return $this
*/
public function setBackend(MonitoringBackend $backend)
{
$this->backend = $backend;
return $this;
}
/**
* Get the monitoring backend
*
* @return MonitoringBackend
*/
public function getBackend()
{
return $this->backend;
}
/**
* Get the transport used to send commands
*
* @param Request $request
*
* @return CommandTransportInterface
*
* @throws ConfigurationError
*/
public function getTransport(Request $request)
{
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 = new CommandTransport();
}
return $transport;
}
/**
* {@inheritdoc}
*/
public function getRedirectUrl()
{
$redirectUrl = parent::getRedirectUrl();
// TODO(el): Forms should provide event handling. This is quite hackish
$formData = $this->getRequestData();
if ($this->wasSent($formData)
&& (! $this->getSubmitLabel() || $this->isSubmitted())
&& $this->isValid($formData)
) {
$this->getResponse()->setAutoRefreshInterval(1);
}
return $redirectUrl;
}
}

View File

@ -0,0 +1,64 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Instance;
use DateTime;
use DateInterval;
use Icinga\Module\Monitoring\Command\Instance\DisableNotificationsExpireCommand;
use Icinga\Module\Monitoring\Forms\Command\CommandForm;
use Icinga\Web\Notification;
/**
* Form for disabling host and service notifications w/ an optional expire date and time on an Icinga instance
*/
class DisableNotificationsExpireCommandForm extends CommandForm
{
/**
* (non-PHPDoc)
* @see \Zend_Form::init() For the method documentation.
*/
public function init()
{
$this->setRequiredCue(null);
$this->setSubmitLabel($this->translate('Disable Notifications'));
$this->addDescription($this->translate(
'This command is used to disable host and service notifications for a specific time.'
));
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::createElements() For the method documentation.
*/
public function createElements(array $formData = array())
{
$expireTime = new DateTime();
$expireTime->add(new DateInterval('PT1H'));
$this->addElement(
'dateTimePicker',
'expire_time',
array(
'required' => true,
'label' => $this->translate('Expire Time'),
'description' => $this->translate('Set the expire time.'),
'value' => $expireTime
)
);
return $this;
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::onSuccess() For the method documentation.
*/
public function onSuccess()
{
$disableNotifications = new DisableNotificationsExpireCommand();
$disableNotifications
->setExpireTime($this->getElement('expire_time')->getValue()->getTimestamp());
$this->getTransport($this->request)->send($disableNotifications);
Notification::success($this->translate('Disabling host and service notifications..'));
return true;
}
}

View File

@ -0,0 +1,279 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Instance;
use Icinga\Module\Monitoring\Command\Instance\ToggleInstanceFeatureCommand;
use Icinga\Module\Monitoring\Forms\Command\CommandForm;
use Icinga\Web\Notification;
/**
* Form for enabling or disabling features of Icinga instances
*/
class ToggleInstanceFeaturesCommandForm extends CommandForm
{
/**
* Instance status
*
* @var object
*/
protected $status;
/**
* (non-PHPDoc)
* @see \Zend_Form::init() For the method documentation.
*/
public function init()
{
$this->setUseFormAutosubmit();
$this->setAttrib('class', self::DEFAULT_CLASSES . ' instance-features');
}
/**
* Set the instance status
*
* @param object $status
*
* @return $this
*/
public function setStatus($status)
{
$this->status = (object) $status;
return $this;
}
/**
* Get the instance status
*
* @return object
*/
public function getStatus()
{
return $this->status;
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::createElements() For the method documentation.
*/
public function createElements(array $formData = array())
{
$notificationDescription = null;
$isIcinga2 = $this->getBackend()->isIcinga2($this->status->program_version);
if (! $isIcinga2) {
if ((bool) $this->status->notifications_enabled) {
if ($this->hasPermission('monitoring/command/feature/instance')) {
$notificationDescription = sprintf(
'<a aria-label="%1$s" class="action-link" title="%1$s"'
. ' href="%2$s" data-base-target="_next">%3$s</a>',
$this->translate('Disable notifications for a specific time on a program-wide basis'),
$this->getView()->href('monitoring/health/disable-notifications'),
$this->translate('Disable temporarily')
);
} else {
$notificationDescription = null;
}
} elseif ($this->status->disable_notif_expire_time) {
$notificationDescription = sprintf(
$this->translate('Notifications will be re-enabled in <strong>%s</strong>'),
$this->getView()->timeUntil($this->status->disable_notif_expire_time)
);
}
}
$toggleDisabled = $this->hasPermission('monitoring/command/feature/instance') ? null : '';
$this->addElement(
'checkbox',
ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS,
array(
'label' => $this->translate('Active Host Checks'),
'autosubmit' => true,
'disabled' => $toggleDisabled
)
);
$this->addElement(
'checkbox',
ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS,
array(
'label' => $this->translate('Active Service Checks'),
'autosubmit' => true,
'disabled' => $toggleDisabled
)
);
$this->addElement(
'checkbox',
ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS,
array(
'label' => $this->translate('Event Handlers'),
'autosubmit' => true,
'disabled' => $toggleDisabled
)
);
$this->addElement(
'checkbox',
ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION,
array(
'label' => $this->translate('Flap Detection'),
'autosubmit' => true,
'disabled' => $toggleDisabled
)
);
$this->addElement(
'checkbox',
ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS,
array(
'label' => $this->translate('Notifications'),
'autosubmit' => true,
'description' => $notificationDescription,
'decorators' => array(
array('Label', array('tag'=>'span', 'separator' => '', 'class' => 'control-label')),
array(
'Description',
array('tag' => 'span', 'class' => 'description', 'escape' => false)
),
array(array('labelWrap' => 'HtmlTag'), array('tag' => 'div', 'class' => 'control-label-group')),
array('ViewHelper', array('separator' => '')),
array('Errors', array('separator' => '')),
array('HtmlTag', array('tag' => 'div', 'class' => 'control-group'))
),
'disabled' => $toggleDisabled
)
);
if (! $isIcinga2) {
$this->addElement(
'checkbox',
ToggleInstanceFeatureCommand::FEATURE_HOST_OBSESSING,
array(
'label' => $this->translate('Obsessing Over Hosts'),
'autosubmit' => true,
'disabled' => $toggleDisabled
)
);
$this->addElement(
'checkbox',
ToggleInstanceFeatureCommand::FEATURE_SERVICE_OBSESSING,
array(
'label' => $this->translate('Obsessing Over Services'),
'autosubmit' => true,
'disabled' => $toggleDisabled
)
);
$this->addElement(
'checkbox',
ToggleInstanceFeatureCommand::FEATURE_PASSIVE_HOST_CHECKS,
array(
'label' => $this->translate('Passive Host Checks'),
'autosubmit' => true,
'disabled' => $toggleDisabled
)
);
$this->addElement(
'checkbox',
ToggleInstanceFeatureCommand::FEATURE_PASSIVE_SERVICE_CHECKS,
array(
'label' => $this->translate('Passive Service Checks'),
'autosubmit' => true,
'disabled' => $toggleDisabled
)
);
}
$this->addElement(
'checkbox',
ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA,
array(
'label' => $this->translate('Performance Data'),
'autosubmit' => true,
'disabled' => $toggleDisabled
)
);
}
/**
* Load feature status
*
* @param object $instanceStatus
*
* @return $this
*/
public function load($instanceStatus)
{
$this->create();
foreach ($this->getValues() as $feature => $enabled) {
$this->getElement($feature)->setChecked($instanceStatus->{$feature});
}
return $this;
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::onSuccess() For the method documentation.
*/
public function onSuccess()
{
$this->assertPermission('monitoring/command/feature/instance');
$notifications = array(
ToggleInstanceFeatureCommand::FEATURE_ACTIVE_HOST_CHECKS => array(
$this->translate('Enabling active host checks..'),
$this->translate('Disabling active host checks..')
),
ToggleInstanceFeatureCommand::FEATURE_ACTIVE_SERVICE_CHECKS => array(
$this->translate('Enabling active service checks..'),
$this->translate('Disabling active service checks..')
),
ToggleInstanceFeatureCommand::FEATURE_EVENT_HANDLERS => array(
$this->translate('Enabling event handlers..'),
$this->translate('Disabling event handlers..')
),
ToggleInstanceFeatureCommand::FEATURE_FLAP_DETECTION => array(
$this->translate('Enabling flap detection..'),
$this->translate('Disabling flap detection..')
),
ToggleInstanceFeatureCommand::FEATURE_NOTIFICATIONS => array(
$this->translate('Enabling notifications..'),
$this->translate('Disabling notifications..')
),
ToggleInstanceFeatureCommand::FEATURE_HOST_OBSESSING => array(
$this->translate('Enabling obsessing over hosts..'),
$this->translate('Disabling obsessing over hosts..')
),
ToggleInstanceFeatureCommand::FEATURE_SERVICE_OBSESSING => array(
$this->translate('Enabling obsessing over services..'),
$this->translate('Disabling obsessing over services..')
),
ToggleInstanceFeatureCommand::FEATURE_PASSIVE_HOST_CHECKS => array(
$this->translate('Enabling passive host checks..'),
$this->translate('Disabling passive host checks..')
),
ToggleInstanceFeatureCommand::FEATURE_PASSIVE_SERVICE_CHECKS => array(
$this->translate('Enabling passive service checks..'),
$this->translate('Disabling passive service checks..')
),
ToggleInstanceFeatureCommand::FEATURE_PERFORMANCE_DATA => array(
$this->translate('Enabling performance data..'),
$this->translate('Disabling performance data..')
)
);
foreach ($this->getValues() as $feature => $enabled) {
if ((bool) $this->status->{$feature} !== (bool) $enabled) {
$toggleFeature = new ToggleInstanceFeatureCommand();
$toggleFeature
->setFeature($feature)
->setEnabled($enabled);
$this->getTransport($this->request)->send($toggleFeature);
Notification::success(
$notifications[$feature][$enabled ? 0 : 1]
);
}
}
return true;
}
}

View File

@ -0,0 +1,172 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use DateTime;
use DateInterval;
use Icinga\Application\Config;
use Icinga\Module\Monitoring\Command\Object\AcknowledgeProblemCommand;
use Icinga\Web\Notification;
/**
* Form for acknowledging host or service problems
*/
class AcknowledgeProblemCommandForm extends ObjectsCommandForm
{
/**
* Initialize this form
*/
public function init()
{
$this->addDescription($this->translate(
'This command is used to acknowledge host or service problems. When a problem is acknowledged,'
. ' future notifications about problems are temporarily disabled until the host or service'
. ' recovers.'
));
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::getSubmitLabel() For the method documentation.
*/
public function getSubmitLabel()
{
return $this->translatePlural('Acknowledge problem', 'Acknowledge problems', count($this->objects));
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::createElements() For the method documentation.
*/
public function createElements(array $formData = array())
{
$config = Config::module('monitoring');
$acknowledgeExpire = (bool) $config->get('settings', 'acknowledge_expire', false);
$this->addElements(array(
array(
'textarea',
'comment',
array(
'required' => true,
'label' => $this->translate('Comment'),
'description' => $this->translate(
'If you work with other administrators, you may find it useful to share information about'
. ' the host or service that is having problems. Make sure you enter a brief description of'
. ' what you are doing.'
),
'attribs' => array('class' => 'autofocus')
)
),
array(
'checkbox',
'persistent',
array(
'label' => $this->translate('Persistent Comment'),
'value' => (bool) $config->get('settings', 'acknowledge_persistent', false),
'description' => $this->translate(
'If you would like the comment to remain even when the acknowledgement is removed, check this'
. ' option.'
)
)
),
array(
'checkbox',
'expire',
array(
'label' => $this->translate('Use Expire Time'),
'value' => $acknowledgeExpire,
'description' => $this->translate(
'If the acknowledgement should expire, check this option.'
),
'autosubmit' => true
)
)
));
$expire = isset($formData['expire']) ? $formData['expire'] : $acknowledgeExpire;
if ($expire) {
$expireTime = new DateTime();
$expireTime->add(new DateInterval($config->get('settings', 'acknowledge_expire_time', 'PT1H')));
$this->addElement(
'dateTimePicker',
'expire_time',
array(
'label' => $this->translate('Expire Time'),
'value' => $expireTime,
'description' => $this->translate(
'Enter the expire date and time for this acknowledgement here. Icinga will delete the'
. ' acknowledgement after this time expired.'
)
)
);
$this->addDisplayGroup(
array('expire', 'expire_time'),
'expire-expire_time',
array(
'decorators' => array(
'FormElements',
array('HtmlTag', array('tag' => 'div'))
)
)
);
}
$this->addElements(array(
array(
'checkbox',
'sticky',
array(
'label' => $this->translate('Sticky Acknowledgement'),
'value' => (bool) $config->get('settings', 'acknowledge_sticky', false),
'description' => $this->translate(
'If you want the acknowledgement to remain until the host or service recovers even if the host'
. ' or service changes state, check this option.'
)
)
),
array(
'checkbox',
'notify',
array(
'label' => $this->translate('Send Notification'),
'value' => (bool) $config->get('settings', 'acknowledge_notify', true),
'description' => $this->translate(
'If you do not want an acknowledgement notification to be sent out to the appropriate contacts,'
. ' uncheck this option.'
)
)
)
));
return $this;
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::onSuccess() For the method documentation.
*/
public function onSuccess()
{
foreach ($this->objects as $object) {
/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */
$ack = new AcknowledgeProblemCommand();
$ack
->setObject($object)
->setComment($this->getElement('comment')->getValue())
->setAuthor($this->request->getUser()->getUsername())
->setPersistent($this->getElement('persistent')->isChecked())
->setSticky($this->getElement('sticky')->isChecked())
->setNotify($this->getElement('notify')->isChecked());
if ($this->getElement('expire')->isChecked()) {
$ack->setExpireTime($this->getElement('expire_time')->getValue()->getTimestamp());
}
$this->getTransport($this->request)->send($ack);
}
Notification::success($this->translatePlural(
'Acknowledging problem..',
'Acknowledging problems..',
count($this->objects)
));
return true;
}
}

View File

@ -0,0 +1,148 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use DateInterval;
use DateTime;
use Icinga\Application\Config;
use Icinga\Module\Monitoring\Command\Object\AddCommentCommand;
use Icinga\Web\Notification;
/**
* Form for adding host or service comments
*/
class AddCommentCommandForm extends ObjectsCommandForm
{
/**
* Initialize this form
*/
public function init()
{
$this->addDescription($this->translate('This command is used to add host or service comments.'));
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::getSubmitLabel() For the method documentation.
*/
public function getSubmitLabel()
{
return $this->translatePlural('Add comment', 'Add comments', count($this->objects));
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::createElements() For the method documentation.
*/
public function createElements(array $formData = array())
{
$this->addElement(
'textarea',
'comment',
array(
'required' => true,
'label' => $this->translate('Comment'),
'description' => $this->translate(
'If you work with other administrators, you may find it useful to share information about'
. ' the host or service that is having problems. Make sure you enter a brief description of'
. ' what you are doing.'
),
'attribs' => array('class' => 'autofocus')
)
);
if (! $this->getBackend()->isIcinga2()) {
$this->addElement(
'checkbox',
'persistent',
array(
'label' => $this->translate('Persistent'),
'value' => (bool) Config::module('monitoring')->get('settings', 'comment_persistent', true),
'description' => $this->translate(
'If you uncheck this option, the comment will automatically be deleted the next time Icinga is'
. ' restarted.'
)
)
);
}
if (version_compare($this->getBackend()->getProgramVersion(), '2.13.0', '>=')) {
$config = Config::module('monitoring');
$commentExpire = (bool) $config->get('settings', 'comment_expire', false);
$this->addElement(
'checkbox',
'expire',
[
'label' => $this->translate('Use Expire Time'),
'value' => $commentExpire,
'description' => $this->translate('If the comment should expire, check this option.'),
'autosubmit' => true
]
);
if (isset($formData['expire']) ? $formData['expire'] : $commentExpire) {
$expireTime = new DateTime();
$expireTime->add(new DateInterval($config->get('settings', 'comment_expire_time', 'PT1H')));
$this->addElement(
'dateTimePicker',
'expire_time',
[
'label' => $this->translate('Expire Time'),
'value' => $expireTime,
'description' => $this->translate(
'Enter the expire date and time for this comment here. Icinga will delete the'
. ' comment after this time expired.'
)
]
);
$this->addDisplayGroup(
['expire', 'expire_time'],
'expire-expire_time',
[
'decorators' => [
'FormElements',
['HtmlTag', ['tag' => 'div']]
]
]
);
}
}
return $this;
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::onSuccess() For the method documentation.
*/
public function onSuccess()
{
foreach ($this->objects as $object) {
/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */
$comment = new AddCommentCommand();
$comment->setObject($object);
$comment->setComment($this->getElement('comment')->getValue());
$comment->setAuthor($this->request->getUser()->getUsername());
if (($persistent = $this->getElement('persistent')) !== null) {
$comment->setPersistent($persistent->isChecked());
}
$expire = $this->getElement('expire');
if ($expire !== null && $expire->isChecked()) {
$comment->setExpireTime($this->getElement('expire_time')->getValue()->getTimestamp());
}
$this->getTransport($this->request)->send($comment);
}
Notification::success($this->translatePlural(
'Adding comment..',
'Adding comments..',
count($this->objects)
));
return true;
}
}

View File

@ -0,0 +1,87 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use Icinga\Module\Monitoring\Command\Object\ScheduleHostCheckCommand;
use Icinga\Module\Monitoring\Command\Object\ScheduleServiceCheckCommand;
use Icinga\Web\Notification;
/**
* Form for immediately checking hosts or services
*/
class CheckNowCommandForm extends ObjectsCommandForm
{
/**
* (non-PHPDoc)
* @see \Zend_Form::init() For the method documentation.
*/
public function init()
{
$this->setAttrib('class', 'inline');
$this->setSubmitLabel($this->translate('Check now'));
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::addSubmitButton() For the method documentation.
*/
public function addSubmitButton()
{
$this->addElements(array(
array(
'button',
'btn_submit',
array(
'class' => 'link-button spinner',
'decorators' => array(
'ViewHelper',
array('HtmlTag', array('tag' => 'div', 'class' => 'control-group form-controls'))
),
'escape' => false,
'ignore' => true,
'label' => $this->getView()->icon('arrows-cw') . $this->translate('Check now'),
'type' => 'submit',
'title' => $this->translate('Schedule the next active check to run immediately'),
'value' => $this->translate('Check now')
)
)
));
return $this;
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::onSuccess() For the method documentation.
*/
public function onSuccess()
{
foreach ($this->objects as $object) {
/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */
if (! $object->active_checks_enabled
&& ! $this->Auth()->hasPermission('monitoring/command/schedule-check')
) {
continue;
}
if ($object->getType() === $object::TYPE_HOST) {
$check = new ScheduleHostCheckCommand();
} else {
$check = new ScheduleServiceCheckCommand();
}
$check
->setObject($object)
->setForced()
->setCheckTime(time());
$this->getTransport($this->request)->send($check);
}
Notification::success(mtp(
'monitoring',
'Scheduling check..',
'Scheduling checks..',
count($this->objects)
));
return true;
}
}

View File

@ -0,0 +1,109 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use Icinga\Module\Monitoring\Command\Object\DeleteCommentCommand;
use Icinga\Module\Monitoring\Forms\Command\CommandForm;
use Icinga\Web\Notification;
/**
* Form for deleting host or service comments
*/
class DeleteCommentCommandForm extends CommandForm
{
/**
* {@inheritdoc}
*/
public function init()
{
$this->setAttrib('class', 'inline');
}
/**
* {@inheritdoc}
*/
public function addSubmitButton()
{
$this->addElement(
'button',
'btn_submit',
array(
'class' => 'link-button spinner',
'decorators' => array(
'ViewHelper',
array('HtmlTag', array('tag' => 'div', 'class' => 'control-group form-controls'))
),
'escape' => false,
'ignore' => true,
'label' => $this->getView()->icon('cancel'),
'title' => $this->translate('Delete this comment'),
'type' => 'submit'
)
);
return $this;
}
/**
* {@inheritdoc}
*/
public function createElements(array $formData = array())
{
$this->addElements(
array(
array(
'hidden',
'comment_id',
array(
'required' => true,
'validators' => array('NotEmpty'),
'decorators' => array('ViewHelper')
)
),
array(
'hidden',
'comment_is_service',
array(
'filters' => array('Boolean'),
'decorators' => array('ViewHelper')
)
),
array(
'hidden',
'comment_name',
array(
'decorators' => array('ViewHelper')
)
),
array(
'hidden',
'redirect',
array(
'decorators' => array('ViewHelper')
)
)
)
);
return $this;
}
/**
* {@inheritdoc}
*/
public function onSuccess()
{
$cmd = new DeleteCommentCommand();
$cmd
->setAuthor($this->Auth()->getUser()->getUsername())
->setCommentId($this->getElement('comment_id')->getValue())
->setCommentName($this->getElement('comment_name')->getValue())
->setIsService($this->getElement('comment_is_service')->getValue());
$this->getTransport($this->request)->send($cmd);
$redirect = $this->getElement('redirect')->getValue();
if (! empty($redirect)) {
$this->setRedirectUrl($redirect);
}
Notification::success($this->translate('Deleting comment..'));
return true;
}
}

View File

@ -0,0 +1,89 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use Icinga\Module\Monitoring\Command\Object\DeleteCommentCommand;
use Icinga\Module\Monitoring\Forms\Command\CommandForm;
use Icinga\Web\Notification;
/**
* Form for deleting host or service comments
*/
class DeleteCommentsCommandForm extends CommandForm
{
/**
* The comments to delete
*
* @var array
*/
protected $comments;
/**
* {@inheritdoc}
*/
public function init()
{
$this->setAttrib('class', 'inline');
}
/**
* Set the comments to delete
*
* @param iterable $comments
*
* @return $this
*/
public function setComments($comments)
{
$this->comments = $comments;
return $this;
}
/**
* {@inheritdoc}
*/
public function createElements(array $formData = array())
{
$this->addElements(array(
array(
'hidden',
'redirect',
array('decorators' => array('ViewHelper'))
)
));
return $this;
}
/**
* {@inheritdoc}
*/
public function getSubmitLabel()
{
return $this->translatePlural('Remove', 'Remove All', count($this->comments));
}
/**
* {@inheritdoc}
*/
public function onSuccess()
{
foreach ($this->comments as $comment) {
$cmd = new DeleteCommentCommand();
$cmd
->setCommentId($comment->id)
->setCommentName($comment->name)
->setAuthor($this->Auth()->getUser()->getUsername())
->setIsService(isset($comment->service_description));
$this->getTransport($this->request)->send($cmd);
}
$redirect = $this->getElement('redirect')->getValue();
if (! empty($redirect)) {
$this->setRedirectUrl($redirect);
}
Notification::success(
$this->translatePlural('Deleting comment..', 'Deleting comments..', count($this->comments))
);
return true;
}
}

View File

@ -0,0 +1,129 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use Icinga\Module\Monitoring\Command\Object\DeleteDowntimeCommand;
use Icinga\Module\Monitoring\Exception\CommandTransportException;
use Icinga\Module\Monitoring\Forms\Command\CommandForm;
use Icinga\Web\Notification;
/**
* Form for deleting host or service downtimes
*/
class DeleteDowntimeCommandForm extends CommandForm
{
/**
* {@inheritdoc}
*/
public function init()
{
$this->setAttrib('class', 'inline');
}
/**
* {@inheritdoc}
*/
public function addSubmitButton()
{
$this->addElement(
'button',
'btn_submit',
array(
'class' => 'link-button spinner',
'decorators' => array(
'ViewHelper',
array('HtmlTag', array('tag' => 'div', 'class' => 'control-group form-controls'))
),
'escape' => false,
'ignore' => true,
'label' => $this->getView()->icon('cancel'),
'title' => $this->translate('Delete this downtime'),
'type' => 'submit'
)
);
return $this;
}
/**
* {@inheritdoc}
*/
public function createElements(array $formData = array())
{
$this->addElements(
array(
array(
'hidden',
'downtime_id',
array(
'decorators' => array('ViewHelper'),
'required' => true,
'validators' => array('NotEmpty')
)
),
array(
'hidden',
'downtime_is_service',
array(
'decorators' => array('ViewHelper'),
'filters' => array('Boolean')
)
),
array(
'hidden',
'downtime_name',
array(
'decorators' => array('ViewHelper')
)
),
array(
'hidden',
'redirect',
array(
'decorators' => array('ViewHelper')
)
)
)
);
return $this;
}
/**
* {@inheritdoc}
*/
public function onSuccess()
{
$cmd = new DeleteDowntimeCommand();
$cmd
->setAuthor($this->Auth()->getUser()->getUsername())
->setDowntimeId($this->getElement('downtime_id')->getValue())
->setDowntimeName($this->getElement('downtime_name')->getValue())
->setIsService($this->getElement('downtime_is_service')->getValue());
$errorMsg = null;
try {
$this->getTransport($this->request)->send($cmd);
} catch (CommandTransportException $e) {
$errorMsg = $e->getMessage();
}
if (! $errorMsg) {
$redirect = $this->getElement('redirect')->getValue();
Notification::success($this->translate('Deleting downtime.'));
} else {
if (! $this->getIsApiTarget()) {
$redirect = $this->getRequest()->getUrl();
}
Notification::error($errorMsg);
}
if (! empty($redirect)) {
$this->setRedirectUrl($redirect);
return true;
}
return false;
}
}

View File

@ -0,0 +1,98 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use Icinga\Module\Monitoring\Command\Object\DeleteDowntimeCommand;
use Icinga\Module\Monitoring\Exception\CommandTransportException;
use Icinga\Module\Monitoring\Forms\Command\CommandForm;
use Icinga\Web\Notification;
/**
* Form for deleting host or service downtimes
*/
class DeleteDowntimesCommandForm extends CommandForm
{
/**
* The downtimes to delete
*
* @var array
*/
protected $downtimes;
/**
* {@inheritdoc}
*/
public function init()
{
$this->setAttrib('class', 'inline');
}
/**
* Set the downtimes to delete
*
* @param iterable $downtimes
*
* @return $this
*/
public function setDowntimes($downtimes)
{
$this->downtimes = $downtimes;
return $this;
}
/**
* {@inheritdoc}
*/
public function createElements(array $formData = array())
{
$this->addElements(array(
array(
'hidden',
'redirect',
array('decorators' => array('ViewHelper'))
)
));
return $this;
}
/**
* {@inheritdoc}
*/
public function getSubmitLabel()
{
return $this->translatePlural('Remove', 'Remove All', count($this->downtimes));
}
/**
* {@inheritdoc}
*/
public function onSuccess()
{
foreach ($this->downtimes as $downtime) {
$delDowntime = new DeleteDowntimeCommand();
$delDowntime
->setDowntimeId($downtime->id)
->setDowntimeName($downtime->name)
->setAuthor($this->Auth()->getUser()->getUsername())
->setIsService(isset($downtime->service_description));
try {
$this->getTransport($this->request)->send($delDowntime);
} catch (CommandTransportException $e) {
// Negative lookahead because there may be messages from other endpoints with different status codes
if (preg_match('/Can\'t send external Icinga command: (?!404)/', $e->getMessage())) {
throw $e;
}
}
}
$redirect = $this->getElement('redirect')->getValue();
if (! empty($redirect)) {
$this->setRedirectUrl($redirect);
}
Notification::success(
$this->translatePlural('Deleting downtime..', 'Deleting downtimes..', count($this->downtimes))
);
return true;
}
}

View File

@ -0,0 +1,47 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use Icinga\Module\Monitoring\Forms\Command\CommandForm;
use Icinga\Module\Monitoring\Object\MonitoredObject;
/**
* Base class for Icinga object command forms
*/
abstract class ObjectsCommandForm extends CommandForm
{
/**
* Involved Icinga objects
*
* @var array|\Traversable|\ArrayAccess
*/
protected $objects;
/**
* Set the involved Icinga objects
*
* @param $objects MonitoredObject|array|\Traversable|\ArrayAccess
*
* @return $this
*/
public function setObjects($objects)
{
if ($objects instanceof MonitoredObject) {
$this->objects = array($objects);
} else {
$this->objects = $objects;
}
return $this;
}
/**
* Get the involved Icinga objects
*
* @return array|\ArrayAccess|\Traversable
*/
public function getObjects()
{
return $this->objects;
}
}

View File

@ -0,0 +1,140 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use Icinga\Web\Notification;
use Icinga\Module\Monitoring\Command\Object\ProcessCheckResultCommand;
/**
* Form for submitting a passive host or service check result
*/
class ProcessCheckResultCommandForm extends ObjectsCommandForm
{
/**
* Initialize this form
*/
public function init()
{
$this->addDescription($this->translate(
'This command is used to submit passive host or service check results.'
));
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::getSubmitLabel() For the method documentation.
*/
public function getSubmitLabel()
{
return $this->translatePlural(
'Submit Passive Check Result',
'Submit Passive Check Results',
count($this->objects)
);
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::createElements() For the method documentation.
*/
public function createElements(array $formData)
{
$object = null;
foreach ($this->getObjects() as $object) {
/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */
// Nasty, but as getObjects() returns everything but an object with a real
// iterator interface this is the only way to fetch just the first element
break;
}
$this->addElement(
'select',
'status',
array(
'required' => true,
'label' => $this->translate('Status'),
'description' => $this->translate('The state this check result should report'),
'multiOptions' => $object->getType() === $object::TYPE_HOST ? $this->getHostMultiOptions() : array(
ProcessCheckResultCommand::SERVICE_OK => $this->translate('OK', 'icinga.state'),
ProcessCheckResultCommand::SERVICE_WARNING => $this->translate('WARNING', 'icinga.state'),
ProcessCheckResultCommand::SERVICE_CRITICAL => $this->translate('CRITICAL', 'icinga.state'),
ProcessCheckResultCommand::SERVICE_UNKNOWN => $this->translate('UNKNOWN', 'icinga.state')
)
)
);
$this->addElement(
'text',
'output',
array(
'required' => true,
'label' => $this->translate('Output'),
'description' => $this->translate('The plugin output of this check result')
)
);
$this->addElement(
'text',
'perfdata',
array(
'allowEmpty' => true,
'label' => $this->translate('Performance Data'),
'description' => $this->translate(
'The performance data of this check result. Leave empty'
. ' if this check result has no performance data'
)
)
);
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::onSuccess() For the method documentation.
*/
public function onSuccess()
{
foreach ($this->objects as $object) {
/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */
if (! $object->passive_checks_enabled) {
continue;
}
$command = new ProcessCheckResultCommand();
$command->setObject($object);
$command->setStatus($this->getValue('status'));
$command->setOutput($this->getValue('output'));
if ($perfdata = $this->getValue('perfdata')) {
$command->setPerformanceData($perfdata);
}
$this->getTransport($this->request)->send($command);
}
Notification::success($this->translatePlural(
'Processing check result..',
'Processing check results..',
count($this->objects)
));
return true;
}
/**
* Returns the available host options based on the program version
*
* @return array
*/
protected function getHostMultiOptions()
{
$options = array(
ProcessCheckResultCommand::HOST_UP => $this->translate('UP', 'icinga.state'),
ProcessCheckResultCommand::HOST_DOWN => $this->translate('DOWN', 'icinga.state')
);
if (! $this->getBackend()->isIcinga2()) {
$options[ProcessCheckResultCommand::HOST_UNREACHABLE] = $this->translate('UNREACHABLE', 'icinga.state');
}
return $options;
}
}

View File

@ -0,0 +1,122 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use Icinga\Module\Monitoring\Command\Object\RemoveAcknowledgementCommand;
use Icinga\Web\Notification;
/**
* Form for removing host or service problem acknowledgements
*/
class RemoveAcknowledgementCommandForm extends ObjectsCommandForm
{
/**
* Whether to show the submit label next to the remove icon
*
* The submit label is disabled in detail views but should be enabled in multi-select views.
*
* @var bool
*/
protected $labelEnabled = false;
/**
* Whether to show the submit label next to the remove icon
*
* @return bool
*/
public function isLabelEnabled()
{
return $this->labelEnabled;
}
/**
* Set whether to show the submit label next to the remove icon
*
* @param bool $labelEnabled
*
* @return $this
*/
public function setLabelEnabled($labelEnabled)
{
$this->labelEnabled = (bool) $labelEnabled;
return $this;
}
/**
* {@inheritdoc}
*/
public function init()
{
$this->setAttrib('class', 'inline');
}
/**
* {@inheritdoc}
*/
public function addSubmitButton()
{
$this->addElement(
'button',
'btn_submit',
array(
'class' => 'link-button spinner',
'decorators' => array(
'ViewHelper',
array('HtmlTag', array('tag' => 'div', 'class' => 'control-group form-controls'))
),
'escape' => false,
'ignore' => true,
'label' => $this->getSubmitLabel(),
'title' => $this->translatePlural(
'Remove acknowledgement',
'Remove acknowledgements',
count($this->objects)
),
'type' => 'submit'
)
);
return $this;
}
/**
* {@inheritdoc}
*/
public function getSubmitLabel()
{
$label = $this->getView()->icon('cancel');
if ($this->isLabelEnabled()) {
$label .= $this->translatePlural(
'Remove acknowledgement',
'Remove acknowledgements',
count($this->objects)
);
}
return $label;
}
/**
* {@inheritdoc}
*/
public function onSuccess()
{
foreach ($this->objects as $object) {
/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */
$removeAck = new RemoveAcknowledgementCommand();
$removeAck->setObject($object);
$removeAck->setAuthor($this->Auth()->getUser()->getUsername());
$this->getTransport($this->request)->send($removeAck);
}
Notification::success(mtp(
'monitoring',
'Removing acknowledgement..',
'Removing acknowledgements..',
count($this->objects)
));
return true;
}
}

View File

@ -0,0 +1,67 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use Icinga\Application\Config;
use Icinga\Module\Monitoring\Command\Object\ScheduleHostCheckCommand;
use Icinga\Web\Notification;
/**
* Form for scheduling host checks
*/
class ScheduleHostCheckCommandForm extends ScheduleServiceCheckCommandForm
{
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::createElements() For the method documentation.
*/
public function createElements(array $formData = array())
{
$config = Config::module('monitoring');
parent::createElements($formData);
$this->addElements(array(
array(
'checkbox',
'all_services',
array(
'label' => $this->translate('All Services'),
'value' => (bool) $config->get('settings', 'hostcheck_all_services', false),
'description' => $this->translate(
'Schedule check for all services on the hosts and the hosts themselves.'
)
)
)
));
return $this;
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::onSuccess() For the method documentation.
*/
public function onSuccess()
{
foreach ($this->objects as $object) {
/** @var \Icinga\Module\Monitoring\Object\Host $object */
if (! $object->active_checks_enabled
&& ! $this->Auth()->hasPermission('monitoring/command/schedule-check')
) {
continue;
}
$check = new ScheduleHostCheckCommand();
$check
->setObject($object)
->setOfAllServices($this->getElement('all_services')->isChecked());
$this->scheduleCheck($check, $this->request);
}
Notification::success($this->translatePlural(
'Scheduling host check..',
'Scheduling host checks..',
count($this->objects)
));
return true;
}
}

View File

@ -0,0 +1,178 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use DateInterval;
use DateTime;
use Icinga\Application\Config;
use Icinga\Module\Monitoring\Command\Object\ApiScheduleHostDowntimeCommand;
use Icinga\Module\Monitoring\Command\Object\PropagateHostDowntimeCommand;
use Icinga\Module\Monitoring\Command\Object\ScheduleHostDowntimeCommand;
use Icinga\Module\Monitoring\Command\Object\ScheduleServiceDowntimeCommand;
use Icinga\Module\Monitoring\Command\Transport\ApiCommandTransport;
use Icinga\Module\Monitoring\Command\Transport\CommandTransport;
use Icinga\Web\Notification;
/**
* Form for scheduling host downtimes
*/
class ScheduleHostDowntimeCommandForm extends ScheduleServiceDowntimeCommandForm
{
/** @var bool */
protected $hostDowntimeAllServices;
public function init()
{
$this->start = new DateTime();
$config = Config::module('monitoring');
$this->commentText = $config->get('settings', 'hostdowntime_comment_text');
$this->hostDowntimeAllServices = (bool) $config->get('settings', 'hostdowntime_all_services', false);
$fixedEnd = clone $this->start;
$fixed = $config->get('settings', 'hostdowntime_end_fixed', 'PT1H');
$this->fixedEnd = $fixedEnd->add(new DateInterval($fixed));
$flexibleEnd = clone $this->start;
$flexible = $config->get('settings', 'hostdowntime_end_flexible', 'PT1H');
$this->flexibleEnd = $flexibleEnd->add(new DateInterval($flexible));
$flexibleDuration = $config->get('settings', 'hostdowntime_flexible_duration', 'PT2H');
$this->flexibleDuration = new DateInterval($flexibleDuration);
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::createElements() For the method documentation.
*/
public function createElements(array $formData = array())
{
parent::createElements($formData);
$this->addElement(
'checkbox',
'all_services',
array(
'description' => $this->translate(
'Schedule downtime for all services on the hosts and the hosts themselves.'
),
'label' => $this->translate('All Services'),
'value' => $this->hostDowntimeAllServices
)
);
if (! $this->getBackend()->isIcinga2()
|| version_compare($this->getBackend()->getProgramVersion(), '2.6.0', '>=')
) {
$this->addElement(
'select',
'child_hosts',
array(
'description' => $this->translate(
'Define what should be done with the child hosts of the hosts.'
),
'label' => $this->translate('Child Hosts'),
'multiOptions' => array(
0 => $this->translate('Do nothing with child hosts'),
1 => $this->translate('Schedule triggered downtime for all child hosts'),
2 => $this->translate('Schedule non-triggered downtime for all child hosts')
),
'value' => 0
)
);
}
return $this;
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::onSuccess() For the method documentation.
*/
public function onSuccess()
{
$end = $this->getValue('end')->getTimestamp();
if ($end <= $this->getValue('start')->getTimestamp()) {
$endElement = $this->_elements['end'];
$endElement->setValue($endElement->getValue()->format($endElement->getFormat()));
$endElement->addError($this->translate('The end time must be greater than the start time'));
return false;
}
$now = new DateTime;
if ($end <= $now->getTimestamp()) {
$endElement = $this->_elements['end'];
$endElement->setValue($endElement->getValue()->format($endElement->getFormat()));
$endElement->addError($this->translate('A downtime must not be in the past'));
return false;
}
// Send all_services API parameter if Icinga is equal to or greater than 2.11.0
$allServicesNative = version_compare($this->getBackend()->getProgramVersion(), '2.11.0', '>=');
// Use ApiScheduleHostDowntimeCommand only when Icinga is equal to or greater than 2.11.0 and
// when an API command transport is requested or only API command transports are configured:
$useApiDowntime = $allServicesNative;
if ($useApiDowntime) {
$transport = $this->getTransport($this->getRequest());
if ($transport instanceof CommandTransport) {
foreach ($transport::getConfig() as $config) {
if (strtolower($config->transport) !== 'api') {
$useApiDowntime = false;
break;
}
}
} elseif (! $transport instanceof ApiCommandTransport) {
$useApiDowntime = false;
}
}
foreach ($this->objects as $object) {
if ($useApiDowntime) {
$hostDowntime = (new ApiScheduleHostDowntimeCommand())
->setForAllServices($this->getElement('all_services')->isChecked())
->setChildOptions((int) $this->getElement('child_hosts')->getValue());
// Code duplicated for readability and scope
$hostDowntime->setObject($object);
$this->scheduleDowntime($hostDowntime, $this->request);
continue;
}
/** @var \Icinga\Module\Monitoring\Object\Host $object */
if (($childHostsEl = $this->getElement('child_hosts')) !== null) {
$childHosts = (int) $childHostsEl->getValue();
} else {
$childHosts = 0;
}
$allServices = $this->getElement('all_services')->isChecked();
if ($childHosts === 0) {
$hostDowntime = (new ScheduleHostDowntimeCommand())
->setForAllServicesNative($allServicesNative);
if ($allServices === true) {
$hostDowntime->setForAllServices();
};
} else {
$hostDowntime = new PropagateHostDowntimeCommand();
if ($childHosts === 1) {
$hostDowntime->setTriggered();
}
if ($allServices === true) {
foreach ($object->services as $service) {
$serviceDowntime = new ScheduleServiceDowntimeCommand();
$serviceDowntime->setObject($service);
$this->scheduleDowntime($serviceDowntime, $this->request);
}
}
}
$hostDowntime->setObject($object);
$this->scheduleDowntime($hostDowntime, $this->request);
}
Notification::success($this->translatePlural(
'Scheduling host downtime..',
'Scheduling host downtimes..',
count($this->objects)
));
return true;
}
}

View File

@ -0,0 +1,112 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use DateTime;
use DateInterval;
use Icinga\Module\Monitoring\Command\Object\ScheduleServiceCheckCommand;
use Icinga\Web\Notification;
use Icinga\Web\Request;
/**
* Form for scheduling service checks
*/
class ScheduleServiceCheckCommandForm extends ObjectsCommandForm
{
/**
* Initialize this form
*/
public function init()
{
$this->addDescription($this->translate(
'This command is used to schedule the next check of hosts or services. Icinga will re-queue the'
. ' hosts or services to be checked at the time you specify.'
));
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::getSubmitLabel() For the method documentation.
*/
public function getSubmitLabel()
{
return $this->translatePlural('Schedule check', 'Schedule checks', count($this->objects));
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::createElements() For the method documentation.
*/
public function createElements(array $formData = array())
{
$checkTime = new DateTime();
$checkTime->add(new DateInterval('PT1H'));
$this->addElements(array(
array(
'dateTimePicker',
'check_time',
array(
'required' => true,
'label' => $this->translate('Check Time'),
'description' => $this->translate(
'Set the date and time when the check should be scheduled.'
),
'value' => $checkTime
)
),
array(
'checkbox',
'force_check',
array(
'label' => $this->translate('Force Check'),
'description' => $this->translate(
'If you select this option, Icinga will force a check regardless of both what time the'
. ' scheduled check occurs and whether or not checks are enabled.'
)
)
)
));
return $this;
}
/**
* Schedule a check
*
* @param ScheduleServiceCheckCommand $check
* @param Request $request
*/
public function scheduleCheck(ScheduleServiceCheckCommand $check, Request $request)
{
$check
->setForced($this->getElement('force_check')->isChecked())
->setCheckTime($this->getElement('check_time')->getValue()->getTimestamp());
$this->getTransport($request)->send($check);
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::onSuccess() For the method documentation.
*/
public function onSuccess()
{
foreach ($this->objects as $object) {
/** @var \Icinga\Module\Monitoring\Object\Service $object */
if (! $object->active_checks_enabled
&& ! $this->Auth()->hasPermission('monitoring/command/schedule-check')
) {
continue;
}
$check = new ScheduleServiceCheckCommand();
$check->setObject($object);
$this->scheduleCheck($check, $this->request);
}
Notification::success($this->translatePlural(
'Scheduling service check..',
'Scheduling service checks..',
count($this->objects)
));
return true;
}
}

View File

@ -0,0 +1,263 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use DateTime;
use DateInterval;
use Icinga\Application\Config;
use Icinga\Module\Monitoring\Command\Object\ScheduleServiceDowntimeCommand;
use Icinga\Web\Notification;
use Icinga\Web\Request;
/**
* Form for scheduling service downtimes
*/
class ScheduleServiceDowntimeCommandForm extends ObjectsCommandForm
{
/**
* Fixed downtime
*/
const FIXED = 'fixed';
/**
* Flexible downtime
*/
const FLEXIBLE = 'flexible';
/** @var DateTime downtime start */
protected $start;
/** @var DateTime fixed downtime end */
protected $fixedEnd;
/** @var DateTime flexible downtime end */
protected $flexibleEnd;
/** @var DateInterval flexible downtime duration */
protected $flexibleDuration;
/** @var mixed Comment text */
protected $commentText;
/**
* Initialize this form
*/
public function init()
{
$this->start = new DateTime();
$config = Config::module('monitoring');
$this->commentText = $config->get('settings', 'servicedowntime_comment_text');
$fixedEnd = clone $this->start;
$fixed = $config->get('settings', 'servicedowntime_end_fixed', 'PT1H');
$this->fixedEnd = $fixedEnd->add(new DateInterval($fixed));
$flexibleEnd = clone $this->start;
$flexible = $config->get('settings', 'servicedowntime_end_flexible', 'PT1H');
$this->flexibleEnd = $flexibleEnd->add(new DateInterval($flexible));
$flexibleDuration = $config->get('settings', 'servicedowntime_flexible_duration', 'PT2H');
$this->flexibleDuration = new DateInterval($flexibleDuration);
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::getSubmitLabel() For the method documentation.
*/
public function getSubmitLabel()
{
return $this->translatePlural('Schedule downtime', 'Schedule downtimes', count($this->objects));
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::createElements() For the method documentation.
*/
public function createElements(array $formData = array())
{
$this->addDescription($this->translate(
'This command is used to schedule host and service downtimes. During the specified downtime,'
. ' Icinga will not send notifications out about the hosts and services. When the scheduled'
. ' downtime expires, Icinga will send out notifications for the hosts and services as it'
. ' normally would. Scheduled downtimes are preserved across program shutdowns and'
. ' restarts.'
));
$isFlexible = (bool) isset($formData['type']) && $formData['type'] === self::FLEXIBLE;
$this->addElements(array(
array(
'textarea',
'comment',
array(
'required' => true,
'label' => $this->translate('Comment'),
'description' => $this->translate(
'If you work with other administrators, you may find it useful to share information about'
. ' the host or service that is having problems. Make sure you enter a brief description of'
. ' what you are doing.'
),
'attribs' => array('class' => 'autofocus'),
'value' => $this->commentText
)
),
array(
'dateTimePicker',
'start',
array(
'required' => true,
'label' => $this->translate('Start Time'),
'description' => $this->translate('Set the start date and time for the downtime.'),
'value' => $this->start
)
),
array(
'dateTimePicker',
'end',
array(
'required' => true,
'label' => $this->translate('End Time'),
'description' => $this->translate('Set the end date and time for the downtime.'),
'preserveDefault' => true,
'value' => $isFlexible ? $this->flexibleEnd : $this->fixedEnd
)
),
array(
'select',
'type',
array(
'required' => true,
'autosubmit' => true,
'label' => $this->translate('Type'),
'description' => $this->translate(
'If you select the fixed option, the downtime will be in effect between the start and end'
. ' times you specify whereas a flexible downtime starts when the host or service enters a'
. ' problem state sometime between the start and end times you specified and lasts as long'
. ' as the duration time you enter. The duration fields do not apply for fixed downtimes.'
),
'multiOptions' => array(
self::FIXED => $this->translate('Fixed'),
self::FLEXIBLE => $this->translate('Flexible')
),
'validators' => array(
array(
'InArray',
true,
array(array(self::FIXED, self::FLEXIBLE))
)
)
)
)
));
$this->addDisplayGroup(
array('start', 'end'),
'start-end',
array(
'decorators' => array(
'FormElements',
array('HtmlTag', array('tag' => 'div'))
)
)
);
if ($isFlexible) {
$this->addElements(array(
array(
'number',
'hours',
array(
'required' => true,
'label' => $this->translate('Hours'),
'value' => $this->flexibleDuration->h,
'min' => -1
)
),
array(
'number',
'minutes',
array(
'required' => true,
'label' => $this->translate('Minutes'),
'value' => $this->flexibleDuration->m,
'min' => -1
)
)
));
$this->addDisplayGroup(
array('hours', 'minutes'),
'duration',
array(
'legend' => $this->translate('Flexible Duration'),
'description' => $this->translate(
'Enter here the duration of the downtime. The downtime will be automatically deleted after this'
. ' time expired.'
),
'decorators' => array(
'FormElements',
array('HtmlTag', array('tag' => 'div')),
array(
'Description',
array('tag' => 'span', 'class' => 'description', 'placement' => 'prepend')
),
'Fieldset'
)
)
);
}
return $this;
}
public function scheduleDowntime(ScheduleServiceDowntimeCommand $downtime, Request $request)
{
$downtime
->setComment($this->getElement('comment')->getValue())
->setAuthor($request->getUser()->getUsername())
->setStart($this->getElement('start')->getValue()->getTimestamp())
->setEnd($this->getElement('end')->getValue()->getTimestamp());
if ($this->getElement('type')->getValue() === self::FLEXIBLE) {
$downtime->setFixed(false);
$downtime->setDuration(
(float) $this->getElement('hours')->getValue() * 3600
+ (float) $this->getElement('minutes')->getValue() * 60
);
}
$this->getTransport($request)->send($downtime);
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::onSuccess() For the method documentation.
*/
public function onSuccess()
{
$end = $this->getValue('end')->getTimestamp();
if ($end <= $this->getValue('start')->getTimestamp()) {
$endElement = $this->_elements['end'];
$endElement->setValue($endElement->getValue()->format($endElement->getFormat()));
$endElement->addError($this->translate('The end time must be greater than the start time'));
return false;
}
$now = new DateTime;
if ($end <= $now->getTimestamp()) {
$endElement = $this->_elements['end'];
$endElement->setValue($endElement->getValue()->format($endElement->getFormat()));
$endElement->addError($this->translate('A downtime must not be in the past'));
return false;
}
foreach ($this->objects as $object) {
/** @var \Icinga\Module\Monitoring\Object\Service $object */
$downtime = new ScheduleServiceDowntimeCommand();
$downtime->setObject($object);
$this->scheduleDowntime($downtime, $this->request);
}
Notification::success($this->translatePlural(
'Scheduling service downtime..',
'Scheduling service downtimes..',
count($this->objects)
));
return true;
}
}

View File

@ -0,0 +1,110 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use Icinga\Application\Config;
use Icinga\Module\Monitoring\Command\Object\SendCustomNotificationCommand;
use Icinga\Web\Notification;
/**
* Form to send custom notifications
*/
class SendCustomNotificationCommandForm extends ObjectsCommandForm
{
/**
* Initialize this form
*/
public function init()
{
$this->addDescription(
$this->translate('This command is used to send custom notifications about hosts or services.')
);
}
/**
* {@inheritdoc}
*/
public function getSubmitLabel()
{
return $this->translatePlural('Send custom notification', 'Send custom notifications', count($this->objects));
}
/**
* {@inheritdoc}
*/
public function createElements(array $formData = array())
{
$config = Config::module('monitoring');
$this->addElements(array(
array(
'textarea',
'comment',
array(
'required' => true,
'label' => $this->translate('Comment'),
'description' => $this->translate(
'If you work with other administrators, you may find it useful to share information about'
. ' the host or service that is having problems. Make sure you enter a brief description of'
. ' what you are doing.'
)
)
),
array(
'checkbox',
'forced',
array(
'label' => $this->translate('Forced'),
'value' => (bool) $config->get('settings', 'custom_notification_forced', false),
'description' => $this->translate(
'If you check this option, the notification is sent out regardless of time restrictions and'
. ' whether or not notifications are enabled.'
)
)
)
));
if (! $this->getBackend()->isIcinga2()) {
$this->addElement(
'checkbox',
'broadcast',
array(
'label' => $this->translate('Broadcast'),
'value' => (bool) $config->get('settings', 'custom_notification_broadcast', false),
'description' => $this->translate(
'If you check this option, the notification is sent out to all normal and escalated contacts.'
)
)
);
}
return $this;
}
/**
* {@inheritdoc}
*/
public function onSuccess()
{
foreach ($this->objects as $object) {
/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */
$notification = new SendCustomNotificationCommand();
$notification
->setObject($object)
->setComment($this->getElement('comment')->getValue())
->setAuthor($this->request->getUser()->getUsername())
->setForced($this->getElement('forced')->isChecked());
if (($broadcast = $this->getElement('broadcast')) !== null) {
$notification->setBroadcast($broadcast->isChecked());
}
$this->getTransport($this->request)->send($notification);
}
Notification::success($this->translatePlural(
'Sending custom notification..',
'Sending custom notifications..',
count($this->objects)
));
return true;
}
}

View File

@ -0,0 +1,187 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Command\Object;
use Icinga\Module\Monitoring\Command\Object\ToggleObjectFeatureCommand;
use Icinga\Module\Monitoring\Object\MonitoredObject;
use Icinga\Web\Notification;
/**
* Form for enabling or disabling features of Icinga objects, i.e. hosts or services
*/
class ToggleObjectFeaturesCommandForm extends ObjectsCommandForm
{
/**
* Feature to feature spec map
*
* @var string[]
*/
protected $features;
/**
* Feature to feature status map
*
* @var int[]
*/
protected $featureStatus;
/**
* {@inheritdoc}
*/
public function init()
{
$this->setUseFormAutosubmit();
$this->setAttrib('class', self::DEFAULT_CLASSES . ' object-features');
$features = array(
ToggleObjectFeatureCommand::FEATURE_ACTIVE_CHECKS => array(
'label' => $this->translate('Active Checks'),
'permission' => 'monitoring/command/feature/object/active-checks'
),
ToggleObjectFeatureCommand::FEATURE_PASSIVE_CHECKS => array(
'label' => $this->translate('Passive Checks'),
'permission' => 'monitoring/command/feature/object/passive-checks'
),
ToggleObjectFeatureCommand::FEATURE_OBSESSING => array(
'label' => $this->translate('Obsessing'),
'permission' => 'monitoring/command/feature/object/obsessing'
),
ToggleObjectFeatureCommand::FEATURE_NOTIFICATIONS => array(
'label' => $this->translate('Notifications'),
'permission' => 'monitoring/command/feature/object/notifications'
),
ToggleObjectFeatureCommand::FEATURE_EVENT_HANDLER => array(
'label' => $this->translate('Event Handler'),
'permission' => 'monitoring/command/feature/object/event-handler'
),
ToggleObjectFeatureCommand::FEATURE_FLAP_DETECTION => array(
'label' => $this->translate('Flap Detection'),
'permission' => 'monitoring/command/feature/object/flap-detection'
)
);
if ($this->getBackend()->isIcinga2()) {
unset($features[ToggleObjectFeatureCommand::FEATURE_OBSESSING]);
}
$this->features = $features;
}
/**
* {@inheritdoc}
*/
public function createElements(array $formData = array())
{
foreach ($this->features as $feature => $spec) {
$options = array(
'autosubmit' => true,
'disabled' => $this->hasPermission($spec['permission']) ? null : 'disabled',
'label' => $spec['label']
);
if ($formData[$feature . '_changed']) {
$options['description'] = $this->translate('changed');
}
if ($formData[$feature] === 2) {
$this->addElement('select', $feature, $options + [
'description' => $this->translate('Multiple Values'),
'filters' => [['Null', ['type' => \Zend_Filter_Null::STRING]]],
'multiOptions' => [
'' => $this->translate('Leave Unchanged'),
$this->translate('Disable All'),
$this->translate('Enable All')
],
'decorators' => array_merge(
array_slice(static::$defaultElementDecorators, 0, 3),
[['Description', ['tag' => 'span']]],
array_slice(static::$defaultElementDecorators, 4, 1),
[['HtmlTag', ['tag' => 'div', 'class' => 'control-group indeterminate']]]
)
]);
} else {
$options['value'] = $formData[$feature];
$this->addElement('checkbox', $feature, $options);
}
}
}
/**
* Load feature status
*
* @param MonitoredObject|object $object
*
* @return $this
*/
public function load($object)
{
$featureStatus = array();
foreach (array_keys($this->features) as $feature) {
$featureStatus[$feature] = $object->{$feature};
if (isset($object->{$feature . '_changed'})) {
$featureStatus[$feature . '_changed'] = (bool) $object->{$feature . '_changed'};
} else {
$featureStatus[$feature . '_changed'] = false;
}
}
$this->create($featureStatus);
$this->featureStatus = $featureStatus;
return $this;
}
/**
* (non-PHPDoc)
* @see \Icinga\Web\Form::onSuccess() For the method documentation.
*/
public function onSuccess()
{
$notifications = array(
ToggleObjectFeatureCommand::FEATURE_ACTIVE_CHECKS => array(
$this->translate('Enabling active checks..'),
$this->translate('Disabling active checks..')
),
ToggleObjectFeatureCommand::FEATURE_PASSIVE_CHECKS => array(
$this->translate('Enabling passive checks..'),
$this->translate('Disabling passive checks..')
),
ToggleObjectFeatureCommand::FEATURE_OBSESSING => array(
$this->translate('Enabling obsessing..'),
$this->translate('Disabling obsessing..')
),
ToggleObjectFeatureCommand::FEATURE_NOTIFICATIONS => array(
$this->translate('Enabling notifications..'),
$this->translate('Disabling notifications..')
),
ToggleObjectFeatureCommand::FEATURE_EVENT_HANDLER => array(
$this->translate('Enabling event handler..'),
$this->translate('Disabling event handler..')
),
ToggleObjectFeatureCommand::FEATURE_FLAP_DETECTION => array(
$this->translate('Enabling flap detection..'),
$this->translate('Disabling flap detection..')
)
);
foreach ($this->getValues() as $feature => $enabled) {
if ($this->getElement($feature)->getAttrib('disabled') !== null
|| $enabled === null
|| (int) $enabled === (int) $this->featureStatus[$feature]
) {
continue;
}
foreach ($this->objects as $object) {
/** @var \Icinga\Module\Monitoring\Object\MonitoredObject $object */
if ((bool) $object->{$feature} !== (bool) $enabled) {
$toggleFeature = new ToggleObjectFeatureCommand();
$toggleFeature
->setFeature($feature)
->setObject($object)
->setEnabled($enabled);
$this->getTransport($this->request)->send($toggleFeature);
}
}
Notification::success(
$notifications[$feature][$enabled ? 0 : 1]
);
}
return true;
}
}

View File

@ -0,0 +1,367 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Config;
use Exception;
use InvalidArgumentException;
use Icinga\Application\Config;
use Icinga\Data\ConfigObject;
use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\IcingaException;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfigForm;
use Icinga\Web\Form;
/**
* Form for managing monitoring backends
*/
class BackendConfigForm extends ConfigForm
{
/**
* The available monitoring backend resources split by type
*
* @var array
*/
protected $resources;
/**
* The backend to load when displaying the form for the first time
*
* @var string
*/
protected $backendToLoad;
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_monitoring_backends');
$this->setSubmitLabel($this->translate('Save Changes'));
}
/**
* Set the resource configuration to use
*
* @param Config $resourceConfig The resource configuration
*
* @return $this
*
* @throws ConfigurationError In case there are no valid monitoring backend resources
*/
public function setResourceConfig(Config $resourceConfig)
{
$resources = array();
foreach ($resourceConfig as $name => $resource) {
if ($resource->type === 'db') {
$resources['ido'][$name] = $name;
}
}
if (empty($resources)) {
throw new ConfigurationError($this->translate(
'Could not find any valid monitoring backend resources. Please configure a database resource first.'
));
}
$this->resources = $resources;
return $this;
}
/**
* Populate the form with the given backend's config
*
* @param string $name
*
* @return $this
*
* @throws NotFoundError In case no backend with the given name is found
*/
public function load($name)
{
if (! $this->config->hasSection($name)) {
throw new NotFoundError('No monitoring backend called "%s" found', $name);
}
$this->backendToLoad = $name;
return $this;
}
/**
* Add a new monitoring backend
*
* The backend to add is identified by the array-key `name'.
*
* @param array $data
*
* @return $this
*
* @throws InvalidArgumentException In case $data does not contain a backend name
* @throws IcingaException In case a backend with the same name already exists
*/
public function add(array $data)
{
if (! isset($data['name'])) {
throw new InvalidArgumentException('Key \'name\' missing');
}
$backendName = $data['name'];
if ($this->config->hasSection($backendName)) {
throw new IcingaException(
$this->translate('A monitoring backend with the name "%s" does already exist'),
$backendName
);
}
unset($data['name']);
$this->config->setSection($backendName, $data);
return $this;
}
/**
* Edit a monitoring backend
*
* @param string $name
* @param array $data
*
* @return $this
*
* @throws NotFoundError In case no backend with the given name is found
*/
public function edit($name, array $data)
{
if (! $this->config->hasSection($name)) {
throw new NotFoundError('No monitoring backend called "%s" found', $name);
}
$backendConfig = $this->config->getSection($name);
if (isset($data['name'])) {
if ($data['name'] !== $name) {
$this->config->removeSection($name);
$name = $data['name'];
}
unset($data['name']);
}
$backendConfig->merge($data);
$this->config->setSection($name, $backendConfig);
return $this;
}
/**
* Remove a monitoring backend
*
* @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(
'checkbox',
'disabled',
array(
'label' => $this->translate('Disable This Backend')
)
);
$this->addElement(
'text',
'name',
array(
'required' => true,
'label' => $this->translate('Backend Name'),
'description' => $this->translate(
'The name of this monitoring backend that is used to differentiate it from others'
)
)
);
$resourceType = isset($formData['type']) ? $formData['type'] : null;
$resourceTypes = array();
if ($resourceType === 'ido' || array_key_exists('ido', $this->resources)) {
$resourceTypes['ido'] = 'IDO Backend';
}
if ($resourceType === null) {
$resourceType = key($resourceTypes);
}
$this->addElement(
'select',
'type',
array(
'required' => true,
'autosubmit' => true,
'label' => $this->translate('Backend Type'),
'description' => $this->translate(
'The type of data source used for retrieving monitoring information'
),
'multiOptions' => $resourceTypes
)
);
$this->addElement(
'select',
'resource',
array(
'required' => true,
'label' => $this->translate('Resource'),
'description' => $this->translate('The resource to use'),
'multiOptions' => $this->resources[$resourceType],
'value' => current($this->resources[$resourceType]),
'autosubmit' => true
)
);
$resourceName = isset($formData['resource']) ? $formData['resource'] : $this->getValue('resource');
$this->addElement(
'note',
'resource_note',
array(
'escape' => false,
'value' => sprintf(
'<a href="%1$s" data-base-target="_next" title="%2$s" aria-label="%2$s">%3$s</a>',
$this->getView()->url('config/editresource', array('resource' => $resourceName)),
sprintf($this->translate('Show the configuration of the %s resource'), $resourceName),
$this->translate('Show resource configuration')
)
)
);
if (isset($formData['skip_validation']) && $formData['skip_validation']) {
// In case another error occured and the checkbox was displayed before
$this->addSkipValidationCheckbox();
}
}
/**
* Populate the configuration of the backend to load
*/
public function onRequest()
{
if ($this->backendToLoad) {
$data = $this->config->getSection($this->backendToLoad)->toArray();
$data['name'] = $this->backendToLoad;
$this->populate($data);
}
}
/**
* Return whether the given values are valid
*
* @param array $formData The data to validate
*
* @return bool
*/
public function isValid($formData)
{
if (! parent::isValid($formData)) {
return false;
}
if (($el = $this->getElement('skip_validation')) === null || false === $el->isChecked()) {
$resourceConfig = ResourceFactory::getResourceConfig($this->getValue('resource'));
if (! self::isValidIdoSchema($this, $resourceConfig)
|| (! $this->getElement('disabled')->isChecked()
&& ! self::isValidIdoInstance($this, $resourceConfig))
) {
if ($el === null) {
$this->addSkipValidationCheckbox();
}
return false;
}
}
return true;
}
/**
* Add a checkbox to the form by which the user can skip the schema validation
*/
protected function addSkipValidationCheckbox()
{
$this->addElement(
'checkbox',
'skip_validation',
array(
'order' => 0,
'ignore' => true,
'label' => $this->translate('Skip Validation'),
'description' => $this->translate(
'Check this to not to validate the IDO schema of the chosen resource.'
)
)
);
}
/**
* Return whether the given resource contains a valid IDO schema
*
* @param Form $form
* @param ConfigObject $resourceConfig
*
* @return bool
*/
public static function isValidIdoSchema(Form $form, ConfigObject $resourceConfig)
{
try {
$db = ResourceFactory::createResource($resourceConfig);
$db->select()->from('icinga_dbversion', array('version'))->fetchOne();
} catch (Exception $_) {
$form->error($form->translate(
'Cannot find the IDO schema. Please verify that the given database '
. 'contains the schema and that the configured user has access to it.'
));
return false;
}
return true;
}
/**
* Return whether a single icinga instance is writing to the given resource
*
* @param Form $form
* @param ConfigObject $resourceConfig
*
* @return bool True if it's a single instance, false if none
* or multiple instances are writing to it
*/
public static function isValidIdoInstance(Form $form, ConfigObject $resourceConfig)
{
$db = ResourceFactory::createResource($resourceConfig);
$rowCount = $db->select()->from('icinga_instances')->count();
if ($rowCount === 0) {
$form->warning($form->translate(
'There is currently no icinga instance writing to the IDO. Make sure '
. 'that a icinga instance is configured and able to write to the IDO.'
));
return false;
} elseif ($rowCount > 1) {
$form->warning($form->translate(
'There is currently more than one icinga instance writing to the IDO. You\'ll see all objects from all'
. ' instances without any differentation. If this is not desired, consider setting up a separate IDO'
. ' for each instance.'
));
return false;
}
return true;
}
}

View File

@ -0,0 +1,75 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Config;
use Icinga\Web\Notification;
use Icinga\Forms\ConfigForm;
/**
* Form for modifying security relevant settings
*/
class SecurityConfigForm extends ConfigForm
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_config_monitoring_security');
$this->setSubmitLabel($this->translate('Save Changes'));
}
/**
* @see Form::onSuccess()
*/
public function onSuccess()
{
$this->config->setSection('security', $this->getValues());
if ($this->save()) {
Notification::success($this->translate('New security configuration has successfully been stored'));
} else {
return false;
}
}
/**
* @see Form::onRequest()
*/
public function onRequest()
{
$this->populate($this->config->getSection('security')->toArray());
}
/**
* @see Form::createElements()
*/
public function createElements(array $formData)
{
$this->addElement(
'text',
'protected_customvars',
array(
'allowEmpty' => true,
'attribs' => array('placeholder' => $this->getDefaultProtectedCustomvars()),
'label' => $this->translate('Protected Custom Variables'),
'description' => $this->translate(
'Comma separated case insensitive list of protected custom variables.'
. ' Use * as a placeholder for zero or more wildcard characters.'
. ' Existence of those custom variables will be shown, but their values will be masked.'
)
)
);
}
/**
* Return the customvars to suggest to protect when none are protected
*
* @return string
*/
public function getDefaultProtectedCustomvars()
{
return '*pw*,*pass*,community';
}
}

View File

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

View File

@ -0,0 +1,37 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Config\Transport;
use Icinga\Web\Form;
class LocalTransportForm extends Form
{
/**
* (non-PHPDoc)
* @see Form::init() For the method documentation.
*/
public function init()
{
$this->setName('form_config_command_transport_local');
}
/**
* (non-PHPDoc)
* @see Form::createElements() For the method documentation.
*/
public function createElements(array $formData = array())
{
$this->addElement(
'text',
'path',
array(
'required' => true,
'label' => $this->translate('Command File'),
'value' => '/var/run/icinga2/cmd/icinga2.cmd',
'description' => $this->translate('Path to the local Icinga command file')
)
);
return $this;
}
}

View File

@ -0,0 +1,185 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Config\Transport;
use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError;
use Icinga\Web\Form;
class RemoteTransportForm extends Form
{
/**
* The available resources split by type
*
* @var array
*/
protected $resources;
/**
* (non-PHPDoc)
* @see Form::init() For the method documentation.
*/
public function init()
{
$this->setName('form_config_command_transport_remote');
}
/**
* Load all available ssh identity resources
*
* @return $this
*
* @throws \Icinga\Exception\ConfigurationError
*/
public function loadResources()
{
$resourceConfig = ResourceFactory::getResourceConfigs();
$resources = array();
foreach ($resourceConfig as $name => $resource) {
if ($resource->type === 'ssh') {
$resources['ssh'][$name] = $name;
}
}
if (empty($resources)) {
throw new ConfigurationError($this->translate('Could not find any valid SSH resources'));
}
$this->resources = $resources;
return $this;
}
/**
* Check whether ssh identity resources exists or not
*
* @return boolean
*/
public function hasResources()
{
$resourceConfig = ResourceFactory::getResourceConfigs();
foreach ($resourceConfig as $name => $resource) {
if ($resource->type === 'ssh') {
return true;
}
}
return false;
}
/**
* (non-PHPDoc)
* @see Form::createElements() For the method documentation.
*/
public function createElements(array $formData = array())
{
$useResource = false;
if ($this->hasResources()) {
$useResource = isset($formData['use_resource'])
? $formData['use_resource'] : $this->getValue('use_resource');
$this->addElement(
'checkbox',
'use_resource',
array(
'label' => $this->translate('Use SSH Identity'),
'description' => $this->translate('Make use of the ssh identity resource'),
'autosubmit' => true,
'ignore' => true
)
);
}
if ($useResource) {
$this->loadResources();
$decorators = static::$defaultElementDecorators;
array_pop($decorators); // Removes the HtmlTag decorator
$this->addElement(
'select',
'resource',
array(
'required' => true,
'label' => $this->translate('SSH Identity'),
'description' => $this->translate('The resource to use'),
'decorators' => $decorators,
'multiOptions' => $this->resources['ssh'],
'value' => current($this->resources['ssh']),
'autosubmit' => false
)
);
$resourceName = isset($formData['resource']) ? $formData['resource'] : $this->getValue('resource');
$this->addElement(
'note',
'resource_note',
array(
'escape' => false,
'decorators' => $decorators,
'value' => sprintf(
'<a href="%1$s" data-base-target="_next" title="%2$s" aria-label="%2$s">%3$s</a>',
$this->getView()->url('config/editresource', array('resource' => $resourceName)),
sprintf($this->translate('Show the configuration of the %s resource'), $resourceName),
$this->translate('Show resource configuration')
)
)
);
}
$this->addElements(array(
array(
'text',
'host',
array(
'required' => true,
'label' => $this->translate('Host'),
'description' => $this->translate(
'Hostname or address of the remote Icinga instance'
)
)
),
array(
'number',
'port',
array(
'required' => true,
'preserveDefault' => true,
'label' => $this->translate('Port'),
'description' => $this->translate('SSH port to connect to on the remote Icinga instance'),
'value' => 22
)
)
));
if (! $useResource) {
$this->addElement(
'text',
'user',
array(
'required' => true,
'label' => $this->translate('User'),
'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'
)
)
);
}
$this->addElement(
'text',
'path',
array(
'required' => true,
'label' => $this->translate('Command File'),
'value' => '/var/run/icinga2/cmd/icinga2.cmd',
'description' => $this->translate('Path to the Icinga command file on the remote Icinga instance')
)
);
return $this;
}
}

View File

@ -0,0 +1,392 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
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;
use Icinga\Exception\NotFoundError;
use Icinga\Forms\ConfigForm;
use Icinga\Module\Monitoring\Command\Transport\ApiCommandTransport;
use Icinga\Module\Monitoring\Command\Transport\LocalCommandFile;
use Icinga\Module\Monitoring\Command\Transport\RemoteCommandFile;
use Icinga\Module\Monitoring\Forms\Config\Transport\ApiTransportForm;
use Icinga\Module\Monitoring\Forms\Config\Transport\LocalTransportForm;
use Icinga\Module\Monitoring\Forms\Config\Transport\RemoteTransportForm;
/**
* 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;
/**
* @var bool
*/
protected $validatePartial = true;
/**
* 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 \Icinga\Web\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();
case ApiCommandTransport::TRANSPORT:
return new ApiTransportForm();
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);
$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'
)
)
);
$transportTypes = array(
ApiCommandTransport::TRANSPORT => $this->translate('Icinga 2 API'),
LocalCommandFile::TRANSPORT => $this->translate('Local Command File'),
RemoteCommandFile::TRANSPORT => $this->translate('Remote Command File')
);
if (! Platform::extensionLoaded('curl')) {
unset($transportTypes[ApiCommandTransport::TRANSPORT]);
}
$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');
}
/**
* 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) {
$btnSubmit = $this->getElement('btn_submit');
if ($btnSubmit !== null) {
// In the setup wizard $this is being used as a subform which doesn't have a submit button.
$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'))
)
)
);
$elements = array('transport_validation', 'transport-progress');
$btnSubmit->setDecorators(array('ViewHelper'));
array_unshift($elements, 'btn_submit');
$this->addDisplayGroup(
$elements,
'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
*/
public function onRequest()
{
if ($this->transportToLoad) {
$data = $this->config->getSection($this->transportToLoad)->toArray();
$data['name'] = $this->transportToLoad;
$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->getSubForm('transport_form') instanceof ApiTransportForm) {
if (! isset($formData['transport_validation'])
&& isset($formData['force_creation']) && $formData['force_creation']
) {
// ignore any validation result
return true;
}
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

@ -0,0 +1,87 @@
<?php
/* Icinga Web 2 | (c) 2017 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Config;
use Icinga\Application\Config;
use Icinga\Web\Form;
use Icinga\Web\Notification;
/**
* Form for reordering command transports
*/
class TransportReorderForm extends Form
{
/**
* Initialize this form
*/
public function init()
{
$this->setName('form_reorder_command_transports');
$this->setViewScript('form/reorder-command-transports.phtml');
}
/**
* {@inheritdoc}
*/
public function createElements(array $formData)
{
// This adds just a dummy element to be able to utilize Form::getValue as part of onSuccess()
$this->addElement(
'hidden',
'transport_newpos',
array(
'required' => true,
'validators' => array(
array(
'validator' => 'regex',
'options' => array(
'pattern' => '/\A\d+\|/'
)
)
)
)
);
}
/**
* Update the command transport order and save the configuration
*/
public function onSuccess()
{
list($position, $transportName) = explode('|', $this->getValue('transport_newpos'), 2);
$config = $this->getConfig();
if (! $config->hasSection($transportName)) {
Notification::error(sprintf($this->translate('Command transport "%s" not found'), $transportName));
return false;
}
if ($config->count() > 1) {
$sections = $config->keys();
array_splice($sections, array_search($transportName, $sections, true), 1);
array_splice($sections, $position, 0, array($transportName));
$sectionsInNewOrder = array();
foreach ($sections as $section) {
$sectionsInNewOrder[$section] = $config->getSection($section);
$config->removeSection($section);
}
foreach ($sectionsInNewOrder as $name => $options) {
$config->setSection($name, $options);
}
$config->saveIni();
Notification::success($this->translate('Command transport order updated'));
}
}
/**
* Get the command transports config
*
* @return Config
*/
public function getConfig()
{
return Config::module('monitoring', 'commandtransports');
}
}

View File

@ -0,0 +1,157 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms;
use Icinga\Web\Url;
use Icinga\Web\Form;
use Icinga\Data\Filter\Filter;
/**
* Configure the filter for the event overview
*/
class EventOverviewForm extends Form
{
/**
* {@inheritdoc}
*/
public function init()
{
$this->setName('form_event_overview');
$this->setDecorators(array(
'FormElements',
array('HtmlTag', array('tag' => 'div', 'class' => 'hbox')),
'Form'
));
}
/**
* {@inheritdoc}
*/
public function createElements(array $formData)
{
$decorators = array(
array('Label', array('class' => 'optional')),
'ViewHelper',
array('HtmlTag', array('tag' => 'div', 'class' => 'hbox-item optionbox')),
);
$url = Url::fromRequest()->getAbsoluteUrl();
$this->addElement(
'checkbox',
'statechange',
array(
'label' => $this->translate('State Changes'),
'class' => 'autosubmit',
'decorators' => $decorators,
'value' => strpos($url, $this->stateChangeFilter()->toQueryString()) === false ? 0 : 1
)
);
$this->addElement(
'checkbox',
'downtime',
array(
'label' => $this->translate('Downtimes'),
'class' => 'autosubmit',
'decorators' => $decorators,
'value' => strpos($url, $this->downtimeFilter()->toQueryString()) === false ? 0 : 1
)
);
$this->addElement(
'checkbox',
'comment',
array(
'label' => $this->translate('Comments'),
'class' => 'autosubmit',
'decorators' => $decorators,
'value' => strpos($url, $this->commentFilter()->toQueryString()) === false ? 0 : 1
)
);
$this->addElement(
'checkbox',
'notification',
array(
'label' => $this->translate('Notifications'),
'class' => 'autosubmit',
'decorators' => $decorators,
'value' => strpos($url, $this->notificationFilter()->toQueryString()) === false ? 0 : 1
)
);
$this->addElement(
'checkbox',
'flapping',
array(
'label' => $this->translate('Flapping'),
'class' => 'autosubmit',
'decorators' => $decorators,
'value' => strpos($url, $this->flappingFilter()->toQueryString()) === false ? 0 : 1
)
);
}
/**
* Return the corresponding filter-object
*
* @returns Filter
*/
public function getFilter()
{
$filters = array();
if ($this->getValue('statechange')) {
$filters[] = $this->stateChangeFilter();
}
if ($this->getValue('comment')) {
$filters[] = $this->commentFilter();
}
if ($this->getValue('notification')) {
$filters[] = $this->notificationFilter();
}
if ($this->getValue('downtime')) {
$filters[] = $this->downtimeFilter();
}
if ($this->getValue('flapping')) {
$filters[] = $this->flappingFilter();
}
return Filter::matchAny($filters);
}
public function stateChangeFilter()
{
return Filter::matchAny(
Filter::expression('type', '=', 'hard_state'),
Filter::expression('type', '=', 'soft_state')
);
}
public function commentFilter()
{
return Filter::matchAny(
Filter::expression('type', '=', 'comment'),
Filter::expression('type', '=', 'comment_deleted'),
Filter::expression('type', '=', 'dt_comment'),
Filter::expression('type', '=', 'dt_comment_deleted'),
Filter::expression('type', '=', 'ack')
);
}
public function notificationFilter()
{
return Filter::expression('type', '=', 'notify');
}
public function downtimeFilter()
{
return Filter::matchAny(
Filter::expression('type', '=', 'downtime_start'),
Filter::expression('type', '=', 'downtime_end')
);
}
public function flappingFilter()
{
return Filter::matchAny(
Filter::expression('type', '=', 'flapping'),
Filter::expression('type', '=', 'flapping_deleted')
);
}
}

View File

@ -0,0 +1,79 @@
<?php
/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Navigation;
use Icinga\Data\Filter\Filter;
use Icinga\Exception\QueryException;
use Icinga\Forms\Navigation\NavigationItemForm;
class ActionForm extends NavigationItemForm
{
/**
* {@inheritdoc}
*/
public function createElements(array $formData)
{
parent::createElements($formData);
$this->addElement(
'text',
'filter',
array(
'allowEmpty' => true,
'label' => $this->translate('Filter'),
'description' => $this->translate(
'Display this action only for objects matching this filter. Leave it blank'
. ' if you want this action being displayed regardless of the object'
)
)
);
}
/**
* {@inheritdoc}
*/
public function isValid($formData)
{
if (! parent::isValid($formData)) {
return false;
}
if (($filterString = $this->getValue('filter')) !== null) {
$filter = Filter::matchAll();
$filter->setAllowedFilterColumns(array(
'host_name',
'hostgroup_name',
'instance_name',
'service_description',
'servicegroup_name',
'contact_name',
'contactgroup_name',
function ($c) {
return preg_match('/^_(?:host|service)_/', $c);
}
));
try {
$filter->addFilter(Filter::fromQueryString($filterString));
} catch (QueryException $_) {
$this->getElement('filter')->addError(sprintf(
$this->translate('Invalid filter provided. You can only use the following columns: %s'),
implode(', ', array(
'instance_name',
'host_name',
'hostgroup_name',
'service_description',
'servicegroup_name',
'contact_name',
'contactgroup_name',
'_(host|service)_<customvar-name>'
))
));
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,8 @@
<?php
/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Navigation;
class HostActionForm extends ActionForm
{
}

View File

@ -0,0 +1,8 @@
<?php
/* Icinga Web 2 | (c) 2015 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Navigation;
class ServiceActionForm extends ActionForm
{
}

View File

@ -0,0 +1,51 @@
<?php
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
namespace Icinga\Module\Monitoring\Forms\Setup;
use Icinga\Web\Form;
use Icinga\Application\Platform;
class BackendPage extends Form
{
public function init()
{
$this->setName('setup_monitoring_backend');
$this->setTitle($this->translate('Monitoring Backend', 'setup.page.title'));
$this->addDescription($this->translate(
'Please configure below how Icinga Web 2 should retrieve monitoring information.'
));
}
public function createElements(array $formData)
{
$this->addElement(
'text',
'name',
array(
'required' => true,
'value' => 'icinga',
'label' => $this->translate('Backend Name'),
'description' => $this->translate('The identifier of this backend')
)
);
$resourceTypes = array();
if (Platform::hasMysqlSupport() || Platform::hasPostgresqlSupport()) {
$resourceTypes['ido'] = 'IDO';
}
$this->addElement(
'select',
'type',
array(
'required' => true,
'label' => $this->translate('Backend Type'),
'description' => $this->translate(
'The data source used for retrieving monitoring information'
),
'multiOptions' => $resourceTypes
)
);
}
}

Some files were not shown because too many files have changed in this diff Show More