Merge branch 'master' of ssh://git.icinga.org/icinga2-web into feature/host-overview--4179

Conflicts:
	application/layouts/scripts/layout.phtml
	modules/monitoring/application/controllers/ListController.php
	modules/monitoring/application/controllers/ShowController.php
	modules/monitoring/application/views/scripts/list/hosts.phtml
	modules/monitoring/application/views/scripts/show/components/comments.phtml
	modules/monitoring/application/views/scripts/show/components/contacts.phtml
	modules/monitoring/application/views/scripts/show/components/customvars.phtml
	modules/monitoring/application/views/scripts/show/host.phtml
	modules/monitoring/library/Monitoring/Backend/AbstractBackend.php
	modules/monitoring/library/Monitoring/Backend/Ido/Query/StatusQuery.php
This commit is contained in:
Jannis Moßhammer 2013-07-12 15:55:31 +02:00
commit b045650a19
82 changed files with 2721 additions and 1167 deletions

3
.gitignore vendored
View File

@ -20,3 +20,6 @@ test/php/bin/extcmd_test
# misc test output # misc test output
test/frontend/static/public test/frontend/static/public
# Generated API documentation
doc/api

View File

@ -1,60 +1,20 @@
<?php <?php
// @codingStandardsIgnoreStart
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Icinga 2 Web - Head for multiple monitoring frontends
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
# namespace Icinga\Web\Form;
use Icinga\Web\ActionController; use Icinga\Web\ActionController;
use Icinga\Application\Icinga; use Icinga\Application\Icinga,
use Zend_Controller_Action_Exception as ActionException; Zend_Controller_Action_Exception as ActionException;
use Icinga\Application\Benchmark;
/**
* Class StaticController
* @package Icinga\Web\Form
*/
class StaticController extends ActionController class StaticController extends ActionController
{ {
/**
* @var bool
*/
protected $handlesAuthentication = true; protected $handlesAuthentication = true;
/**
*
*/
public function init() public function init()
{ {
$this->_helper->viewRenderer->setNoRender(true); $this->_helper->viewRenderer->setNoRender(true);
$this->_helper->layout()->disableLayout(); $this->_helper->layout()->disableLayout();
} }
/**
* @return array
*/
private function getModuleList() private function getModuleList()
{ {
$modules = Icinga::app()->moduleManager()->getLoadedModules(); $modules = Icinga::app()->moduleManager()->getLoadedModules();
@ -73,25 +33,54 @@ class StaticController extends ActionController
return $result; return $result;
} }
/**
*
*/
public function modulelistAction() public function modulelistAction()
{ {
$this->_helper->viewRenderer->setNoRender(true); $this->_helper->viewRenderer->setNoRender(true);
$this->_helper->layout()->disableLayout(); $this->_helper->layout()->disableLayout();
$this->getResponse()->setHeader("Content-Type", "application/json"); $this->getResponse()->setHeader("Content-Type","application/json");
echo "define(function() { return " . json_encode($this->getModuleList(), true) . "; })"; echo "define(function() { return ".json_encode($this->getModuleList(),true)."; })";
exit; exit;
} }
/** public function imgAction()
* @throws \Zend_Controller_Action_Exception {
*/ $module = $this->_getParam('moduleName');
$file = $this->_getParam('file');
$basedir = Icinga::app()->getModule($module)->getBaseDir();
$filePath = $basedir . '/public/img/' . $file;
if (! file_exists($filePath)) {
throw new ActionException(sprintf(
'%s does not exist',
$filePath
), 404);
}
if (preg_match('/\.([a-z]+)$/i', $file, $m)) {
$extension = $m[1];
} else {
$extension = 'fixme';
}
$hash = md5_file($filePath);
if ($hash === $this->getRequest()->getHeader('If-None-Match')) {
$this->getResponse()->setHttpResponseCode(304);
return;
}
header('ETag: ' . $hash);
header('Content-Type: image/' . $extension);
header('Cache-Control: max-age=3600');
header('Last-Modified: ' . gmdate(
'D, d M Y H:i:s',
filemtime($filePath)
) . ' GMT');
readfile($filePath);
$this->_viewRenderer->setNoRender();
}
public function javascriptAction() public function javascriptAction()
{ {
$module = $this->_getParam('module_name'); $module = $this->_getParam('moduleName');
$file = $this->_getParam('file'); $file = $this->_getParam('file');
$basedir = Icinga::app()->getModule($module)->getBaseDir(); $basedir = Icinga::app()->getModule($module)->getBaseDir();
@ -126,8 +115,7 @@ class StaticController extends ActionController
} else { } else {
readfile($filePath); readfile($filePath);
} }
return; $this->_viewRenderer->setNoRender();
} }
}
// @codingStandardsIgnoreEnd }

View File

@ -6,9 +6,7 @@
<div class="layout-main-detail collapsed"> <div class="layout-main-detail collapsed">
<div id="icinga-main" container-id="icinga-main" class="icinga-container"> <div id="icinga-main" container-id="icinga-main" class="icinga-container">
<?php echo $this->layout()->moduleStart ?> <?= $this->render('inline.phtml') ?>
<?php echo $this->layout()->content ?>
<?php echo $this->layout()->moduleEnd ?>
</div> </div>
<div id="icinga-detail" class="icinga-container " container-id="icinga-detail"> <div id="icinga-detail" class="icinga-container " container-id="icinga-detail">

View File

@ -12,14 +12,19 @@
<meta name="description" content=""> <meta name="description" content="">
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="<?php echo $this->baseUrl('css/normalize.min.css') ?>"> <link rel="stylesheet" href="<?= $this->baseUrl('css/normalize.min.css') ?>">
<link rel="stylesheet" href="<?php echo $this->baseUrl('css/vendor/bootstrap.css') ?>"> <link rel="stylesheet" href="<?= $this->baseUrl('css/vendor/bootstrap.css') ?>">
<link rel="stylesheet" href="<?php echo $this->baseUrl('css/main.css') ?>"> <link rel="stylesheet" href="<?= $this->baseUrl('css/vendor/ui-lightness/jquery-ui-1.10.3.custom.min.css') ?>">
<link rel="stylesheet" href="<?php echo $this->baseUrl('css/vendor/jquery.qtip.min.css') ?>"> <link rel="stylesheet" href="<?= $this->baseUrl('css/main.css') ?>">
<link rel="stylesheet" href="<?= $this->baseUrl('css/vendor/jquery.qtip.min.css') ?>">
<script type="text/javascript"> <script type="text/javascript">
var base_url = '<?php echo $this->baseUrl() ?>'; var base_url = '<?= $this->baseUrl() ?>';
ICINGA_DEBUG = true; ICINGA_DEBUG = true;
</script> </script>
<? if (isset($_GET['iframe']) && $_GET['iframe'] === 'true'): ?>
<base target="_parent" />
<? endif ?>
<script src="<?php echo $this->baseUrl('js/vendor/modernizr-2.6.2.min.js') ?>"></script> <script src="<?php echo $this->baseUrl('js/vendor/modernizr-2.6.2.min.js') ?>"></script>
<link rel="stylesheet" href="<?php echo $this->baseUrl('css.php') ?>"> <link rel="stylesheet" href="<?php echo $this->baseUrl('css.php') ?>">
<link rel="stylesheet" href="<?php echo $this->baseUrl('css/vendor/bootstrap-responsive.min.css') ?>"> <link rel="stylesheet" href="<?php echo $this->baseUrl('css/vendor/bootstrap-responsive.min.css') ?>">
@ -31,28 +36,6 @@
</head> </head>
<body class="cranberry"> <body class="cranberry">
<?php echo $this->render('parts/topbar.phtml') ?> <?= $this->render('just-the-body.phtml') ?>
<div class="main">
<div class="tabbable tabs-left" style="height:100%;">
<?php echo $this->render('parts/navigation.phtml') ?>
</div>
<div class="layout-main-detail collapsed">
<div id="icinga-main" container-id="icinga-main" class="icinga-container">
<?php echo $this->layout()->moduleStart ?>
<?php echo $this->layout()->content ?>
<?php echo $this->layout()->moduleEnd ?>
</div>
<div id="icinga-detail" class="icinga-container " container-id="icinga-detail">
<!--<div class="container-controls">
<a class="container-expand-link" title="expand" target="_self" href="...">
<i class="icon-fullscreen"></i>
</a>
<a class="container-detach-link" title="detach" target="popup" href="...">
<i class="icon-share"></i>
</a>
</div>-->
</div><!-- End of icinga-detail -->
</div><!-- End of layout-main-detail -->
</div><!-- End of main -->
</body> </body>
</html> </html>

View File

@ -5,8 +5,7 @@
$item = $this->navigation->listAll("menu"); $item = $this->navigation->listAll("menu");
?> ?>
<?php if (true || $this->auth()->isAuthenticated()): ?> <?php if ($this->auth()->isAuthenticated()): ?>
<ul class="nav nav-tabs icinga-navigation" > <ul class="nav nav-tabs icinga-navigation" >
<?php <?php
$activeSet = false; $activeSet = false;
@ -32,10 +31,10 @@
} }
$activeSet = $activeSet || $active; $activeSet = $activeSet || $active;
?> ?>
<li class="<?php echo $active ? "active" : "" ?>"><a href="<?php echo $url ?>"><?php echo $itemName ?></a></li> <li class="<?= $active ? "active" : "" ?>"><a href="<?= $url ?>"><?= $itemName ?></a></li>
<?php <?php
$class = ""; $class = "";
} }
?> ?>
</ul> </ul>
<?php endif ?> <? endif ?>

View File

@ -1,7 +1,7 @@
<div class="navbar navbar-inverse navbar-fixed-top"> <div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner "> <div class="navbar-inner ">
<ul class="nav pull-left"> <ul class="nav pull-left">
<li style="float:left"><a href="<?php echo $this->baseUrl('/') ?>" class="brand" style="margin-left:0px;">Icinga</a></li> <li style="float:left"><a href="<?= $this->baseUrl('/') ?>" class="brand" style="margin-left:0px;">Icinga</a></li>
</ul> </ul>
<?php if ($this->auth()->isAuthenticated()): ?> <?php if ($this->auth()->isAuthenticated()): ?>
<ul class="nav pull-right" > <ul class="nav pull-right" >
@ -11,13 +11,14 @@
</form> </form>
</li> </li>
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><?php echo <a href="#" class="dropdown-toggle" data-toggle="dropdown"><?=
$this->escape($this->auth()->getUser()->getUsername()) $this->escape($this->auth()->getUser()->getUsername())
?> <i class="icon-user icon-white" style="margin-top:0.2em"></i> ?> <i class="icon-user icon-white" style="margin-top:0.2em"></i>
<b class="caret"></b> <b class="caret"></b>
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><?php echo $this->qlink(_('Logout'), '/authentication/logout') ?></li> <li><?= $this->qlink($this->translate('Settings'), 'settings') ?></li>
<li><?= $this->qlink($this->translate('Logout'), 'authentication/logout') ?></li>
</ul> </ul>
</li> </li>
</ul> </ul>

View File

@ -1,3 +1,2 @@
<div class="clearfix" style="margin-top: 100px;"> <h1>Welcome to Icinga!</h1>
<h1>Bootstrap loaded!</h1> You should install/configure some <?= $this->qlink('modules', 'modules/overview') ?> now!
</div>

View File

@ -1,61 +1,58 @@
<? if ($this->pageCount > 1): ?>
<?php <?php
if (is_array($this->preserve)) {
$preserve = $this->preserve;
} else {
$preserve = array();
}
$fromto = t('%d to %d of %d'); if ($this->pageCount <= 1) return;
$fromto = $this->translate('%d to %d of %d');
$total = $this->totalItemCount; $total = $this->totalItemCount;
?> $limit = $this->itemCountPerPage;
<div class="pagination pagination-mini" style="margin:0px"> $title_prev = sprintf(
$fromto,
($this->current - 2) * $limit + 1,
($this->current - 1) * $limit,
$total
);
$title_next = sprintf(
$fromto,
($this->current) * $limit + 1,
($this->current + 1) * $limit,
$total
);
?><div class="pagination pagination-mini" style="margin:0px">
<ul> <ul>
<!-- Previous page link --> <!-- Previous page link -->
<? if (isset($this->previous)): ?> <?php if (isset($this->previous)): ?>
<li><a href="<?= $this->url($preserve + array('page' => $this->previous)); ?>" title="<?= <li><a href="<?= $this->url()->setParam('page', $this->previous) ?>" title="<?= $titleprev ?>">« <?= $this->translate('Back') ?></a></li>
sprintf($fromto, <?php else: ?>
($this->current - 2) * $this->itemCountPerPage + 1, <li class="disabled"><span>« <?= $this->translate('Back') ?></span></li>
($this->current - 1) * $this->itemCountPerPage, <?php endif ?>
$this->totalItemCount)
?>">« <?= t('Back') ?></a></li>
<? else: ?>
<li class="disabled"><span>« <?= t('Back') ?></span></li>
<?php
endif;
?>
<!-- Numbered page links --> <!-- Numbered page links -->
<? foreach ($this->pagesInRange as $page): ?>
<?php <?php
$start = ($page - 1) * $this->itemCountPerPage + 1; foreach ($this->pagesInRange as $page):
$end = $page * $this->itemCountPerPage;
if ($end > $total) {
$end = $total;
}
$title = sprintf($fromto, $start, $end, $total);
$active_class = $page === $this->current ? ' class="active"' : '';
?> $start = ($page - 1) * $limit + 1;
<?php if ($page === '...'): ?> $end = $page * $limit;
if ($end > $total) {
$end = $total;
}
$title = sprintf($fromto, $start, $end, $total);
$class = $page === $this->current ? ' class="active"' : '';
if ($page === '...'): ?>
<li class="disabled"><span>...</span></li> <li class="disabled"><span>...</span></li>
<?php else: ?> <?php else: ?>
<li<?= $active_class ?>><a href="<?= $this->url( <li<?= $class ?>><a href="<?= $this->url()->setParam('page', $page) ?>" title="<?= $title ?>"><?= $page ?></a></li>
$preserve + array('page' => $page) <?php
); ?>" title="<?= $title ?>"><?= $page; ?></a></li> endif;
<? endif ?> endforeach;
<? endforeach ?> ?>
<!-- Next page link --> <!-- Next page link -->
<? if (isset($this->next)): ?> <? if (isset($this->next)): ?>
<li><a href="<?= $this->url($preserve + array('page' => $this->next)); ?>" title="<?= <li><a href="<?= $this->url()->setParam('page', $this->next) ?>" title="<?= $title_next ?>"><?= t('Next') ?> »</a></li>
sprintf($fromto,
($this->current) * $this->itemCountPerPage + 1,
($this->current + 1) * $this->itemCountPerPage,
$total)
?>"><?= t('Next') ?> »</a></li>
<? else: ?> <? else: ?>
<li class="disabled"><span><?= t('Next') ?> »</span></li> <li class="disabled"><span><?= t('Next') ?> »</span></li>
<? endif ?> <? endif ?>
</ul> </ul>
</div> </div>
<? endif ?>

64
bin/createapidoc.sh Executable file
View File

@ -0,0 +1,64 @@
#!/bin/sh
# {{{ICINGA_LICENSE_HEADER}}}
# Icinga 2 Web - Head for multiple monitoring frontends
# Copyright (C) %(YEAR)s Icinga Development Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# @copyright 2013 Icinga Development Team <info@icinga.org>
# @author Icinga Development Team <info@icinga.org>
# {{{ICINGA_LICENSE_HEADER}}}
set -o nounset
DIR=$(readlink -f $(dirname $0)/../)
BIN=$(basename $0)
PHPDOC=$(which phpdoc)
CONFIG=$DIR/doc/phpdoc.xml
OUTPUT=$DIR/doc/api
ARG=${1-}
BUILD=""
cd $DIR
if [ ! -x $PHPDOC ]; then
echo "phpDocumentor not found (phpdoc)"
echo "Please read http://phpdoc.org/docs/latest/for-users/installation.html how to install"
exit 1
fi
if [ -d $OUTPUT ]; then
echo "Output directory exists"
echo "rm -rf $OUTPUT"
rm -rf $OUTPUT
fi
if [ "$ARG" == "--build" ]; then
BUILD="-q"
fi
if [ "$ARG" == "--help" ]; then
echo "Usage $BIN [ --build ]"
echo ""
echo "Options:"
echo " --build Silent output"
echo " --help Print this screen"
echo ""
exit 1
fi
$PHPDOC $BUILD -c $CONFIG
exit $?

View File

@ -1,5 +1,27 @@
#!/bin/sh #!/bin/sh
# {{{ICINGA_LICENSE_HEADER}}}
# Icinga 2 Web - Head for multiple monitoring frontends
# Copyright (C) %(YEAR)s Icinga Development Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# @copyright 2013 Icinga Development Team <info@icinga.org>
# @author Icinga Development Team <info@icinga.org>
# {{{ICINGA_LICENSE_HEADER}}}
set -o nounset set -o nounset
SCRIPTNAME=$(readlink -f $0) SCRIPTNAME=$(readlink -f $0)

27
doc/apidoc_creation.md Normal file
View File

@ -0,0 +1,27 @@
# Create API documentation
## Prerequisites
You need phpDocumentor 2 installed on your system to create the api
documentation. Please visit [phpdoc's website](http://phpdoc.org/) for more
information. Additionally, the graphviz package is required to be installed.
## Configuration
phpDocumentator is configured with xml configuration reside in doc/phpdoc.xml.
In there you'll find the target path where the documentation is created as
html. Default location is doc/api/. Just point to index.html in this directory
with a browser.
If you generated the documentation already, you can follow [this link](apidoc/idnex.html).
## Generation
Change to Icinga 2 Web root directory (source tree) and run:
bin/createapidoc.sh
## Options for createapidoc.sh
--build Optional, silent build mode
--help Displays help message

22
doc/phpdoc.xml Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<phpdoc>
<title>Icinga 2 Web</title>
<parser>
<target>./api</target>
<extension>php</extension>
</parser>
<visibility>public,private,protected</visibility>
<transformer>
<target>./api</target>
</transformer>
<logging>
<level>quiet</level>
</logging>
<transformations>
<template name="responsive" />
</transformations>
<files>
<directory>../library/Icinga</directory>
<directory>../library/application</directory>
</files>
</phpdoc>

View File

@ -2,6 +2,7 @@
namespace Icinga\Application; namespace Icinga\Application;
use Icinga\Application\Modules\Module;
use Zend_Config_Ini; use Zend_Config_Ini;
use Zend_Config; use Zend_Config;
@ -32,17 +33,19 @@ class Config extends Zend_Config_Ini
return parent::__construct($filename, $section, $options); return parent::__construct($filename, $section, $options);
} }
public function getModuleConfig($key, $module) public static function module($name, $file = null)
{ {
$manager = Icinga::app()->moduleManager(); if ($file === null) {
$res = null; $file = $name . '.ini'; // TODO: default should be module/config.ini
if ($manager->hasInstalled($module)) { }
$filename = $manager->getModuleConfigDir($module) . "/$key.ini"; $filename = Module::get($name)->getConfigDir() . '/' . $file;
if (file_exists($filename)) { if (file_exists($filename)) {
return $this->$key = new Config($filename); $config = new Config($filename);
// Compat: $config->$module->$whatever
self::getInstance()->$name = $config;
return $config;
} }
} return null;
return $res;
} }
public function __get($key) public function __get($key)

View File

@ -3,6 +3,7 @@
namespace Icinga\Application\Modules; namespace Icinga\Application\Modules;
use Icinga\Application\ApplicationBootstrap; use Icinga\Application\ApplicationBootstrap;
use Icinga\Application\Icinga;
use Icinga\Web\Hook; use Icinga\Web\Hook;
use Zend_Controller_Router_Route as Route; use Zend_Controller_Router_Route as Route;
@ -38,6 +39,23 @@ class Module
return true; return true;
} }
public static function exists($name)
{
return Icinga::app()->moduleManager()->hasEnabled($name);
}
public static function get($name, $autoload = false)
{
$manager = Icinga::app()->moduleManager();
if (! $manager->hasLoaded($name)) {
if ($autoload === true && $manager->hasEnabled($name)) {
$manager->loadModule($name);
}
}
// @throws ProgrammingError:
return $manager->getModule($name);
}
public function hasCss() public function hasCss()
{ {
return file_exists($this->getCssFilename()); return file_exists($this->getCssFilename());
@ -58,6 +76,13 @@ class Module
return $this->configdir; return $this->configdir;
} }
public function getConfig($file = null)
{
return $this->app
->getConfig()
->module($this->name, $file);
}
protected function registerLibrary() protected function registerLibrary()
{ {
if (file_exists($this->libdir) && is_dir($this->libdir)) { if (file_exists($this->libdir) && is_dir($this->libdir)) {
@ -88,17 +113,14 @@ class Module
} }
$this->registerLocales() $this->registerLocales()
->registerRoutes() ->registerRoutes();
->registerMenuEntries(); // ->registerMenuEntries();
return $this; return $this;
} }
protected function registerMenuEntries() protected function registerMenuEntries()
{ {
$cfg = $this->app $cfg = $this->getConfig('menu.ini');
->getConfig()
->getModuleConfig('menu', $this->name);
$view = $this->app->getView(); $view = $this->app->getView();
if ($cfg) { if ($cfg) {
$view->view->navigation = $cfg->merge($view->view->navigation); $view->view->navigation = $cfg->merge($view->view->navigation);
@ -119,6 +141,17 @@ class Module
) )
) )
); );
$this->app->frontController()->getRouter()->addRoute(
$this->name . '_img',
new Route(
'img/' . $this->name . '/:file',
array(
'controller' => 'static',
'action' => 'img',
'moduleName' => $this->name
)
)
);
return $this; return $this;
} }

View File

@ -32,6 +32,7 @@ use Zend_Paginator as Paginator;
use Zend_View_Helper_PaginationControl as PaginationControl; use Zend_View_Helper_PaginationControl as PaginationControl;
use Zend_Controller_Action_HelperBroker as ActionHelper; use Zend_Controller_Action_HelperBroker as ActionHelper;
use Zend_Controller_Router_Route as Route; use Zend_Controller_Router_Route as Route;
use Icinga\Web\View as IcingaView;
/** /**
* Use this if you want to make use of Icinga funtionality in other web projects * Use this if you want to make use of Icinga funtionality in other web projects
@ -169,7 +170,7 @@ class Web extends ApplicationBootstrap
protected function prepareView() protected function prepareView()
{ {
$view = ActionHelper::getStaticHelper('viewRenderer'); $view = ActionHelper::getStaticHelper('viewRenderer');
$view->initView(); $view->setView(new IcingaView());
$view->view->addHelperPath($this->appdir . '/views/helpers'); $view->view->addHelperPath($this->appdir . '/views/helpers');
// TODO: find out how to avoid this additional helper path: // TODO: find out how to avoid this additional helper path:

View File

@ -72,6 +72,11 @@ abstract class AbstractQuery
$this->init(); $this->init();
} }
public function getDatasource()
{
return $this->ds;
}
protected function getDefaultColumns() protected function getDefaultColumns()
{ {
return null; return null;
@ -89,6 +94,9 @@ abstract class AbstractQuery
$this->table = $table; $this->table = $table;
if ($columns !== null) { if ($columns !== null) {
$this->columns($columns); $this->columns($columns);
} else {
// TODO: Really?
$this->columns = $this->getDefaultColumns();
} }
return $this; return $this;
} }
@ -100,7 +108,6 @@ abstract class AbstractQuery
} else { } else {
$this->columns = array($columns); $this->columns = array($columns);
} }
return $this; return $this;
} }
@ -355,6 +362,7 @@ abstract class AbstractQuery
$limit = $request->getParam('limit', 20); $limit = $request->getParam('limit', 20);
} }
} }
$this->limit($limit, $page * $limit);
$paginator = new \Zend_Paginator( $paginator = new \Zend_Paginator(
new \Icinga\Web\Paginator\Adapter\QueryAdapter($this) new \Icinga\Web\Paginator\Adapter\QueryAdapter($this)

View File

@ -36,6 +36,22 @@ class ArrayDatasource implements DatasourceInterface
return $result; return $result;
} }
public function fetchPairs(ArrayQuery $query)
{
$result = array();
$keys = null;
foreach ($this->getResult($query) as $row) {
if ($keys === null) {
$keys = array_keys((array) $row);
if (count($keys) < 2) {
$keys[1] = $keys[0];
}
}
$result[$row->{$keys[0]}] = $row->{$keys[1]};
}
return $result;
}
public function fetchAll(ArrayQuery $query) public function fetchAll(ArrayQuery $query)
{ {
$result = $this->getResult($query); $result = $this->getResult($query);
@ -70,11 +86,14 @@ class ArrayDatasource implements DatasourceInterface
$result[] = $row; $result[] = $row;
} else { } else {
$c_row = (object) array(); $c_row = (object) array();
foreach ($columns as $key) { foreach ($columns as $alias => $key) {
if (is_int($alias)) {
$alias = $key;
}
if (isset($row->$key)) { if (isset($row->$key)) {
$c_row->$key = $row->$key; $c_row->$alias = $row->$key;
} else { } else {
$c_row->$key = null; $c_row->$alias = null;
} }
} }
$result[] = $c_row; $result[] = $c_row;

View File

@ -0,0 +1,46 @@
<?php
namespace Icinga\File;
use Icinga\Data\AbstractQuery;
class Csv
{
protected $query;
protected function __construct()
{
}
public static function fromQuery(AbstractQuery $query)
{
$csv = new Csv();
$csv->query = $query;
return $csv;
}
public function dump()
{
header('Content-type: text/csv');
echo (string) $this;
}
public function __toString()
{
$first = true;
$csv = '';
foreach ($this->query->fetchAll() as $row) {
if ($first) {
$csv .= implode(',', array_keys((array) $row)) . "\r\n";
$first = false;
}
$out = array();
foreach ($row as & $val) {
$out[] = '"' . $val . '"';
}
$csv .= implode(',', $out) . "\r\n";
}
return $csv;
}
}

View File

View File

@ -1,14 +1,18 @@
<?php <?php
namespace Icinga\Pdf; namespace Icinga\File;
use TCPDF; use TCPDF;
use Icinga\Web\Url;
use Icinga\Application\Icinga;
// $_SERVER['DOCUMENT_ROOT'] = '/';
$_SERVER['DOCUMENT_ROOT'] = Icinga::app()->getApplicationDir() . '/../public';
define('K_TCPDF_EXTERNAL_CONFIG', true); define('K_TCPDF_EXTERNAL_CONFIG', true);
//define('K_PATH_URL', 'http://net-test-icinga-vm1.adm.netways.de/develop/'); // ??? //define('K_PATH_URL', 'http://net-test-icinga-vm1.adm.netways.de/develop/'); // ???
// define('K_PATH_URL', '/var/www/net-test-icinga-vm1.adm.netways.de/develop/public'); // ??? // define('K_PATH_URL', '/var/www/net-test-icinga-vm1.adm.netways.de/develop/public'); // ???
define('K_PATH_URL', '/develop'); // ??? define('K_PATH_URL', (string) Url::create('/') === '/' ? '' : (string) Url::create('/')); // ???'/'));
define('K_PATH_MAIN', dirname(ICINGA_LIBDIR) . '/public'); define('K_PATH_MAIN', dirname(ICINGA_LIBDIR) . '/public');
define('K_PATH_FONTS', ICINGA_LIBDIR . '/vendor/tcpdf/fonts/'); define('K_PATH_FONTS', ICINGA_LIBDIR . '/vendor/tcpdf/fonts/');
define('K_PATH_CACHE', ICINGA_LIBDIR . '/vendor/tcpdf/cache/'); define('K_PATH_CACHE', ICINGA_LIBDIR . '/vendor/tcpdf/cache/');
@ -19,13 +23,13 @@ define('K_BLANK_IMAGE', K_PATH_IMAGES.'_blank.png'); // COULD be anything?
// define('K_CELL_HEIGHT_RATIO', 1.25); // define('K_CELL_HEIGHT_RATIO', 1.25);
define('K_SMALL_RATIO', 2/3); define('K_SMALL_RATIO', 2/3);
define('K_TCPDF_CALLS_IN_HTML', true); // SECURITY: is false better? define('K_TCPDF_CALLS_IN_HTML', false); // SECURITY: is false better?
define('K_TCPDF_THROW_EXCEPTION_ERROR', true); define('K_TCPDF_THROW_EXCEPTION_ERROR', true);
define('K_THAI_TOPCHARS', false);
require_once 'vendor/tcpdf/tcpdf.php'; require_once 'vendor/tcpdf/tcpdf.php';
class File extends TCPDF class Pdf extends TCPDF
{ {
protected $cell_height_ratio = 1.25; protected $cell_height_ratio = 1.25;
public function __construct( public function __construct(

View File

@ -1,31 +1,10 @@
<?php <?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Icinga 2 Web - Head for multiple monitoring frontends
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
/**
* LDAP connection handler
*/
namespace Icinga\Protocol\Ldap; namespace Icinga\Protocol\Ldap;
use Icinga\Exception\ConfigurationError as ConfigError;
use Icinga\Application\Platform; use Icinga\Application\Platform;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Application\Logger as Log; use Icinga\Application\Logger as Log;
@ -46,89 +25,65 @@ use Icinga\Application\Logger as Log;
* *
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org> * @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
* @author Icinga-Web Team <info@icinga.org> * @author Icinga-Web Team <info@icinga.org>
* @package Icinga\Protocol\Ldap
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
*/ */
class Connection class Connection
{ {
/** const LDAP_NO_SUCH_OBJECT = 0x20;
* @var string
*/
protected $ds; protected $ds;
/**
* @var string
*/
protected $hostname; protected $hostname;
protected $port = 389;
/**
* @var string
*/
protected $bind_dn; protected $bind_dn;
/**
* @var string
*/
protected $bind_pw; protected $bind_pw;
/**
* @var string
*/
protected $root_dn; protected $root_dn;
/**
* @var string
*/
protected $count; protected $count;
/**
* @var array
*/
protected $ldap_extension = array( protected $ldap_extension = array(
'1.3.6.1.4.1.1466.20037' => 'STARTTLS', // notes? '1.3.6.1.4.1.1466.20037' => 'STARTTLS',
// '1.3.6.1.4.1.4203.1.11.1' => '11.1', // PASSWORD_MODIFY // '1.3.6.1.4.1.4203.1.11.1' => '11.1', // PASSWORD_MODIFY
// '1.3.6.1.4.1.4203.1.11.3' => '11.3', // Whoami // '1.3.6.1.4.1.4203.1.11.3' => '11.3', // Whoami
// '1.3.6.1.1.8' => '8', // Cancel Extended Request // '1.3.6.1.1.8' => '8', // Cancel Extended Request
); );
protected $use_tls = false;
protected $force_tls = false;
/**
* @var array
*/
protected $ms_capability = array( protected $ms_capability = array(
// Prefix LDAP_CAP_ // Prefix LDAP_CAP_
// Source: http://msdn.microsoft.com/en-us/library/cc223359.aspx // Source: http://msdn.microsoft.com/en-us/library/cc223359.aspx
// Running Active Directory as AD DS: // Running Active Directory as AD DS:
'1.2.840.113556.1.4.800' => 'ACTIVE_DIRECTORY_OID', '1.2.840.113556.1.4.800' => 'ACTIVE_DIRECTORY_OID',
// Capable of signing and sealing on an NTLM authenticated connection // Capable of signing and sealing on an NTLM authenticated connection
// and of performing subsequent binds on a signed or sealed connection. // and of performing subsequent binds on a signed or sealed connection.
'1.2.840.113556.1.4.1791' => 'ACTIVE_DIRECTORY_LDAP_INTEG_OID', '1.2.840.113556.1.4.1791' => 'ACTIVE_DIRECTORY_LDAP_INTEG_OID',
// If AD DS: running at least W2K3, if AD LDS running at least W2K8 // If AD DS: running at least W2K3, if AD LDS running at least W2K8
'1.2.840.113556.1.4.1670' => 'ACTIVE_DIRECTORY_V51_OID', '1.2.840.113556.1.4.1670' => 'ACTIVE_DIRECTORY_V51_OID',
// If AD LDS: accepts DIGEST-MD5 binds for AD LDSsecurity principals // If AD LDS: accepts DIGEST-MD5 binds for AD LDSsecurity principals
'1.2.840.113556.1.4.1880' => 'ACTIVE_DIRECTORY_ADAM_DIGEST', '1.2.840.113556.1.4.1880' => 'ACTIVE_DIRECTORY_ADAM_DIGEST',
// Running Active Directory as AD LDS // Running Active Directory as AD LDS
'1.2.840.113556.1.4.1851' => 'ACTIVE_DIRECTORY_ADAM_OID', '1.2.840.113556.1.4.1851' => 'ACTIVE_DIRECTORY_ADAM_OID',
// If AD DS: it's a Read Only DC (RODC) // If AD DS: it's a Read Only DC (RODC)
'1.2.840.113556.1.4.1920' => 'ACTIVE_DIRECTORY_PARTIAL_SECRETS_OID', '1.2.840.113556.1.4.1920' => 'ACTIVE_DIRECTORY_PARTIAL_SECRETS_OID',
// Running at least W2K8 // Running at least W2K8
'1.2.840.113556.1.4.1935' => 'ACTIVE_DIRECTORY_V60_OID', '1.2.840.113556.1.4.1935' => 'ACTIVE_DIRECTORY_V60_OID',
// Running at least W2K8r2 // Running at least W2K8r2
'1.2.840.113556.1.4.2080' => 'ACTIVE_DIRECTORY_V61_R2_OID', '1.2.840.113556.1.4.2080' => 'ACTIVE_DIRECTORY_V61_R2_OID',
// Running at least W2K12 // Running at least W2K12
'1.2.840.113556.1.4.2237' => 'ACTIVE_DIRECTORY_W8_OID', '1.2.840.113556.1.4.2237' => 'ACTIVE_DIRECTORY_W8_OID',
); );
/**
* @var string
*/
protected $root; protected $root;
protected $supports_v3 = false;
protected $supports_tls = false;
/** /**
* Constructor * Constructor
* *
@ -142,21 +97,14 @@ class Connection
$this->bind_dn = $config->bind_dn; $this->bind_dn = $config->bind_dn;
$this->bind_pw = $config->bind_pw; $this->bind_pw = $config->bind_pw;
$this->root_dn = $config->root_dn; $this->root_dn = $config->root_dn;
$this->use_tls = isset($config->tls) ? $config->tls : false;
$this->force_tls = $this->use_tls;
} }
/**
* @return string
*/
public function getDN() public function getDN()
{ {
return $this->root_dn; return $this->root_dn;
} }
/**
* @return Root|string
*/
public function root() public function root()
{ {
if ($this->root === null) { if ($this->root === null) {
@ -165,36 +113,22 @@ class Connection
return $this->root; return $this->root;
} }
/**
* @return Query
*/
public function select() public function select()
{ {
return new Query($this); return new Query($this);
} }
/**
* @param $query
* @param array $fields
* @return mixed
*/
public function fetchOne($query, $fields = array()) public function fetchOne($query, $fields = array())
{ {
$row = (array)$this->fetchRow($query, $fields); $row = (array) $this->fetchRow($query, $fields);
return array_shift($row); return array_shift($row);
} }
/**
* @param $query
* @param array $fields
* @return mixed
* @throws Exception
*/
public function fetchDN($query, $fields = array()) public function fetchDN($query, $fields = array())
{ {
$rows = $this->fetchAll($query, $fields); $rows = $this->fetchAll($query, $fields);
if (count($rows) !== 1) { if (count($rows) !== 1) {
throw new Exception( throw new \Exception(
sprintf( sprintf(
'Cannot fetch single DN for %s', 'Cannot fetch single DN for %s',
$query $query
@ -204,11 +138,7 @@ class Connection
return key($rows); return key($rows);
} }
/**
* @param $query
* @param array $fields
* @return mixed
*/
public function fetchRow($query, $fields = array()) public function fetchRow($query, $fields = array())
{ {
// TODO: This is ugly, make it better! // TODO: This is ugly, make it better!
@ -216,21 +146,15 @@ class Connection
return array_shift($results); return array_shift($results);
} }
/**
* @param Query $query
* @return int
*/
public function count(Query $query) public function count(Query $query)
{ {
$results = $this->runQuery($query, '+'); $results = $this->runQuery($query, '+');
if (! $results) {
return 0;
}
return ldap_count_entries($this->ds, $results); return ldap_count_entries($this->ds, $results);
} }
/**
* @param $query
* @param array $fields
* @return array
*/
public function fetchAll($query, $fields = array()) public function fetchAll($query, $fields = array())
{ {
$offset = null; $offset = null;
@ -241,6 +165,9 @@ class Connection
} }
$entries = array(); $entries = array();
$results = $this->runQuery($query, $fields); $results = $this->runQuery($query, $fields);
if (! $results) {
return array();
}
$entry = ldap_first_entry($this->ds, $results); $entry = ldap_first_entry($this->ds, $results);
$count = 0; $count = 0;
while ($entry) { while ($entry) {
@ -248,7 +175,8 @@ class Connection
&& ($limit === null || ($offset + $limit) >= $count) && ($limit === null || ($offset + $limit) >= $count)
) { ) {
$attrs = ldap_get_attributes($this->ds, $entry); $attrs = ldap_get_attributes($this->ds, $entry);
$entries[ldap_get_dn($this->ds, $entry)] = $this->cleanupAttributes($attrs); $entries[ldap_get_dn($this->ds, $entry)]
= $this->cleanupAttributes($attrs);
} }
$count++; $count++;
$entry = ldap_next_entry($this->ds, $entry); $entry = ldap_next_entry($this->ds, $entry);
@ -257,13 +185,9 @@ class Connection
return $entries; return $entries;
} }
/**
* @param $attrs
* @return object
*/
public function cleanupAttributes(& $attrs) public function cleanupAttributes(& $attrs)
{ {
$clean = (object)array(); $clean = (object) array();
for ($i = 0; $i < $attrs['count']; $i++) { for ($i = 0; $i < $attrs['count']; $i++) {
$attr_name = $attrs[$i]; $attr_name = $attrs[$i];
if ($attrs[$attr_name]['count'] === 1) { if ($attrs[$attr_name]['count'] === 1) {
@ -277,12 +201,6 @@ class Connection
return $clean; return $clean;
} }
/**
* @param $query
* @param $fields
* @return resource
* @throws Exception
*/
protected function runQuery($query, $fields) protected function runQuery($query, $fields)
{ {
$this->connect(); $this->connect();
@ -293,17 +211,19 @@ class Connection
// We do not support pagination right now, and there is no chance to // We do not support pagination right now, and there is no chance to
// do so for PHP < 5.4. Warnings about "Sizelimit exceeded" will // do so for PHP < 5.4. Warnings about "Sizelimit exceeded" will
// therefore not be hidden right now. // therefore not be hidden right now.
Log::debug("Query: %s", $query->__toString()); $results = @ldap_search(
$results = ldap_search(
$this->ds, $this->ds,
$this->root_dn, $this->root_dn,
(string)$query, (string) $query,
$fields, $fields,
0, // Attributes and values 0, // Attributes and values
0 // No limit - at least where possible 0 // No limit - at least where possible
); );
if (!$results) { if ($results === false) {
throw new Exception( if (ldap_errno($this->ds) === self::LDAP_NO_SUCH_OBJECT) {
return false;
}
throw new \Exception(
sprintf( sprintf(
'LDAP query "%s" (root %s) failed: %s', 'LDAP query "%s" (root %s) failed: %s',
$query, $query,
@ -311,7 +231,6 @@ class Connection
ldap_error($this->ds) ldap_error($this->ds)
) )
); );
die('Query failed'); die('Query failed');
} }
$list = array(); $list = array();
@ -323,186 +242,229 @@ class Connection
return $results; return $results;
} }
/**
* @param $username
* @param $password
* @return bool
*/
public function testCredentials($username, $password) public function testCredentials($username, $password)
{ {
Log::debug("Trying to connect to %s", $this->hostname); $ds = $this->prepareNewConnection();
$ds = ldap_connect($this->hostname);
Log::debug("ldap_bind (%s)", $username);
$r = @ldap_bind($ds, $username, $password); $r = @ldap_bind($ds, $username, $password);
if ($r) { if ($r) {
log::debug(
'Successfully tested LDAP credentials (%s / %s)',
$username,
'***'
);
return true; return true;
} else { } else {
log::fatal( log::debug(
'LDAP connection (%s / %s) failed: %s', 'Testing LDAP credentials (%s / %s) failed: %s',
$username, $username,
'***', '***',
ldap_error($ds) ldap_error($ds)
); );
return false; return false;
/* TODO: Log failure
throw new Exception(sprintf(
'LDAP connection (%s / %s) failed: %s',
$username,
'***',
ldap_error($ds)
));
*/
} }
} }
/** protected function getConfigDir($sub = null)
* @return string
*/
protected function getConfigDir()
{ {
return Config::getInstance()->getConfigDir() . '/ldap'; $dir = Config::getInstance()->getConfigDir() . '/ldap';
if ($sub !== null) {
$dir .= '/' . $sub;
}
return $dir;
} }
/**
* @param $domain
*/
protected function discoverServerlistForDomain($domain) protected function discoverServerlistForDomain($domain)
{ {
$ldaps_records = dns_get_record('_ldaps._tcp.' . $domain, DNS_SRV); $ldaps_records = dns_get_record('_ldaps._tcp.' . $domain, DNS_SRV);
$ldap_records = dns_get_record('_ldap._tcp.' . $domain, DNS_SRV); $ldap_records = dns_get_record('_ldap._tcp.' . $domain, DNS_SRV);
} }
/** protected function prepareNewConnection()
* {
*/ $use_tls = false;
$force_tls = true;
$force_tls = false;
if ($use_tls) {
$this->prepareTlsEnvironment();
}
$ds = ldap_connect($this->hostname, $this->port);
$cap = $this->discoverCapabilities($ds);
if ($use_tls) {
if ($cap->starttls) {
if (@ldap_start_tls($ds)) {
Log::debug('LDAP STARTTLS succeeded');
} else {
Log::debug('LDAP STARTTLS failed: %s', ldap_error($ds));
throw new \Exception(
sprintf(
'LDAP STARTTLS failed: %s',
ldap_error($ds)
)
);
}
} elseif ($force_tls) {
throw new \Exception(
sprintf(
'TLS is required but not announced by %s',
$this->host_name
)
);
} else {
// TODO: Log noticy -> TLS enabled but not announced
}
}
// ldap_rename requires LDAPv3:
if ($cap->ldapv3) {
if (! ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3)) {
throw new Exception('LDAPv3 is required');
}
} else {
// TODO: remove this -> FORCING v3 for now
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
Log::warn('No LDAPv3 support detected');
}
// Not setting this results in "Operations error" on AD when using the
// whole domain as search base:
ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
// ldap_set_option($ds, LDAP_OPT_DEREF, LDAP_DEREF_NEVER);
return $ds;
}
protected function prepareTlsEnvironment() protected function prepareTlsEnvironment()
{ {
$strict_tls = true; $strict_tls = true;
$use_local_ca = true; // TODO: allow variable known CA location (system VS Icinga)
if (Platform::isWindows()) { if (Platform::isWindows()) {
// putenv('LDAP...')
} else { } else {
$cfg_dir = $this->getConfigDir();
if ($strict_tls) { if ($strict_tls) {
putenv(sprintf('LDAPRC=%s/%s', $cfg_dir, 'ldap_ca.conf')); $ldap_conf = $this->getConfigDir('ldap_ca.conf');
} else { } else {
putenv(sprintf('LDAPRC=%s/%s', $cfg_dir, 'ldap_nocert.conf')); $ldap_conf = $this->getConfigDir('ldap_nocert.conf');
}
putenv('LDAPRC=' . $ldap_conf);
if (getenv('LDAPRC') !== $ldap_conf) {
throw new Exception('putenv failed');
} }
} }
// file_put_contents('/tmp/tom_LDAP.conf', "TLS_REQCERT never\n");
} }
/** protected function discoverCapabilities($ds)
* @return object
*/
protected function fetchRootDseDetails()
{ {
$query = $this->select()->from('*', array('+')); $query = $this->select()->from(
'*',
/*, array( array(
'defaultNamingContext', 'defaultNamingContext',
'namingContexts', 'namingContexts',
'vendorName',
'vendorVersion',
'supportedSaslMechanisms', 'supportedSaslMechanisms',
'dnsHostName', 'dnsHostName',
'schemaNamingContext', 'schemaNamingContext',
'supportedLDAPVersion', // => array(3, 2) 'supportedLDAPVersion', // => array(3, 2)
'supportedCapabilities' 'supportedCapabilities',
))*/ 'supportedExtension',
'+'
)
);
$fields = $query->listFields(); $fields = $query->listFields();
$result = ldap_read( $result = @ldap_read(
$this->ds, $ds,
'', '',
(string)$query, (string) $query,
$query->listFields(), $query->listFields()
0,
0
); );
$entry = ldap_first_entry($this->ds, $result); if (! $result) {
$result = $this->cleanupAttributes(ldap_get_attributes($this->ds, $entry)); throw new Exception(
sprintf(
'Capability query failed: %s',
ldap_error($ds)
)
);
}
$entry = ldap_first_entry($ds, $result);
$cap = (object) array(
'ldapv3' => false,
'starttls' => false,
);
if ($entry === false) {
// TODO: Is it OK to have no capabilities?
return $cap;
}
$result = $this->cleanupAttributes(
ldap_get_attributes($ds, $entry)
);
/*
if (isset($result->dnsHostName)) {
ldap_set_option($ds, LDAP_OPT_HOST_NAME, $result->dnsHostName);
}
*/
if ((is_string($result->supportedLDAPVersion)
&& (int) $result->supportedLDAPVersion === 3)
|| (is_array($result->supportedLDAPVersion)
&& in_array(3, $result->supportedLDAPVersion)
)) {
$cap->ldapv3 = true;
}
if (isset($result->supportedCapabilities)) { if (isset($result->supportedCapabilities)) {
foreach ($result->supportedCapabilities as $oid) { foreach ($result->supportedCapabilities as $oid) {
if (array_key_exists($oid, $this->ms_capability)) { if (array_key_exists($oid, $this->ms_capability)) {
echo $this->ms_capability[$oid] . "\n"; // echo $this->ms_capability[$oid] . "\n";
} }
} }
} }
if (isset($result->supportedExtension)) { if (isset($result->supportedExtension)) {
foreach ($result->supportedExtension as $oid) { foreach ($result->supportedExtension as $oid) {
if (array_key_exists($oid, $this->ldap_extension)) { if (array_key_exists($oid, $this->ldap_extension)) {
// STARTTLS -> läuft mit OpenLDAP if ($this->ldap_extension[$oid] === 'STARTTLS') {
$cap->starttls = true;
} }
} }
} }
return $result; }
return $cap;
} }
/**
*
*/
public function discoverCapabilities()
{
$this->fetchRootDseDetails();
}
/**
* @throws Exception
*/
public function connect() public function connect()
{ {
if ($this->ds !== null) { if ($this->ds !== null) {
return; return;
} }
$this->ds = $this->prepareNewConnection();
if ($this->use_tls) {
$this->prepareTlsEnvironment();
}
Log::debug("Trying to connect to %s", $this->hostname);
$this->ds = ldap_connect($this->hostname, 389);
$this->discoverCapabilities();
if ($this->use_tls) {
Log::debug("Trying ldap_start_tls()");
if (@ldap_start_tls($this->ds)) {
Log::debug("Trying ldap_start_tls() succeeded");
} else {
Log::warn(
"ldap_start_tls() failed: %s. Does your ldap_ca.conf point to the certificate? ",
ldap_error($this->ds)
);
}
}
// ldap_rename requires LDAPv3:
if (!ldap_set_option($this->ds, LDAP_OPT_PROTOCOL_VERSION, 3)) {
throw new Exception('LDAPv3 is required');
}
// Not setting this results in "Operations error" on AD when using the
// whole domain as search base:
ldap_set_option($this->ds, LDAP_OPT_REFERRALS, 0);
// ldap_set_option($this->ds, LDAP_OPT_DEREF, LDAP_DEREF_NEVER);
Log::debug("Trying ldap_bind(%s)", $this->bind_dn);
$r = @ldap_bind($this->ds, $this->bind_dn, $this->bind_pw); $r = @ldap_bind($this->ds, $this->bind_dn, $this->bind_pw);
if (!$r) { if (! $r) {
log::fatal( throw new \Exception(
'LDAP connection (%s / %s) failed: %s',
$this->bind_dn,
'***',
ldap_error($this->ds)
);
throw new ConfigError(
sprintf( sprintf(
'Could not connect to the authentication server, please '. 'LDAP connection to %s:%s (%s / %s) failed: %s',
'review your LDAP connection settings.' $this->hostname,
$this->port,
$this->bind_dn,
'***' /* $this->bind_pw */,
ldap_error($this->ds)
) )
); );
} }
} }
public function __destruct()
{
putenv('LDAPRC');
}
} }

View File

@ -1,32 +1,12 @@
<?php <?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Icinga 2 Web - Head for multiple monitoring frontends
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Ldap; namespace Icinga\Protocol\Ldap;
use Icinga\Web\Paginator\Adapter\QueryAdapter; /**
* Search class
*
* @package Icinga\Protocol\Ldap
*/
/** /**
* Search abstraction class * Search abstraction class
* *
@ -40,50 +20,22 @@ use Icinga\Web\Paginator\Adapter\QueryAdapter;
* @author Icinga-Web Team <info@icinga.org> * @author Icinga-Web Team <info@icinga.org>
* @package Icinga\Protocol\Ldap * @package Icinga\Protocol\Ldap
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
* @package Icinga\Protocol\Ldap
*/ */
class Query class Query
{ {
/**
* @var Connection
*/
protected $connection; protected $connection;
/**
* @var array
*/
protected $filters = array(); protected $filters = array();
/**
* @var array
*/
protected $fields = array(); protected $fields = array();
/**
* @var
*/
protected $limit_count; protected $limit_count;
/**
* @var
*/
protected $limit_offset; protected $limit_offset;
/**
* @var array
*/
protected $sort_columns = array(); protected $sort_columns = array();
/**
* @var
*/
protected $count; protected $count;
/** /**
* Constructor * Constructor
* *
* @param \Icinga\Protocol\Ldap\Connection $connection LDAP Connection object * @param Connection LDAP Connection object
* @return \Icinga\Protocol\Ldap\Query * @return void
*/ */
public function __construct(Connection $connection) public function __construct(Connection $connection)
{ {
@ -106,14 +58,11 @@ class Query
/** /**
* Count result set, ignoring limits * Count result set, ignoring limits
* *
* @param null $count
* @param null $offset
* @throws Exception
* @return int * @return int
*/ */
public function limit($count = null, $offset = null) public function limit($count = null, $offset = null)
{ {
if (!preg_match('~^\d+~', $count . $offset)) { if (! preg_match('~^\d+~', $count . $offset)) {
throw new Exception( throw new Exception(
sprintf( sprintf(
'Got invalid limit: %s, %s', 'Got invalid limit: %s, %s',
@ -122,8 +71,8 @@ class Query
) )
); );
} }
$this->limit_count = (int)$count; $this->limit_count = (int) $count;
$this->limit_offset = (int)$offset; $this->limit_offset = (int) $offset;
return $this; return $this;
} }
@ -176,15 +125,12 @@ class Query
{ {
$result = $this->fetchAll(); $result = $this->fetchAll();
$sorted = array(); $sorted = array();
$quotedDn = preg_quote($this->connection->getDN(), '/');
foreach ($result as $key => & $item) { foreach ($result as $key => & $item) {
$new_key = LdapUtils::implodeDN( $new_key = LdapUtils::implodeDN(
array_reverse( array_reverse(
LdapUtils::explodeDN( LdapUtils::explodeDN(
preg_replace( preg_replace('/,' . $quotedDn . '$/', '', $key)
'/,' . preg_quote($this->connection->getDN(), '/') . '$/',
'',
$key
)
) )
) )
); );
@ -194,7 +140,11 @@ class Query
ksort($sorted); ksort($sorted);
$tree = Root::forConnection($this->connection); $tree = Root::forConnection($this->connection);
$root_dn = $tree->getDN();
foreach ($sorted as $sort_key => & $key) { foreach ($sorted as $sort_key => & $key) {
if ($key === $root_dn) {
continue;
}
$tree->createChildByDN($key, $result[$key]); $tree->createChildByDN($key, $result[$key]);
} }
return $tree; return $tree;
@ -246,8 +196,6 @@ class Query
* *
* This creates an objectClass filter * This creates an objectClass filter
* *
* @param $objectClass
* @param array $fields
* @return Query * @return Query
*/ */
public function from($objectClass, $fields = array()) public function from($objectClass, $fields = array())
@ -308,8 +256,6 @@ class Query
/** /**
* Return a pagination adapter for the current query * Return a pagination adapter for the current query
* *
* @param null $limit
* @param null $page
* @return \Zend_Paginator * @return \Zend_Paginator
*/ */
public function paginate($limit = null, $page = null) public function paginate($limit = null, $page = null)
@ -325,7 +271,7 @@ class Query
} }
$paginator = new \Zend_Paginator( $paginator = new \Zend_Paginator(
// TODO: Adapter doesn't fit yet: // TODO: Adapter doesn't fit yet:
new QueryAdapter($this) new \Icinga\Web\Paginator\Adapter\QueryAdapter($this)
); );
$paginator->setItemCountPerPage($limit); $paginator->setItemCountPerPage($limit);
$paginator->setCurrentPageNumber($page); $paginator->setCurrentPageNumber($page);
@ -364,7 +310,7 @@ class Query
} }
/** /**
* Destructor * Descructor
*/ */
public function __destruct() public function __destruct()
{ {

View File

@ -1,6 +1,28 @@
<?php <?php
namespace Icinga\Protocol\Livestatus; namespace Icinga\Protocol\Livestatus;
use Icinga\Application\Benchmark;
use Exception;
/**
* Backend class managing handling MKI Livestatus connections
*
* Usage example:
*
* <code>
* $lconf = new Connection((object) array(
* 'hostname' => 'localhost',
* 'root_dn' => 'dc=monitoring,dc=...',
* 'bind_dn' => 'cn=Mangager,dc=monitoring,dc=...',
* 'bind_pw' => '***'
* ));
* </code>
*
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
* @author Icinga-Web Team <info@icinga.org>
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
*/
class Connection class Connection
{ {
const TYPE_UNIX = 1; const TYPE_UNIX = 1;
@ -49,19 +71,23 @@ class Connection
$this->assertPhpExtensionLoaded('sockets'); $this->assertPhpExtensionLoaded('sockets');
if ($socket[0] === '/') { if ($socket[0] === '/') {
if (! is_writable($socket)) { if (! is_writable($socket)) {
throw new \Exception(sprintf( throw new \Exception(
sprintf(
'Cannot write to livestatus socket "%s"', 'Cannot write to livestatus socket "%s"',
$socket $socket
)); )
);
} }
$this->socket_type = self::TYPE_UNIX; $this->socket_type = self::TYPE_UNIX;
$this->socket_path = $socket; $this->socket_path = $socket;
} else { } else {
if (! preg_match('~^tcp://([^:]+):(\d+)~', $socket, $m)) { if (! preg_match('~^tcp://([^:]+):(\d+)~', $socket, $m)) {
throw new \Exception(sprintf( throw new \Exception(
sprintf(
'Invalid TCP socket syntax: "%s"', 'Invalid TCP socket syntax: "%s"',
$socket $socket
)); )
);
} }
// TODO: Better syntax checks // TODO: Better syntax checks
$this->socket_host = $m[1]; $this->socket_host = $m[1];
@ -80,17 +106,17 @@ class Connection
{ {
$count = clone($query); $count = clone($query);
$count->count(); $count->count();
\Icinga\Benchmark::measure('Sending Livestatus Count Query'); Benchmark::measure('Sending Livestatus Count Query');
$data = $this->_fetch((string) $count); $data = $this->doFetch((string) $count);
\Icinga\Benchmark::measure('Got Livestatus count result'); Benchmark::measure('Got Livestatus count result');
return $data[0][0]; return $data[0][0];
} }
public function fetchAll(Query $query) public function fetchAll(Query $query)
{ {
\Icinga\Benchmark::measure('Sending Livestatus Query'); Benchmark::measure('Sending Livestatus Query');
$data = $this->_fetch((string) $query); $data = $this->doFetch((string) $query);
\Icinga\Benchmark::measure('Got Livestatus Data'); Benchmark::measure('Got Livestatus Data');
if ($query->hasColumns()) { if ($query->hasColumns()) {
$headers = $query->getColumnAliases(); $headers = $query->getColumnAliases();
} else { } else {
@ -114,12 +140,12 @@ class Connection
$query->getLimit() $query->getLimit()
); );
} }
\Icinga\Benchmark::measure('Data sorted, limits applied'); Benchmark::measure('Data sorted, limits applied');
return $result; return $result;
} }
protected function _fetch($raw_query) protected function doFetch($raw_query)
{ {
$conn = $this->getConnection(); $conn = $this->getConnection();
$this->writeToSocket($raw_query); $this->writeToSocket($raw_query);
@ -128,15 +154,17 @@ class Connection
$length = (int) trim(substr($header, 4)); $length = (int) trim(substr($header, 4));
$body = $this->readFromSocket($length); $body = $this->readFromSocket($length);
if ($status !== 200) { if ($status !== 200) {
throw new \Exception(sprintf( throw new Exception(
sprintf(
'Problem while reading %d bytes from livestatus: %s', 'Problem while reading %d bytes from livestatus: %s',
$length, $length,
$body $body
)); )
);
} }
$result = json_decode($body); $result = json_decode($body);
if ($result === null) { if ($result === null) {
throw new \Exception('Got invalid response body from livestatus'); throw new Exception('Got invalid response body from livestatus');
} }
return $result; return $result;
@ -147,13 +175,15 @@ class Connection
$offset = 0; $offset = 0;
$buffer = ''; $buffer = '';
while($offset < $length) { while ($offset < $length) {
$data = socket_read($this->connection, $length - $offset); $data = socket_read($this->connection, $length - $offset);
if ($data === false) { if ($data === false) {
throw new \Exception(sprintf( throw new Exception(
sprintf(
'Failed to read from livestatus socket: %s', 'Failed to read from livestatus socket: %s',
socket_strerror(socket_last_error($this->connection)) socket_strerror(socket_last_error($this->connection))
)); )
);
} }
$size = strlen($data); $size = strlen($data);
$offset += $size; $offset += $size;
@ -164,10 +194,13 @@ class Connection
} }
} }
if ($offset !== $length) { if ($offset !== $length) {
throw new \Exception(sprintf( throw new \Exception(
sprintf(
'Got only %d instead of %d bytes from livestatus socket', 'Got only %d instead of %d bytes from livestatus socket',
$offset, $length $offset,
)); $length
)
);
} }
return $buffer; return $buffer;
@ -185,10 +218,12 @@ class Connection
protected function assertPhpExtensionLoaded($name) protected function assertPhpExtensionLoaded($name)
{ {
if (! extension_loaded($name)) { if (! extension_loaded($name)) {
throw new \Exception(sprintf( throw new \Exception(
sprintf(
'The extension "%s" is not loaded', 'The extension "%s" is not loaded',
$name $name
)); )
);
} }
} }
@ -213,12 +248,14 @@ class Connection
$this->connection = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); $this->connection = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (! @socket_connect($this->connection, $this->socket_host, $this->socket_port)) { if (! @socket_connect($this->connection, $this->socket_host, $this->socket_port)) {
throw new \Exception(sprintf( throw new \Exception(
sprintf(
'Cannot connect to livestatus TCP socket "%s:%d": %s', 'Cannot connect to livestatus TCP socket "%s:%d": %s',
$this->socket_host, $this->socket_host,
$this->socket_port, $this->socket_port,
socket_strerror(socket_last_error($this->connection)) socket_strerror(socket_last_error($this->connection))
)); )
);
} }
socket_set_option($this->connection, SOL_TCP, TCP_NODELAY, 1); socket_set_option($this->connection, SOL_TCP, TCP_NODELAY, 1);
} }
@ -227,10 +264,12 @@ class Connection
{ {
$this->connection = socket_create(AF_UNIX, SOCK_STREAM, 0); $this->connection = socket_create(AF_UNIX, SOCK_STREAM, 0);
if (! socket_connect($this->connection, $this->socket_path)) { if (! socket_connect($this->connection, $this->socket_path)) {
throw new \Exception(sprintf( throw new \Exception(
sprintf(
'Cannot connect to livestatus local socket "%s"', 'Cannot connect to livestatus local socket "%s"',
$this->socket_path $this->socket_path
)); )
);
} }
} }
@ -246,4 +285,3 @@ class Connection
$this->disconnect(); $this->disconnect();
} }
} }

View File

@ -1,9 +1,10 @@
<?php <?php
namespace Icinga\Protocol\Livestatus; namespace Icinga\Protocol\Livestatus;
use Icinga\Protocol;
class Query extends Protocol\AbstractQuery use Icinga\Protocol\AbstractQuery;
class Query extends AbstractQuery
{ {
protected $connection; protected $connection;
@ -62,7 +63,10 @@ class Query extends Protocol\AbstractQuery
public function order($col) public function order($col)
{ {
if (($pos = strpos($col, ' ')) !== false) { if (($pos = strpos($col, ' ')) === false) {
$col = $col;
$dir = self::SORT_ASC;
} else {
$dir = strtoupper(substr($col, $pos + 1)); $dir = strtoupper(substr($col, $pos + 1));
if ($dir === 'DESC') { if ($dir === 'DESC') {
$dir = self::SORT_DESC; $dir = self::SORT_DESC;
@ -70,8 +74,6 @@ class Query extends Protocol\AbstractQuery
$dir = self::SORT_ASC; $dir = self::SORT_ASC;
} }
$col = substr($col, 0, $pos); $col = substr($col, 0, $pos);
} else {
$col = $col;
} }
$this->order_columns[] = array($col, $dir); $this->order_columns[] = array($col, $dir);
return $this; return $this;
@ -82,11 +84,13 @@ class Query extends Protocol\AbstractQuery
public function limit($count = null, $offset = null) public function limit($count = null, $offset = null)
{ {
if (! preg_match('~^\d+~', $count . $offset)) { if (! preg_match('~^\d+~', $count . $offset)) {
throw new Exception(sprintf( throw new Exception(
sprintf(
'Got invalid limit: %s, %s', 'Got invalid limit: %s, %s',
$count, $count,
$offset $offset
)); )
);
} }
$this->limit_count = (int) $count; $this->limit_count = (int) $count;
$this->limit_offset = (int) $offset; $this->limit_offset = (int) $offset;
@ -116,10 +120,12 @@ class Query extends Protocol\AbstractQuery
public function from($table, $columns = null) public function from($table, $columns = null)
{ {
if (! $this->connection->hasTable($table)) { if (! $this->connection->hasTable($table)) {
throw new Exception(sprintf( throw new Exception(
sprintf(
'This livestatus connection does not provide "%s"', 'This livestatus connection does not provide "%s"',
$table $table
)); )
);
} }
$this->table = $table; $this->table = $table;
if (is_array($columns)) { if (is_array($columns)) {
@ -207,4 +213,3 @@ class Query extends Protocol\AbstractQuery
unset($this->connection); unset($this->connection);
} }
} }

View File

@ -1,35 +1,13 @@
<?php <?php
// {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}}
/**
* Icinga 2 Web - Head for multiple monitoring frontends
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Web; namespace Icinga\Web;
use Icinga\Authentication\Manager; use Icinga\Authentication\Manager as AuthManager;
use Icinga\Application\Benchmark; use Icinga\Application\Benchmark;
use Icinga\Exception; use Icinga\Exception;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Pdf\File;
use Icinga\Web\Notification; use Icinga\Web\Notification;
use Zend_Layout as ZfLayout; use Zend_Layout as ZfLayout;
use Zend_Controller_Action as ZfController; use Zend_Controller_Action as ZfController;
@ -37,6 +15,11 @@ use Zend_Controller_Request_Abstract as ZfRequest;
use Zend_Controller_Response_Abstract as ZfResponse; use Zend_Controller_Response_Abstract as ZfResponse;
use Zend_Controller_Action_HelperBroker as ZfActionHelper; use Zend_Controller_Action_HelperBroker as ZfActionHelper;
/*
* @TODO(el): There was a note from tg that the following line is temporary. Ask him why.
*/
use Icinga\File\Pdf;
/** /**
* Base class for all core action controllers * Base class for all core action controllers
* *
@ -45,21 +28,16 @@ use Zend_Controller_Action_HelperBroker as ZfActionHelper;
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org> * @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
* @author Icinga-Web Team <info@icinga.org> * @author Icinga-Web Team <info@icinga.org>
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
* @package Icinga\Web
*/ */
class ActionController extends ZfController class ActionController extends ZfController
{ {
/** /**
* The Icinga Config object is available in all controllers. This is the * The Icinga Config object is available in all controllers. This is the
* modules config for module action controllers. * modules config for module action controllers.
*
* @var Config * @var Config
*/ */
protected $config; protected $config;
/**
* @var bool
*/
protected $replaceLayout = false; protected $replaceLayout = false;
/** /**
@ -84,20 +62,12 @@ class ActionController extends ZfController
*/ */
protected $action_name; protected $action_name;
/** // @TODO(el): Should be true, is/was disabled for testing purpose
* @var bool
*/
protected $handlesAuthentication = false; protected $handlesAuthentication = false;
/**
* @var bool
*/
protected $modifiesSession = false; protected $modifiesSession = false;
/** private $allowAccess = false;
* @var bool
*/
protected $allowAccess = false;
/** /**
* The constructor starts benchmarking, loads the configuration and sets * The constructor starts benchmarking, loads the configuration and sets
@ -113,6 +83,7 @@ class ActionController extends ZfController
array $invokeArgs = array() array $invokeArgs = array()
) { ) {
Benchmark::measure('Action::__construct()'); Benchmark::measure('Action::__construct()');
$this->module_name = $request->getModuleName(); $this->module_name = $request->getModuleName();
$this->controller_name = $request->getControllerName(); $this->controller_name = $request->getControllerName();
$this->action_name = $request->getActionName(); $this->action_name = $request->getActionName();
@ -123,20 +94,11 @@ class ActionController extends ZfController
->_setInvokeArgs($invokeArgs); ->_setInvokeArgs($invokeArgs);
$this->_helper = new ZfActionHelper($this); $this->_helper = new ZfActionHelper($this);
/*
* --------------------------------------------
* Authentication is disabled to test bootstrap
* --------------------------------------------
*
* @todo remove this!
*/
if ($this->handlesAuthentication() || if ($this->handlesAuthentication() ||
Manager::getInstance( AuthManager::getInstance(
null, null,
array( array(
"writeSession" => $this->modifiesSession 'writeSession' => $this->modifiesSession
) )
)->isAuthenticated() )->isAuthenticated()
) { ) {
@ -171,7 +133,7 @@ class ActionController extends ZfController
* Helper function creating a new widget * Helper function creating a new widget
* *
* @param string $name The widget name * @param string $name The widget name
* @param array|string $properties Optional widget properties * @param string $properties Optional widget properties
* *
* @return Widget\AbstractWidget * @return Widget\AbstractWidget
*/ */
@ -185,9 +147,8 @@ class ActionController extends ZfController
* *
* TODO: This has not been implemented yet * TODO: This has not been implemented yet
* *
* @param $uri
* @param string $permission Permission name * @param string $permission Permission name
* @internal param string $object No idea what this should have been :-) * @param string $object No idea what this should have been :-)
* *
* @return bool * @return bool
*/ */
@ -204,18 +165,31 @@ class ActionController extends ZfController
* @param string $permission Permission name * @param string $permission Permission name
* @param string $object No idea what this should have been :-) * @param string $object No idea what this should have been :-)
* *
* @throws \Exception
* @return self * @return self
*/ */
final protected function assertPermission($permission, $object = null) final protected function assertPermission($permission, $object = null)
{ {
if (!$this->hasPermission($permission, $object)) { if (! $this->hasPermission($permission, $object)) {
// TODO: Log violation, create dedicated Exception class // TODO: Log violation, create dedicated Exception class
throw new \Exception('Permission denied'); throw new \Exception('Permission denied');
} }
return $this; return $this;
} }
protected function preserve($key, $value = null)
{
if ($value === null) {
$value = $this->_getParam($key);
}
if ($value !== null) {
if (! isset($this->view->preserve)) {
$this->view->preserve = array();
}
$this->view->preserve[$key] = $value;
}
return $this;
}
/** /**
* Our benchmark wants to know when we started our dispatch loop * Our benchmark wants to know when we started our dispatch loop
* *
@ -224,8 +198,7 @@ class ActionController extends ZfController
public function preDispatch() public function preDispatch()
{ {
Benchmark::measure('Action::preDispatch()'); Benchmark::measure('Action::preDispatch()');
if (! $this->allowAccess) {
if (!$this->allowAccess) {
$this->_request->setModuleName('default') $this->_request->setModuleName('default')
->setControllerName('authentication') ->setControllerName('authentication')
->setActionName('login') ->setActionName('login')
@ -237,21 +210,27 @@ class ActionController extends ZfController
$this->view->controller_name = $this->controller_name; $this->view->controller_name = $this->controller_name;
$this->view->module_name = $this->module_name; $this->view->module_name = $this->module_name;
// TODO(el): What is this, why do we need that here?
$this->view->compact = $this->_request->getParam('view') === 'compact';
Benchmark::measure(sprintf(
'Action::preDispatched(): %s / %s / %s',
$this->module_name,
$this->controller_name,
$this->action_name
));
//$this->quickRedirect('/authentication/login?a=e'); //$this->quickRedirect('/authentication/login?a=e');
} }
/**
* @param $url
* @param array $params
*/
public function redirectNow($url, array $params = array()) public function redirectNow($url, array $params = array())
{ {
if ($url instanceof Url) {
$url = $url->getRelative();
}
$this->_helper->Redirector->gotoUrlAndExit($url); $this->_helper->Redirector->gotoUrlAndExit($url);
} }
/**
* @return bool
*/
public function handlesAuthentication() public function handlesAuthentication()
{ {
return $this->handlesAuthentication; return $this->handlesAuthentication;
@ -284,7 +263,6 @@ class ActionController extends ZfController
{ {
Benchmark::measure('Action::postDispatch()'); Benchmark::measure('Action::postDispatch()');
// TODO: Move this elsewhere, this is just an ugly test: // TODO: Move this elsewhere, this is just an ugly test:
if ($this->_request->getParam('filetype') === 'pdf') { if ($this->_request->getParam('filetype') === 'pdf') {
@ -296,8 +274,8 @@ class ActionController extends ZfController
// TODO: We need a way to retrieve public dir, even if located elsewhere // TODO: We need a way to retrieve public dir, even if located elsewhere
$css = $less->compileFile($cssdir . '/pdfprint.less'); $css = $less->compileFile($cssdir . '/pdfprint.less');
/* /*
foreach ($app->moduleManager()->getLoadedModules() as $name => $module) { foreach (\Icinga\Application\Icinga::app()->moduleManager()->getLoadedModules() as $name => $module) {
if ($module->hasCss()) { if ($module->hasCss()) {
$css .= $less->compile( $css .= $less->compile(
'.icinga-module.module-' '.icinga-module.module-'
@ -308,8 +286,8 @@ class ActionController extends ZfController
); );
} }
} }
*/
*/
// END of CSS test // END of CSS test
$this->render( $this->render(
@ -317,16 +295,32 @@ class ActionController extends ZfController
$this->_helper->viewRenderer->getResponseSegment(), $this->_helper->viewRenderer->getResponseSegment(),
$this->_helper->viewRenderer->getNoController() $this->_helper->viewRenderer->getNoController()
); );
$html = '<style>' . $css . '</style>' . (string)$this->getResponse(); $html = (string) $this->getResponse();
if ($this->module_name !== null) {
$html = '<div class="icinga-module module-'
. $this->module_name
. '">'
. "\n"
. $html
. '</div>';
}
$pdf = new File(); $html = '<style>' . $css . '</style>' . $html;
//$html .= $this->view->action('services', 'list', 'monitoring', array('limit' => 10));
// $html = preg_replace('~icinga-module.module-bpapp~', 'csstest', $html);
// echo $html; exit;
$pdf = new Pdf();
$pdf->AddPage(); $pdf->AddPage();
$pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true); $pdf->setFontSubsetting(false);
$pdf->writeHTML($html); //0, 0, '', '', $html, 0, 1, 0, true, '', true);
$pdf->Output('docs.pdf', 'I'); $pdf->Output('docs.pdf', 'I');
exit; exit;
} }
// END of PDF test // END of PDF test
if ($this->_request->isXmlHttpRequest()) { if ($this->_request->isXmlHttpRequest()) {
if ($this->replaceLayout || $this->_getParam('_render') === 'body') { if ($this->replaceLayout || $this->_getParam('_render') === 'body') {
$this->_helper->layout()->setLayout('just-the-body'); $this->_helper->layout()->setLayout('just-the-body');
@ -335,7 +329,6 @@ class ActionController extends ZfController
$this->_helper->layout()->setLayout('inline'); $this->_helper->layout()->setLayout('inline');
} }
} }
$notification = Notification::getInstance(); $notification = Notification::getInstance();
if ($notification->hasMessages()) { if ($notification->hasMessages()) {
$nhtml = '<ul class="notification">'; $nhtml = '<ul class="notification">';
@ -349,10 +342,10 @@ class ActionController extends ZfController
$this->getResponse()->append('notification', $nhtml); $this->getResponse()->append('notification', $nhtml);
} }
/*if (Session::getInstance()->show_benchmark) { if (AuthManager::getInstance()->getSession()->get('show_benchmark')) {
Benchmark::measure('Response ready'); Benchmark::measure('Response ready');
$this->getResponse()->append('benchmark', $this->renderBenchmark()); $this->_helper->layout()->benchmark = $this->renderBenchmark();
}*/ }
} }
} }

View File

@ -1,28 +1,8 @@
<?php <?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Icinga 2 Web - Head for multiple monitoring frontends
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Module action controller
*/
namespace Icinga\Web; namespace Icinga\Web;
use Icinga\Application\Config; use Icinga\Application\Config;
@ -84,7 +64,7 @@ class ModuleActionController extends ActionController
*/ */
protected function loadConfig() protected function loadConfig()
{ {
$this->config = Config::getInstance()->{$this->module_name}; $this->config = Config::module($this->module_name);
} }
/** /**

View File

@ -1,106 +1,47 @@
<?php <?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Icinga 2 Web - Head for multiple monitoring frontends
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Web; namespace Icinga\Web;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
use Icinga\Application\Platform; use Icinga\Application\Platform;
use Icinga\Application\Logger as Log; use Icinga\Application\Logger as Log;
use Icinga\Authentication\Manager as AuthManager;
/** /**
* Class Notification * // @TODO(eL): Use Notification not as Singleton but within request:
* @package Icinga\Web * <code>
* <?php
* $request->[getUser()]->notify('some message', Notification::INFO);
* </code>
*/ */
class Notification class Notification
{ {
/** protected static $instance;
* @var Notification protected $isCli = false;
*/
private static $instance;
/**
* @var bool
*/
private $cliFlag = false;
/**
* @param boolean $cliFlag
*/
public function setCliFlag($cliFlag)
{
$this->cliFlag = $cliFlag;
}
/**
* @return boolean
*/
public function getCliFlag()
{
return $this->cliFlag;
}
/**
* @param $msg
*/
public static function info($msg) public static function info($msg)
{ {
self::getInstance()->addMessage($msg, 'info'); self::getInstance()->addMessage($msg, 'info');
} }
/**
* @param $msg
*/
public static function success($msg) public static function success($msg)
{ {
self::getInstance()->addMessage($msg, 'success'); self::getInstance()->addMessage($msg, 'success');
} }
/**
* @param $msg
*/
public static function warning($msg) public static function warning($msg)
{ {
self::getInstance()->addMessage($msg, 'warning'); self::getInstance()->addMessage($msg, 'warning');
} }
/**
* @param $msg
*/
public static function error($msg) public static function error($msg)
{ {
self::getInstance()->addMessage($msg, 'error'); self::getInstance()->addMessage($msg, 'error');
} }
/** protected function addMessage($message, $type = 'info')
* @param $message
* @param string $type
* @throws \Icinga\Exception\ProgrammingError
*/
public function addMessage($message, $type = 'info')
{ {
if (!in_array( if (! in_array(
$type, $type,
array( array(
'info', 'info',
@ -108,8 +49,7 @@ class Notification
'warning', 'warning',
'success' 'success'
) )
) )) {
) {
throw new ProgrammingError( throw new ProgrammingError(
sprintf( sprintf(
'"%s" is not a valid notification type', '"%s" is not a valid notification type',
@ -118,7 +58,7 @@ class Notification
); );
} }
if ($this->cliFlag) { if ($this->is_cli) {
$msg = sprintf('[%s] %s', $type, $message); $msg = sprintf('[%s] %s', $type, $message);
switch ($type) { switch ($type) {
case 'info': case 'info':
@ -135,7 +75,7 @@ class Notification
return; return;
} }
$mo = (object)array( $mo = (object) array(
'type' => $type, 'type' => $type,
'message' => $message, 'message' => $message,
); );
@ -146,17 +86,11 @@ class Notification
$this->session->messages = $msgs; $this->session->messages = $msgs;
} }
/**
* @return bool
*/
public function hasMessages() public function hasMessages()
{ {
return !empty($this->session->messages); return ! empty($this->session->messages);
} }
/**
* @return mixed
*/
public function getMessages() public function getMessages()
{ {
$msgs = $this->session->messages; $msgs = $this->session->messages;
@ -164,24 +98,18 @@ class Notification
return $msgs; return $msgs;
} }
/**
* Create a new Notification object
*/
final private function __construct() final private function __construct()
{ {
//$this->session = new SessionNamespace('IcingaNotification'); $this->session = AuthManager::getInstance()->getSession();
//if (!is_array($this->session->messages)) { if (!is_array($this->session->get('messages'))) {
$this->session->messages = array(); $this->session->messages = array();
//} }
if (Platform::isCli()) { if (Platform::isCli()) {
$this->cliFlag = true; $this->is_cli = true;
} }
} }
/**
* @return Notification
*/
public static function getInstance() public static function getInstance()
{ {
if (self::$instance === null) { if (self::$instance === null) {

View File

@ -8,31 +8,39 @@ use Icinga\Exception\ProgrammingError;
class Url class Url
{ {
protected $params = array(); protected $params = array();
protected $url; protected $path;
protected $baseUrl; protected $baseUrl;
protected $request;
public function __construct($url, $params = null) public function __construct($url, $params = null, $request = null)
{ {
if (($split = strpos($url, '?')) === false) { if ($request === null) {
$this->url = $url; $this->request = Icinga::app()->frontController()->getRequest();
if (! empty($params)) {
$this->params = $params;
}
} else { } else {
$this->url = substr($url, 0, $split); // Tests only
$this->request = $request;
}
if ($url === null) {
$this->path = $this->request->getPathInfo();
$this->params = $this->request->getQuery();
} else {
if (($split = strpos($url, '?')) === false) {
$this->path = $url;
} else {
$this->path = substr($url, 0, $split);
// TODO: Use something better than parse_str
parse_str(substr($url, $split + 1), $urlParams); parse_str(substr($url, $split + 1), $urlParams);
$this->params = $urlParams; $this->params = $urlParams;
}
}
if (! empty($params)) { if (! empty($params)) {
$this->params += $params; $this->setParams($params);
// TODO: Test += behavior!
} }
} }
} public static function create($url, $params = null, $request = null)
public static function create($url, $params = null)
{ {
$u = new Url($url, $params); $u = new Url($url, $params, $request);
return $u; return $u;
} }
@ -43,30 +51,15 @@ class Url
return $this; return $this;
} }
public static function current() public static function current($request = null)
{ {
$app = Icinga::app(); $url = new Url(null, null, $request);
$view = $app->getView()->view;
$request = $app->frontController()->getRequest();
$parts = array();
// TODO: getQuery!
$params = $request->getParams();
foreach (array('module', 'controller', 'action') as $param) {
if ($view->{$param . '_name'} !== 'default') {
$parts[] = $view->{$param . '_name'};
}
if (array_key_exists($param, $params)) {
unset($params[$param]);
}
}
$rel = implode('/', $parts);
$url = new Url($rel, $params);
return $url; return $url;
} }
public function getScript() public function getPath()
{ {
return $this->url; return $this->path;
} }
public function getRelative() public function getRelative()
@ -79,9 +72,9 @@ class Url
$args[] = rawurlencode($name) . '=' . rawurlencode($value); $args[] = rawurlencode($name) . '=' . rawurlencode($value);
} }
} }
$url = vsprintf($this->url, $params); $url = vsprintf($this->path, $params);
if (! empty($args)) { if (! empty($args)) {
$url .= '?' . implode('&', $args); $url .= '?' . implode('&amp;', $args);
} }
return $url; return $url;
} }
@ -94,17 +87,19 @@ class Url
public function setParams($params) public function setParams($params)
{ {
if ($params === null) {
$this->params = array();
} else {
$this->params = $params; $this->params = $params;
}
return $this; return $this;
} }
public function set($key, $val) public function getParams()
{ {
$this->params[$key] = $val; return $this->params;
return $this;
} }
public function hasParam($key) public function hasParam($key)
{ {
return array_key_exists($key, $this->params); return array_key_exists($key, $this->params);
@ -118,13 +113,16 @@ class Url
return $default; return $default;
} }
public function getParams() public function setParam($key, $value)
{ {
return $this->params; $this->params[$key] = $value;
return $this;
} }
public function without($keys) public function remove()
{ {
$args = func_get_args();
foreach ($args as $keys) {
if (! is_array($keys)) { if (! is_array($keys)) {
$keys = array($keys); $keys = array($keys);
} }
@ -133,16 +131,24 @@ class Url
unset($this->params[$key]); unset($this->params[$key]);
} }
} }
}
return $this; return $this;
} }
public function without()
{
$url = clone($this);
$args = func_get_args();
return call_user_func_array(array($url, 'remove'), $args);
}
public function __toString() public function __toString()
{ {
$url = $this->getRelative(); $url = $this->getRelative();
$base = is_null($this->baseUrl) $base = null === $this->baseUrl
? Icinga::app()->getView()->view->baseUrl() ? $this->request->getBaseUrl()
: $this->baseUrl; : $this->baseUrl;
if ($base === '') { if ($base === '' && $url[0]!== '/') {
// Otherwise all URLs would be relative to wherever you are // Otherwise all URLs would be relative to wherever you are
$base = '/'; $base = '/';
} }
@ -152,3 +158,4 @@ class Url
return $base . $url; return $base . $url;
} }
} }

View File

@ -0,0 +1,64 @@
<?php
namespace Icinga\Web;
use Zend_View_Abstract as ZfViewAbstract;
use Icinga\Web\Url;
use Icinga\Util\Format;
class View extends ZfViewAbstract
{
private $_useViewStream = false;
public function __construct($config = array())
{
$this->_useViewStream = (bool) ini_get('short_open_tag') ? false : true;
if ($this->_useViewStream) {
if (!in_array('zend.view', stream_get_wrappers())) {
stream_wrapper_register('zend.view', '\Icinga\Web\ViewStream');
}
}
parent::__construct($config);
}
public function init()
{
$this->loadGlobalHelpers();
}
protected function loadGlobalHelpers()
{
$pattern = dirname(__FILE__) . '/View/helpers/*.php';
$files = glob($pattern);
foreach ($files as $file) {
require_once $file;
}
}
protected function _run()
{
foreach ($this->getVars() as $k => $v) {
// Exporting global variables to view scripts:
$$k = $v;
}
if ($this->_useViewStream) {
include 'zend.view://' . func_get_arg(0);
} else {
include func_get_arg(0);
}
}
public function __call($name, $args)
{
$namespaced = '\\Icinga\\Web\\View\\' . $name;
if (function_exists($namespaced)) {
return call_user_func_array(
$namespaced,
$args
);
} else {
return parent::__call($name, $args);
}
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Icinga\Web\View;
use Icinga\Web\Url;
function url($path = null, $params = null)
{
if ($path === null) {
$url = Url::current();
if ($params !== null) {
$url->setParams($params);
}
} else {
$url = Url::create($path, $params);
}
return $url;
}

View File

@ -0,0 +1,10 @@
<?php
namespace Icinga\Web;
use Zend_View_Stream as ZfViewStream;
class ViewStream extends ZfViewStream
{
}

View File

@ -1,28 +1,8 @@
<?php <?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Icinga 2 Web - Head for multiple monitoring frontends
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
/**
* Web widget class
*/
namespace Icinga\Web; namespace Icinga\Web;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
@ -45,16 +25,16 @@ use Icinga\Exception\ProgrammingError;
*/ */
class Widget class Widget
{ {
/** /**
* Create a new widget * Create a new widget
* *
* @param string $name Widget name * @param string $name Widget name
* @param array $options Widget constructor options * @param array $options Widget constructor options
* *
* @throws \Icinga\Exception\ProgrammingError
* @return Icinga\Web\Widget\AbstractWidget * @return Icinga\Web\Widget\AbstractWidget
*/ */
public static function create($name, $options = array()) public static function create($name, $options = array(), $module_name = null)
{ {
$class = 'Icinga\\Web\\Widget\\' . ucfirst($name); $class = 'Icinga\\Web\\Widget\\' . ucfirst($name);
@ -67,7 +47,7 @@ class Widget
); );
} }
$widget = new $class($options); $widget = new $class($options, $module_name);
return $widget; return $widget;
} }
} }

View File

@ -8,6 +8,8 @@ namespace Icinga\Web\Widget;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
use Icinga\Web\Url; use Icinga\Web\Url;
use Countable;
/** /**
* Navigation tab widget * Navigation tab widget
* *
@ -17,7 +19,7 @@ use Icinga\Web\Url;
* @author Icinga-Web Team <info@icinga.org> * @author Icinga-Web Team <info@icinga.org>
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
*/ */
class Tabs extends AbstractWidget implements \Countable class Tabs extends AbstractWidget implements Countable
{ {
/** /**
* This is where single tabs added to this container will be stored * This is where single tabs added to this container will be stored
@ -201,25 +203,37 @@ class Tabs extends AbstractWidget implements \Countable
$special = array(); $special = array();
$special[] = $this->view()->qlink( $special[] = $this->view()->qlink(
'PDF', $this->view()->img('img/classic/application-pdf.png') . ' PDF',
Url::current(), Url::current(),
array('filetype' => 'pdf'), array('filetype' => 'pdf'),
array('target' => '_blank') array('target' => '_blank', 'quote' => false)
);
$special[] = $this->view()->qlink(
$this->view()->img('img/classic/application-csv.png') . ' CSV',
Url::current(),
array('format' => 'csv'),
array('target' => '_blank', 'quote' => false)
);
$special[] = $this->view()->qlink(
$this->view()->img('img/classic/application-json.png') . ' JSON',
Url::current(),
array('format' => 'json', 'quote' => false),
array('target' => '_blank', 'quote' => false)
); );
$special[] = $this->view()->qlink( $special[] = $this->view()->qlink(
'Basket', $this->view()->img('img/classic/basket.png') . ' URL Basket',
Url::create('basket/add'), Url::create('basket/add'),
array('url' => Url::current()->getRelative()) array('url' => Url::current()->getRelative()),
array('quote' => false)
); );
$special[] = $this->view()->qlink( $special[] = $this->view()->qlink(
'Dashboard', $this->view()->img('img/classic/dashboard.png') . ' Dashboard',
Url::create('dashboard/addurl'), Url::create('dashboard/addurl'),
array('url' => Url::current()->getRelative()) array('url' => Url::current()->getRelative()),
array('quote' => false)
); );
// @todo rework auth
// $auth = Auth::getInstance(); // $auth = Auth::getInstance();
// if ($this->specialActions && ! empty($special) && $auth->isAuthenticated() && $auth->getUsername() === 'admin') { // if ($this->specialActions && ! empty($special) && $auth->isAuthenticated() && $auth->getUsername() === 'admin') {
if ($this->specialActions) { if ($this->specialActions) {
@ -241,10 +255,6 @@ class Tabs extends AbstractWidget implements \Countable
return $html; return $html;
} }
/**
* Counting registered tabs
* @return int
*/
public function count() public function count()
{ {
return count($this->tabs); return count($this->tabs);

View File

@ -29,7 +29,7 @@ class Monitoring_CommandController extends ModuleActionController
throw new Exception("Invalid token given", 401); throw new Exception("Invalid token given", 401);
$this->_helper->viewRenderer->setNoRender(true); $this->_helper->viewRenderer->setNoRender(true);
$this->_helper->layout()->disableLayout(); $this->_helper->layout()->disableLayout();
$targets = Icinga\Application\Config::getInstance()->getModuleConfig("instances", "monitoring"); $targets = $this->config->instances;
$instance = $this->_getParam("instance"); $instance = $this->_getParam("instance");
if ($instance && isset($targets[$instance])) { if ($instance && isset($targets[$instance])) {
$this->target = new \Icinga\Protocol\Commandpipe\CommandPipe($targets[$instance]); $this->target = new \Icinga\Protocol\Commandpipe\CommandPipe($targets[$instance]);

View File

@ -2,6 +2,7 @@
use Icinga\Web\ModuleActionController; use Icinga\Web\ModuleActionController;
use Icinga\Web\Hook; use Icinga\Web\Hook;
use Icinga\File\Csv;
use Icinga\Monitoring\Backend; use Icinga\Monitoring\Backend;
use Icinga\Application\Benchmark; use Icinga\Application\Benchmark;
@ -83,9 +84,10 @@ class Monitoring_ListController extends ModuleActionController
$state_column = 'service_hard_state'; $state_column = 'service_hard_state';
$state_change_column = 'service_last_hard_state_change'; $state_change_column = 'service_last_hard_state_change';
} }
$this->view->services = $this->backend->select()
->from('status', array( $this->view->services = $this->query('status', array(
'host_name', 'host_name',
'host_problems',
'service_description', 'service_description',
'service_state' => $state_column, 'service_state' => $state_column,
'service_in_downtime', 'service_in_downtime',
@ -94,35 +96,11 @@ class Monitoring_ListController extends ModuleActionController
'service_output', 'service_output',
'service_last_state_change' => $state_change_column 'service_last_state_change' => $state_change_column
)); ));
$this->preserve('sort')
if ($search = $this->_getParam('search')) { ->preserve('backend')
$this->_setParam('search', null); ->preserve('extracolumns');
if (strpos($search, '=') === false) { $this->view->sort = $this->_getParam('sort');
$this->_setParam('service_description', $search); if ($this->view->compact) {
} else {
list($key, $val) = preg_split('~\s*=\s*~', $search, 2);
if ($this->view->services->isValidFilterColumn($key) || $key[0] === '_') {
$this->_setParam($key, $val);
}
}
}
$this->view->services->applyRequest($this->_request);
if ($this->_getParam('dump') === 'sql') {
echo '<pre>' . htmlspecialchars(wordwrap($this->view->services->getQuery()->dump())) . '</pre>';
exit;
}
$preserve = array();
if ($this->_getParam('sort')) {
$preserve['sort'] = $this->view->sort = $this->_getParam('sort');
}
if ($this->_getParam('backend')) {
$preserve['backend'] = $this->_getParam('backend');
}
$this->view->preserve = $preserve;
if ($this->_getParam('view') == 'compact') {
$this->_helper->viewRenderer('services-compact'); $this->_helper->viewRenderer('services-compact');
} }
} }
@ -181,19 +159,58 @@ class Monitoring_ListController extends ModuleActionController
exit; exit;
} }
protected function query($view, $columns)
{
$extra = preg_split(
'~,~',
$this->_getParam('extracolumns', ''),
-1,
PREG_SPLIT_NO_EMPTY
);
$this->view->extraColumns = $extra;
$query = $this->backend->select()
->from($view, array_merge($columns, $extra))
->applyRequest($this->_request);
$this->handleFormatRequest($query);
return $query;
}
protected function handleFormatRequest($query)
{
if ($this->_getParam('format') === 'sql') {
echo '<pre>'
. htmlspecialchars(wordwrap($query->getQuery()->dump()))
. '</pre>';
exit;
}
if ($this->_getParam('format') === 'json'
|| $this->_request->getHeader('Accept') === 'application/json')
{
header('Content-type: application/json');
echo json_encode($query->fetchAll());
exit;
}
if ($this->_getParam('format') === 'csv'
|| $this->_request->getHeader('Accept') === 'text/csv') {
Csv::fromQuery($query)->dump();
exit;
}
}
protected function getTabs() protected function getTabs()
{ {
$tabs = $this->widget('tabs'); $tabs = $this->widget('tabs');
$tabs->add('services', array( $tabs->add('services', array(
'title' => 'Services', 'title' => 'All services',
'icon' => 'img/classic/service.png', 'icon' => 'img/classic/service.png',
'url' => 'monitoring/list/services', 'url' => 'monitoring/list/services',
)); ));
$tabs->add('hosts', array( $tabs->add('hosts', array(
'title' => 'Hosts', 'title' => 'All hosts',
'icon' => 'img/classic/server.png', 'icon' => 'img/classic/server.png',
'url' => 'monitoring/list/hosts', 'url' => 'monitoring/list/hosts',
)); ));
/*
$tabs->add('hostgroups', array( $tabs->add('hostgroups', array(
'title' => 'Hostgroups', 'title' => 'Hostgroups',
'icon' => 'img/classic/servers-network.png', 'icon' => 'img/classic/servers-network.png',
@ -214,6 +231,7 @@ class Monitoring_ListController extends ModuleActionController
'icon' => 'img/classic/servers-network.png', 'icon' => 'img/classic/servers-network.png',
'url' => 'monitoring/list/contactgroups', 'url' => 'monitoring/list/contactgroups',
)); ));
*/
return $tabs; return $tabs;
} }
} }

View File

@ -5,7 +5,8 @@
use Icinga\Monitoring\Backend; use Icinga\Monitoring\Backend;
use Icinga\Web\ModuleActionController; use Icinga\Web\ModuleActionController;
use Icinga\Web\Hook; use Icinga\Web\Hook;
use Icinga\Application\Benchmark; use Icinga\Monitoring\Object\Host;
use Icinga\Monitoring\Object\Service;
/** /**
* Class Monitoring_ShowController * Class Monitoring_ShowController
@ -26,13 +27,18 @@ class Monitoring_ShowController extends ModuleActionController
{ {
$host = $this->_getParam('host'); $host = $this->_getParam('host');
$service = $this->_getParam('service'); $service = $this->_getParam('service');
$this->backend = Backend::getInstance($this->_getParam('backend'));
$object = null;
// TODO: Do not allow wildcards in names!
if ($host !== null) { if ($host !== null) {
// TODO: $this->assertPermission('host/read', $host); // TODO: $this->assertPermission('host/read', $host);
} if ($this->action_name !== 'host' && $service !== null && $service !== '*') {
if ($service !== null) {
// TODO: $this->assertPermission('service/read', $service); // TODO: $this->assertPermission('service/read', $service);
$object = Service::fetch($this->backend, $host, $service);
} else {
$object = Host::fetch($this->backend, $host);
}
} }
// TODO: don't allow wildcards
$this->backend = Backend::getInstance($this->_getParam('backend')); $this->backend = Backend::getInstance($this->_getParam('backend'));
if ($service !== null && $service !== '*') { if ($service !== null && $service !== '*') {
@ -42,30 +48,14 @@ class Monitoring_ShowController extends ModuleActionController
$this->view->host = $this->backend->fetchHost($host, true); $this->view->host = $this->backend->fetchHost($host, true);
} }
$this->view->compact = $this->_getParam('view') === 'compact'; $this->view->compact = $this->_getParam('view') === 'compact';
if ($object === null) {
// TODO: Notification, not found
$this->redirectNow('monitoring/list/services');
return;
}
$this->view->object = $object;
$this->view->tabs = $this->createTabs(); $this->view->tabs = $this->createTabs();
$this->prepareTicketHook();
// If ticket hook:
$params = array();
if ($host !== null) {
$params['host'] = $this->view->host->host_name;
}
if ($service !== null) {
$params['service'] = $this->view->service->service_description;
}
if (Hook::has('ticket')) {
$params['ticket'] = '__ID__';
$this->view->ticket_link = preg_replace(
'~__ID__~',
'\$1',
$this->view->qlink(
'#__ID__',
'monitoring/show/ticket',
$params
)
);
// TODO: Global ticket pattern config (or per environment)
$this->view->ticket_pattern = '~#(\d{4,6})~';
}
} }
/** /**
@ -144,6 +134,8 @@ class Monitoring_ShowController extends ModuleActionController
->where('object_type', 'service') ->where('object_type', 'service')
->fetchPairs(); ->fetchPairs();
Benchmark::measure('Service action done'); Benchmark::measure('Service action done');
$object = $this->view->object->prefetch();
$this->prepareGrapherHook();
} }
/** /**
@ -243,6 +235,8 @@ class Monitoring_ShowController extends ModuleActionController
->where('host_name', $this->view->host->host_name) ->where('host_name', $this->view->host->host_name)
->where('object_type', 'host') ->where('object_type', 'host')
->fetchPairs(); ->fetchPairs();
$this->view->object->prefetch();
$this->prepareGrapherHook();
} }
/** /**
@ -250,9 +244,6 @@ class Monitoring_ShowController extends ModuleActionController
*/ */
public function historyAction() public function historyAction()
{ {
if ($this->view->host) {
$this->view->tabs->activate('history')->enableSpecialActions();
}
$this->view->history = $this->backend->select() $this->view->history = $this->backend->select()
->from( ->from(
'eventHistory', 'eventHistory',
@ -269,7 +260,6 @@ class Monitoring_ShowController extends ModuleActionController
) )
)->applyRequest($this->_request); )->applyRequest($this->_request);
$this->view->preserve = $this->view->history->getAppliedFilter()->toParams(); $this->view->preserve = $this->view->history->getAppliedFilter()->toParams();
if ($this->_getParam('dump') === 'sql') { if ($this->_getParam('dump') === 'sql') {
echo '<pre>' . htmlspecialchars($this->view->history->getQuery()->dump()) . '</pre>'; echo '<pre>' . htmlspecialchars($this->view->history->getQuery()->dump()) . '</pre>';
@ -278,6 +268,7 @@ class Monitoring_ShowController extends ModuleActionController
if ($this->_getParam('sort')) { if ($this->_getParam('sort')) {
$this->view->preserve['sort'] = $this->_getParam('sort'); $this->view->preserve['sort'] = $this->_getParam('sort');
} }
$this->view->preserve = $this->view->history->getAppliedFilter()->toParams();
} }
/** /**
@ -285,6 +276,7 @@ class Monitoring_ShowController extends ModuleActionController
*/ */
public function servicesAction() public function servicesAction()
{ {
$this->_setParam('service', null);
// Ugly and slow: // Ugly and slow:
$this->view->services = $this->view->action( $this->view->services = $this->view->action(
'services', 'services',
@ -295,6 +287,9 @@ class Monitoring_ShowController extends ModuleActionController
//'sort', 'service_description' //'sort', 'service_description'
) )
); );
$this->view->services = $this->view->action('services', 'list', 'monitoring', array(
'view' => 'compact'
));
} }
/** /**
@ -302,10 +297,9 @@ class Monitoring_ShowController extends ModuleActionController
*/ */
public function ticketAction() public function ticketAction()
{ {
$this->view->tabs->activate('ticket')->enableSpecialActions();
$id = $this->_getParam('ticket');
// Still hardcoded, TODO: get URL:
if (Hook::has('ticket')) { if (Hook::has('ticket')) {
// TODO: Still hardcoded, should ask for URL:
$id = $this->_getParam('ticket');
$ticketModule = 'rt'; $ticketModule = 'rt';
$this->render(); $this->render();
$this->_forward( $this->_forward(
@ -319,12 +313,54 @@ class Monitoring_ShowController extends ModuleActionController
} }
} }
protected function prepareTicketHook()
{
if (Hook::has('ticket')) {
$object = $this->view->object;
$params = array(
'host' => $object->host_name
);
if ($object instanceof Service) {
$params['service'] = $object->service_description;
}
$params['ticket'] = '__ID__';
$this->view->ticket_link = preg_replace(
'~__ID__~',
'\$1',
$this->view->qlink('#__ID__',
'monitoring/show/ticket',
$params
)
);
// TODO: Global ticket pattern config (or per environment)
$this->view->ticket_pattern = '~#(\d{4,6})~';
}
}
protected function prepareGrapherHook()
{
if ($grapher = Hook::get('grapher')) {
$object = $this->view->object;
if ($grapher->hasGraph(
$object->host_name,
$object->service_description
)) {
$this->view->preview_image = $grapher->getPreviewImage(
$object->host_name,
$object->service_description
);
}
}
}
/** /**
* Creating tabs for this controller * Creating tabs for this controller
* @return \Icinga\Web\Widget\AbstractWidget * @return \Icinga\Web\Widget\AbstractWidget
*/ */
protected function createTabs() protected function createTabs()
{ {
$object = $this->view->object;
$tabs = $this->widget('tabs'); $tabs = $this->widget('tabs');
if (!$this->view->host) { if (!$this->view->host) {
return $tabs; return $tabs;
@ -335,11 +371,10 @@ class Monitoring_ShowController extends ModuleActionController
if ($backend = $this->_getParam('backend')) { if ($backend = $this->_getParam('backend')) {
$params['backend'] = $backend; $params['backend'] = $backend;
} }
if (isset($this->view->service)) { if ($object instanceof Service) {
$params['service'] = $this->view->service->service_description; $params['service'] = $object->service_description;
$hostParams = $params + array('active' => 'host'); } elseif ($service = $this->_getParam('service')) {
} else { $params['service'] = $service;
$hostParams = $params;
} }
$tabs->add( $tabs->add(
'host', 'host',
@ -347,7 +382,7 @@ class Monitoring_ShowController extends ModuleActionController
'title' => 'Host', 'title' => 'Host',
'icon' => 'img/classic/server.png', 'icon' => 'img/classic/server.png',
'url' => 'monitoring/show/host', 'url' => 'monitoring/show/host',
'urlParams' => $hostParams, 'urlParams' => $params,
) )
); );
$tabs->add( $tabs->add(
@ -390,7 +425,10 @@ class Monitoring_ShowController extends ModuleActionController
) )
); );
} }
/*
$tabs->activate($this->action_name)->enableSpecialActions();
/**
$tabs->add('contacts', array( $tabs->add('contacts', array(
'title' => 'Contacts', 'title' => 'Contacts',
'icon' => 'img/classic/customer.png', 'icon' => 'img/classic/customer.png',

View File

@ -0,0 +1,49 @@
<?php
use Icinga\Web\ModuleActionController;
use Icinga\Web\Url;
use Icinga\Monitoring\Backend;
use Zend_Soap_Server as ZfSoapServer;
use Zend_Soap_AutoDiscover as ZfSoapAutoDiscover;
class Api
{
/**
* @return array
*/
public function problems()
{
try {
$backend = Backend::getInstance('localdb');
$result = $backend->select()->from('status', array(
'host', 'service', 'host_state', 'service_state', 'service_output'
))->where('problems', 1)->fetchAll();
} catch (Exception $e) {
return array('error' => $e->getMessage());
}
return $result;
}
}
class Monitoring_SoapController extends ModuleActionController
{
protected $handlesAuthentication = true;
public function indexAction()
{
$wsdl = new ZfSoapAutoDiscover();
$wsdl->setClass('Api');
if (isset($_GET['wsdl'])) {
$wsdl->handle();
} else {
$wsdl->dump('/tmp/test.wsdl');
$uri = 'http://itenos-devel.tom.local/' . Url::create('monitoring/soap');
$server = new Zend_Soap_Server('/tmp/test.wsdl');
$server->setClass('Api');
$server->handle();
}
exit;
}
}

View File

@ -1,8 +1,14 @@
<?php if (empty($this->hosts)): ?> <?php
- no hosts is matching this filter -
<?php return; endif; ?> $count = $hosts->count();
<?php $hosts = $this->hosts->paginate(); ?> if (! $count) {
<table class="hosts action"> echo '- no host is matching this filter -';
return;
}
$hosts->paginate();
?><table class="hosts action">
<thead> <thead>
<tr> <tr>
<th style="width: 6em;" >State</th> <th style="width: 6em;" >State</th>
@ -10,8 +16,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($hosts as $host): ?> <?php foreach ($hosts->fetchAll() as $host):
<?php
$icons = array(); $icons = array();
if ($host->host_acknowledged) { if ($host->host_acknowledged) {
@ -54,6 +59,6 @@ $state_title = strtoupper($this->monitoringState($host, 'host'))
array('class' => 'row-action') array('class' => 'row-action')
) ?></td> ) ?></td>
</tr> </tr>
<?php endforeach; ?> <? endforeach ?>
</tbody> </tbody>
</table> </table>

View File

@ -1,14 +1,14 @@
<?php if (empty($this->services)): ?> <?php
<div class="alert alert-info fullpage_infobox">
Sorry, no services found for this search
</div>
<?php return; endif; ?>
<?
$services = $this->services->paginate(); $count = $services->count();
if (! $count) {
echo '- no object is matching this filter -';
return;
}
?> $services->paginate();
<table class="services action">
?><table class="services action">
<thead> <thead>
<tr> <tr>
<th style="width: 6em;" >State</th> <th style="width: 6em;" >State</th>
@ -16,24 +16,19 @@ $services = $this->services->paginate();
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($services as $service): ?> <?php foreach ($services->fetchAll() as $service):
<?php
$icons = array(); $icons = array();
if ($service->service_acknowledged) { if ($service->service_acknowledged) {
$icons['ack.gif'] = 'Problem has been acknowledged'; $icons['ack.gif'] = 'Problem has been acknowledged';
} }
if (! empty($service->service_downtimes_with_info)) { if ($service->service_in_downtime) {
$icons['downtime.gif'] = implode("\n", $icons['downtime.gif'] = 'Service is in a scheduled downtime';
$this->resolveComments($service->service_downtimes_with_info)
);
} }
if (! empty($service->service_comments_with_info)) { if ($service->host_problems) {
$icons['comment.gif'] = implode("\n", $icons['server.png'] = 'This services host has a problem';
$this->resolveComments($service->service_comments_with_info)
);
} }
$state_classes = array($this->monitoringState($service, 'service')); $state_classes = array($this->monitoringState($service, 'service'));
@ -54,20 +49,54 @@ if (isset($this->preserve['backend'])) {
} }
?> ?>
<tr class="<?= implode(' ', $state_classes) ?>"> <tr class="<?= implode(' ', $state_classes) ?>">
<td class="state" title="<?= $state_title ?>"> <td class="state" title="<?= $state_title ?>"><?=
<?= $this->qlink( $this->qlink(
$this->timeSince($service->service_last_state_change), $this->timeSince($service->service_last_state_change),
'monitoring/show/history', $params, array('quote' => false)) ?> 'monitoring/show/history',
</td> $params,
<td> array('quote' => false)
<?= $this->qlink( ) ?></td>
<td><?php
foreach ($icons as $icon => $alt) {
echo $this->img('img/classic/' . $icon, array(
'class' => 'icon',
'title' => $alt
));
}
echo $this->qlink(
$service->host_name, $service->host_name,
'monitoring/show/host', 'monitoring/show/host',
$params $params
); ?>: ) . ': ' . $this->qlink(
<?= $this->qlink($service->service_description, 'monitoring/show/service', $params, array('class' => 'row-action')) ?> $service->service_description,
</td> 'monitoring/show/service',
$params,
array('class' => 'row-action')
) ?></td>
</tr> </tr>
<?php endforeach; ?> <?php endforeach ?>
</tbody> </tbody>
</table> </table>
<?php
$limit = $services->getLimit();
$count = $services->count();
if ($limit < $count) {
$more = sprintf(
$this->translate(
'Showing %d of %s, click here for more',
$limit,
$count
)
);
} else {
$more = $this->translate(
sprintf(
'Showing %d objects, click here for details',
$count
)
);
}
?><p><?= $this->qlink($more,$this->url()->without('page', 'limit', 'view')) ?></p>

View File

@ -1,7 +1,7 @@
<?= $this->tabs ?> <?= $this->tabs ?>
<?php <?php
$this->services->limit(10);
$services = $this->services->paginate(); $paginator = $services->paginate();
function getRowProperties(&$service, &$last_host, $scope) { function getRowProperties(&$service, &$last_host, $scope) {
if ($last_host !== $service->host_name) { if ($last_host !== $service->host_name) {
@ -21,10 +21,14 @@ function getRowProperties(&$service, &$last_host, $scope) {
$icons['ack.gif'] = 'Problem has been acknowledged'; $icons['ack.gif'] = 'Problem has been acknowledged';
} }
if (! empty($service->service_in_downtime)) { if ($service->service_in_downtime) {
$icons['downtime.gif'] = 'Service is in a scheduled downtime'; $icons['downtime.gif'] = 'Service is in a scheduled downtime';
} }
if ($service->host_problems) {
$icons['server.png'] = 'This services host has a problem';
}
$state_classes = array($scope->monitoringState($service)); $state_classes = array($scope->monitoringState($service));
if ($service->service_handled) { if ($service->service_handled) {
@ -63,9 +67,9 @@ if (isset($_GET['dir'])) {
} }
?> ?>
<div class="dontprint"> <div class="dontprint">
<?php if (! empty($fparams)): ?> <? if (! empty($fparams)): ?>
<div style="float: right; width: 20em; font-size: 0.8em;"><b>Filters</b><br> <div style="float: right; width: 20em; font-size: 0.8em;"><b>Filters</b><br>
<?php foreach ($fparams as $k => $v): ?> <? foreach ($fparams as $k => $v): ?>
<?= $this->qlink( <?= $this->qlink(
'x', 'x',
'monitoring/list/services', 'monitoring/list/services',
@ -74,9 +78,9 @@ if (isset($_GET['dir'])) {
'style' => array('color' => 'red') 'style' => array('color' => 'red')
) )
) ?> <?= $this->escape("$k = $v") ?></br> ) ?> <?= $this->escape("$k = $v") ?></br>
<?php endforeach; ?> <? endforeach ?>
</div> </div>
<?php endif; ?> <? endif ?>
<form method="get" action="<?= $this->qUrl( <form method="get" action="<?= $this->qUrl(
'monitoring/list/services?' . http_build_query( 'monitoring/list/services?' . http_build_query(
$this->services->getAppliedFilter()->toParams() $this->services->getAppliedFilter()->toParams()
@ -93,11 +97,21 @@ Sort by <?= $this->formSelect(
array( array(
'severity' => 'Severity', 'severity' => 'Severity',
'service_last_state_change' => 'Last state change', 'service_last_state_change' => 'Last state change',
'service_last_time_unknown' => 'Last UNKNOWN',
'service_last_time_critical' => 'Last CRITICAL',
'service_last_time_warning' => 'Last WARNING',
'service_last_time_ok' => 'Last OK',
'host_name' => 'Host', 'host_name' => 'Host',
'service_description' => 'Service', 'service_description' => 'Service',
) )
) ?> ) ?>
<input type="text" placeholder="Add filter..." name="search" /> <?= $this->formText(
'search',
$this->search,
array(
'placeholder' => 'Add filllter...',
)
) ?>
</form> </form>
</div> </div>
<?php if (empty($services)): ?> <?php if (empty($services)): ?>
@ -106,11 +120,11 @@ Sort by <?= $this->formSelect(
Sorry, no services found for this search Sorry, no services found for this search
</div> </div>
<?php return; endif; ?> <?php return; endif ?>
<?= $this->paginationControl($services, null, null, array('preserve' => $this->preserve )); ?> <?= $this->paginationControl($paginator, null, null, array('preserve' => $this->preserve )); ?>
<table class="action"> <table class="action">
<tbody> <tbody>
<?php foreach ($services as $service): <?php foreach ($services->fetchAll() as $service):
list($host_col,$icons,$state_classes,$state_title,$graph) = getRowProperties($service,$last_host,$this); ?> list($host_col,$icons,$state_classes,$state_title,$graph) = getRowProperties($service,$last_host,$this); ?>
<tr class="<?= implode(' ', $state_classes) ?>"> <tr class="<?= implode(' ', $state_classes) ?>">
<td class="state" title="<?= $state_title ?>"> <td class="state" title="<?= $state_title ?>">
@ -148,6 +162,9 @@ Sort by <?= $this->formSelect(
</span> </span>
<?= $graph ?> <?= $graph ?>
</td> </td>
<? foreach ($this->extraColumns as $col): ?>
<td><?= $this->escape($service->$col) ?></td>
<? endforeach ?>
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
</tbody> </tbody>

View File

@ -0,0 +1,29 @@
<?= $this->expandable(
$this->translate('Check statistics'),
'<table style="width:100%">
<thead>
</thead>
<tbody>
<tr>
<td style="width: 25em">' . $this->translate('Last check') . '</td>
<td>' . $this->format()->timeSince($object->last_check) . '</td>
</tr>
<tr>
<td>' . $this->translate('Next check') . '</td>
<td class="icinga-uiwidget-countdown" countdown-end="' . $object->next_check . '">'
. $this->format()->timeUntil($object->next_check)
. '</td>
</tr>
<tr>
<td>' . $this->translate('Check execution time') . '</td>
<td>' . $object->check_execution_time . '</td>
</tr>
<tr>
<td>' . $this->translate('Check latency') . '</td>
<td>' . $object->check_latency . '</td>
</tr>
</tbody>
</table>
',
array('collapsed' => false)
);

View File

@ -0,0 +1,6 @@
<?= $this->expandable(
'<b>Command:</b> ' . array_shift(preg_split('|!|', $object->check_command)),
$this->commandArguments($object->check_command),
// TODO: no JS handler right now, should be collapsed
array('collapsed' => ! (bool) strpos($object->check_command, '!'))
) ?>

View File

@ -1,12 +1,13 @@
<?php
if (empty($object->comments)) return;
<?php if (! empty($this->comments)) { ?>
<?
$list = array(); $list = array();
foreach ($this->comments as $comment) { foreach ($object->comments as $comment) {
if ($this->ticket_pattern) { if ($ticket_pattern) {
$text = preg_replace( $text = preg_replace(
$this->ticket_pattern, $ticket_pattern,
$this->ticket_link, $ticket_link,
$this->escape($comment->comment_data) $this->escape($comment->comment_data)
); );
} else { } else {
@ -29,4 +30,3 @@ foreach ($this->comments as $comment) {
<blockquote> <?= implode('<br />', $list) ?></blockquote> <blockquote> <?= implode('<br />', $list) ?></blockquote>
</div> </div>
<?php } ?>

View File

@ -0,0 +1,28 @@
<? $showService = $object->service_description && $tabs->getActiveName() !== 'host' ?>
<? if (! $compact): ?>
<?= $tabs ?>
<? endif ?>
<table style="margin-bottom: 1em">
<tr class="<?= $this->monitoringState($object, 'host') ?><?= $object->host_handled ? ' handled' : '' ?>">
<td><b><?= $this->escape($object->host_name) ?><?
if ($object->host_address && $object->host_address !== $object->host_name): ?>
(<?= $this->escape($object->host_address) ?>)
<? endif ?>
</b></td>
<td class="state"<?= $showService ? '' : ' rowspan="2"' ?>>
<?= $this->util()->getHostStateName($object->host_state) ?><br />
since <?= $this->timeSince($object->host_last_state_change) ?>
</td>
</tr>
<? if ($showService): ?>
<tr class="<?= $this->monitoringState($object, 'service') ?><?= $object->service_handled ? ' handled' : '' ?>">
<td><b>Service: <?= $this->escape($object->service_description) ?></b></td>
<td class="state">
<?= $this->util()->getServiceStateName($object->service_state); ?><br />
since <?= $this->timeSince($object->service_last_state_change) ?>
</td>
</tr>
<? else: ?>
<tr><td><b>Host state</b></td></tr>
<? endif ?>
</table>

View File

@ -0,0 +1,12 @@
<?php
if (empty($object->hostgroups)) return;
$list = array();
foreach ($object->hostgroups as $name => $alias) {
$list[] = $this->qlink($alias, 'monitoring/list/services', array(
'hostgroups' => $name
));
}
echo '<b>Hostgroups:</b> ' . implode(', ', $list) . "<br />\n";

View File

@ -0,0 +1,4 @@
<?= $this->expandable(
$this->pluginOutput($object->output),
$this->pluginOutput($object->long_output)
) ?>

View File

@ -0,0 +1,7 @@
<? if ($object->perfdata): ?>
<?= $this->expandable(
'<b>Performance data</b>',
$this->perfdata($object->perfdata),
array('collapsed' => false)
) ?>
<? endif ?>

View File

@ -0,0 +1,12 @@
<?php
if (empty($object->servicegroups)) return;
$list = array();
foreach ($object->servicegroups as $name => $alias) {
$list[] = $this->qlink($alias, 'monitoring/list/services', array(
'servicegroups' => $name
));
}
echo '<b>Servicegroups:</b> ' . implode(', ', $list) . "<br />\n";

View File

@ -1,20 +1,17 @@
<?= $this->partial('show/header.phtml', array( <?= $this->render('show/components/header.phtml') ?>
'host' => $this->host, <?php
'service' => $this->service, $history->limit(10);
'tabs' => $this->tabs $hhistory = $this->history->paginate();
)); ?><?php
$this->history->limit(10);
$history = $this->history->paginate();
?> ?>
<?php if (empty($history)): ?> <?php if (empty($hhistory)): ?>
There are no matching history entries right now There are no matching history entries right now
<?php else: ?> <?php else: ?>
<?= $this->paginationControl($history, null, null, array('preserve' => $this->preserve)); ?> <?= $this->paginationControl($hhistory, null, null, array('preserve' => $preserve)); ?>
<table class="paginatable"> <table class="paginatable">
<tbody> <tbody>
<?php foreach ($history as $event): ?> <?php foreach ($hhistory as $event): ?>
<?php <?php
if ($event->object_type == 'host') { if ($event->object_type == 'host') {
@ -27,24 +24,24 @@ $row_class = array_key_exists($event->state, $states) ? $states[$event->state] :
?> ?>
<tr class="<?= $row_class ?>"><td class="state"><?= date('d.m. H:i', $event->timestamp ) ?></td> <tr class="<?= $row_class ?>"><td class="state"><?= date('d.m. H:i', $event->timestamp ) ?></td>
<?php if (! isset($this->host)): ?> <? if (! $object): ?>
<td><?= $this->escape($event->host_name) ?></td> <td><?= $this->escape($event->host_name) ?></td>
<?php endif; ?> <? endif ?>
<?php if (! isset($this->service)): ?> <? if (! $object instanceof Icinga\Monitoring\Object\Service): ?>
<td><?php if (isset($this->host)): ?> <td><? if ($object): ?>
<?= $this->qlink( <?= $this->qlink(
$event->service_description, $event->service_description,
'monitoring/show/service', 'monitoring/show/service',
array( array(
'host' => $this->host->host_name, 'host' => $object->host_name,
'service' => $event->service_description 'service' => $event->service_description
) )
) ?> ) ?>
<?php else: ?> <? else: ?>
<?= $this->escape($event->service_description) ?> <?= $this->escape($event->service_description) ?>
<?php endif; ?> <? endif ?>
</td> </td>
<?php endif; ?> <? endif ?>
<td style="padding: 0.2em;"><?php <td style="padding: 0.2em;"><?php
$imgparams = array( $imgparams = array(
@ -83,17 +80,17 @@ switch ($event->type) {
break; break;
} }
?> ?>
<?php if ($event->attempt !== null): ?> <? if ($event->attempt !== null): ?>
[ <?= $event->attempt ?>/<?=$event->max_attempts ?> ] [ <?= $event->attempt ?>/<?=$event->max_attempts ?> ]
<?php endif; ?> <? endif ?>
<?= $this->ticket_pattern ? preg_replace( <?= $ticket_pattern ? preg_replace(
$this->ticket_pattern, $ticket_pattern,
$this->ticket_link, $ticket_link,
$this->pluginOutput($event->output) $this->pluginOutput($event->output)
) : $this->pluginOutput($event->output) ?> ) : $this->pluginOutput($event->output) ?>
</td> </td>
</tr> </tr>
<?php endforeach; ?> <? endforeach ?>
</tbody> </tbody>
</table> </table>
<?php endif; ?> <? endif ?>

View File

@ -1,31 +1,10 @@
<?= $this->partial('show/header.phtml', array( <?= $this->render('show/components/header.phtml') ?>
'host' => $this->host, <?= $this->render('show/components/output.phtml') ?>
'service' => $this->service, <?= $this->render('show/components/servicegroups.phtml') ?>
'tabs' => $this->tabs,
'compact' => $this->compact
)); ?>
<?= $this->expandable(
$this->pluginOutput($this->service->service_output),
$this->pluginOutput($this->service->service_long_output)
) ?>
<?= $this->render('show/components/contacts.phtml') ?> <?= $this->render('show/components/contacts.phtml') ?>
<?= $this->render('show/components/comments.phtml') ?> <?= $this->render('show/components/comments.phtml') ?>
<?= $this->render('show/components/customvars.phtml') ?>
<?php foreach ($this->customvars as $name => $value): ?> <?= $this->render('show/components/perfdata.phtml') ?>
<b><?= $this->escape($name) ?>:</b> <?= $this->escape($value) ?><br /> <?= $this->render('show/components/checkstatistics.phtml') ?>
<?php endforeach; ?> <?= $this->render('show/components/command.phtml') ?>
<?= $this->expandable(
'<b>Command:</b> ' . array_shift(preg_split('|!|', $this->service->service_check_command)),
$this->commandArguments($this->service->service_check_command)
) ?>
<?php if ($this->service->service_perfdata): ?>
<?= $this->expandable(
'<b>Performance data</b>:',
$this->perfdata($this->service->service_perfdata),
array('collapsed' => false)
) ?>
<?php endif; ?>
<?= $this->partial('show/legacy-service.phtml', array('service' => $this->service)) ?>
<?= $this->preview_image ?> <?= $this->preview_image ?>

View File

@ -1 +1,2 @@
<?= $this->services ?> <?= $this->render('show/components/header.phtml') ?>
<?= $services ?>

View File

@ -1,2 +1,2 @@
<?= $this->tabs ?> <?= $tabs ?>

View File

@ -1,17 +1,17 @@
<?php if (! $this->compact): ?> <? if (! $this->compact): ?>
<?= $this->tabs ?> <?= $this->tabs ?>
<?php endif; ?> <? endif ?>
<?php if (empty($this->summary)): ?> <? if (empty($this->summary)): ?>
There are no such services right now There are no such services right now
<?php else: ?> <? else: ?>
<?php <?php
$now = time(); $now = time();
?> ?>
<?php if (! $this->compact && $this->summary instanceof \Zend_Paginator): ?> <? if (! $this->compact && $this->summary instanceof \Zend_Paginator): ?>
<?= $this->paginationControl($this->summary, null, null, array('preserve' => $this->preserve)); ?> <?= $this->paginationControl($this->summary, null, null, array('preserve' => $this->preserve)); ?>
<?php endif; ?> <? endif ?>
<table class="pivot action"> <table class="pivot action">
<thead> <thead>
<tr> <tr>
@ -20,11 +20,11 @@ $now = time();
<th style="width: 5em;">Critical</th> <th style="width: 5em;">Critical</th>
<th style="width: 5em;">Unknown</th> <th style="width: 5em;">Unknown</th>
<th style="width: 5em;">Warning</th> <th style="width: 5em;">Warning</th>
<?php if (! $this->compact): ?> <th style="width: 9%;">OK</th><?php endif; ?> <? if (! $this->compact): ?> <th style="width: 9%;">OK</th><? endif ?>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php foreach ($this->summary as $row): ?> <? foreach ($this->summary as $row): ?>
<?php <?php
$class_ok = ''; $class_ok = '';
@ -149,19 +149,19 @@ if (isset($row->hostgroup_name)) {
} }
?> ?>
<tr> <tr>
<!-- <td class="<?= $name_class ?>"><?php if ($row->last_state_change + 600 > $now): ?> <!-- <td class="<?= $name_class ?>"><? if ($row->last_state_change + 600 > $now): ?>
<blink><?= $this->timeSince($row->last_state_change) ?></blink> <blink><?= $this->timeSince($row->last_state_change) ?></blink>
<?php else: ?> <? else: ?>
<?= $this->timeSince($row->last_state_change) ?> <?= $this->timeSince($row->last_state_change) ?>
<?php endif; ?></td>--> <? endif ?></td>-->
<td style="text-align: left;"><?= $name_html ?></td> <td style="text-align: left;"><?= $name_html ?></td>
<td class="<?= $class_critical ?>"><?= $html_critical ?></td> <td class="<?= $class_critical ?>"><?= $html_critical ?></td>
<td class="<?= $class_unknown ?>"><?= $html_unknown ?></td> <td class="<?= $class_unknown ?>"><?= $html_unknown ?></td>
<td class="<?= $class_warning ?>"><?= $html_warning ?></td> <td class="<?= $class_warning ?>"><?= $html_warning ?></td>
<?php if (! $this->compact): ?><td class="ok"><?= $html_ok ?></td><?php endif; ?> <? if (! $this->compact): ?><td class="ok"><?= $html_ok ?></td><? endif ?>
</tr> </tr>
<?php endforeach; ?> <? endforeach ?>
</tbody> </tbody>
</table> </table>
<?php endif; ?> <? endif ?>
<?php if ($this->compact): ?><a href="<?= $this->baseUrl('monitoring/summary/group') ?>">more</a><?php endif; ?> <? if ($this->compact): ?><a href="<?= $this->baseUrl('monitoring/summary/group') ?>">more</a><? endif ?>

View File

@ -0,0 +1,126 @@
<?php
use Icinga\Monitoring\Backend;
use Icinga\Util\Format;
$backend = Backend::getInstance($params->shift('backend'));
$query = $backend->select()->from('status', array(
'host_name',
'host_state',
'host_output',
'host_acknowledged',
'host_in_downtime',
'service_description',
'service_state',
'service_acknowledged',
'service_in_downtime',
'service_handled',
'service_output',
'service_last_state_change'
))->order('service_last_state_change ASC');
$endless = $params->shift('endless');
$query->applyFilters($params->getParams());
$host_colors = array(
0 => '2', // UP
1 => '1', // DOWN
2 => '3', // UNREACH (brown)
99 => '0', // PEND
);
$host_states = array(
0 => 'UP', // UP
1 => 'DOWN', // DOWN
2 => 'UNREACHABLE', // UNREACH (brown)
99 => 'PENDING', // PEND
);
$service_colors = array(
0 => '2', // OK
1 => '3', // WARN
2 => '1', // CRIT
3 => '5', // UNKN
99 => '0', // PEND
);
$service_states = array(
0 => 'OK', // OK
1 => 'WARNING', // WARN
2 => 'CRITICAL', // CRIT
3 => 'UNKNOWN', // UNKN
99 => 'PENDING', // PEND
);
$finished = false;
while (! $finished) {
$out = '';
$last_host = null;
foreach ($query->fetchAll() as $key => $row) {
$host_extra = array();
if ($row->host_in_downtime) {
$host_extra[] = 'DOWNTIME';
}
if ($row->host_acknowledged) {
$host_extra[] = 'ACK';
}
if (empty($host_extra)) {
$host_extra = '';
} else {
$host_extra = " \033[34;1m[" . implode(',', $host_extra) . "]\033[0m";
}
$service_extra = array();
if ($row->service_in_downtime) {
$service_extra[] = 'DOWNTIME';
}
if ($row->service_acknowledged) {
$service_extra[] = 'ACK';
}
if (empty($service_extra)) {
$service_extra = '';
} else {
$service_extra = " \033[34;52;1m[" . implode(',', $service_extra) . "]\033[0m";
}
if ($row->host_name !== $last_host) {
$out .= sprintf(
"\n\033[01;37;4%dm %-5s \033[0m \033[30;1m%s\033[0m%s: %s\n",
$host_colors[$row->host_state],
substr($host_states[$row->host_state], 0, 5),
$row->host_name,
$host_extra,
$row->host_output
);
}
$last_host = $row->host_name;
$out .= sprintf(
"\033[01;37;4%dm \033[01;37;4%dm %4s \033[0m %s%s since %s: %s\n",
$host_colors[$row->host_state],
$service_colors[$row->service_state],
substr($service_states[$row->service_state] . ' ', 0, 4),
$row->service_description,
$service_extra,
Format::timeSince($row->service_last_state_change),
preg_replace('/\n/', sprintf(
"\n\033[01;37;4%dm \033[01;37;4%dm \033[0m ",
$host_colors[$row->host_state],
$service_colors[$row->service_state]
), substr(wordwrap(str_repeat(' ', 30) . preg_replace('~\@{3,}~', '@@@', $row->service_output), 72), 30))
);
}
$out .= "\n";
if ($endless) {
echo "\033[2J\033[1;1H\033[1S" . $out;
sleep(3);
} else {
echo $out;
$finished = true;
}
}

View File

@ -0,0 +1,21 @@
<?php
$client = new SoapClient('http://itenos-devel.tom.local/monitoring/soap?wsdl', array(
'login' => 'icingaadmin',
'password' => 'tomtom',
'trace' => true,
'exceptions' => true,
'cache_wsdl' => WSDL_CACHE_NONE,
'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
// 'authentication' => SOAP_AUTHENTICATION_BASIC
));
// print_r($client->__getFunctions());
try {
print_r($client->problems());
} catch (Exception $e) {
echo $e->getMessage() . "\n\n";
echo $client->__getLastRequest() . "\n\n";
echo $client->__getLastResponse() . "\n\n";
}

View File

@ -101,6 +101,7 @@ class AbstractBackend implements DatasourceInterface
'host_in_downtime', 'host_in_downtime',
'host_acknowledged', 'host_acknowledged',
'host_check_command', 'host_check_command',
'host_check_command',
'host_last_state_change', 'host_last_state_change',
'host_alias', 'host_alias',
'host_output', 'host_output',
@ -138,8 +139,8 @@ class AbstractBackend implements DatasourceInterface
'host_flap_detection_enabled', 'host_flap_detection_enabled',
'host_is_flapping', 'host_is_flapping',
'host_percent_state_change', 'host_percent_state_change',
'host_latency', 'host_check_latency',
'host_execution_time', 'host_check_execution_time',
'host_scheduled_downtime_depth', 'host_scheduled_downtime_depth',
'host_failure_prediction_enabled', 'host_failure_prediction_enabled',
'host_process_performance_data', 'host_process_performance_data',

View File

@ -19,6 +19,8 @@ abstract class AbstractQuery extends Query
protected $joinedVirtualTables = array(); protected $joinedVirtualTables = array();
protected $object_id = 'object_id'; protected $object_id = 'object_id';
protected $host_id = 'host_id';
protected $service_id = 'service_id';
protected $hostgroup_id = 'hostgroup_id'; protected $hostgroup_id = 'hostgroup_id';
protected $servicegroup_id = 'servicegroup_id'; protected $servicegroup_id = 'servicegroup_id';
@ -31,10 +33,18 @@ abstract class AbstractQuery extends Query
$this->prefix = $this->ds->getPrefix(); $this->prefix = $this->ds->getPrefix();
if ($this->ds->getConnection()->getDbType() === 'oracle') { if ($this->ds->getConnection()->getDbType() === 'oracle') {
$this->object_id = $this->hostgroup_id = $this->servicegroup_id = 'id'; $this->object_id = $this->host_id = $this->service_id = $this->hostgroup_id = $this->servicegroup_id = 'id'; // REALLY?
foreach ($this->columnMap as $table => & $columns) { foreach ($this->columnMap as $table => & $columns) {
foreach ($column as $key => & $value) { foreach ($columns as $key => & $value) {
$value = preg_replace('/UNIX_TIMESTAMP/', 'localts2unixts', $value); $value = preg_replace('/UNIX_TIMESTAMP/', 'localts2unixts', $value);
$value = preg_replace('/ COLLATE .+$/', '', $value);
}
}
}
if ($this->ds->getConnection()->getDbType() === 'pgsql') {
foreach ($this->columnMap as $table => & $columns) {
foreach ($columns as $key => & $value) {
$value = preg_replace('/ COLLATE .+$/', '', $value);
} }
} }
} }
@ -58,7 +68,11 @@ abstract class AbstractQuery extends Query
// TODO: extend if we allow queries with only hosts / only services // TODO: extend if we allow queries with only hosts / only services
// ($leftcol s.host_object_id vs h.host_object_id // ($leftcol s.host_object_id vs h.host_object_id
if ($this->hasJoinedVirtualTable('services')) {
$leftcol = 's.' . $type . '_object_id'; $leftcol = 's.' . $type . '_object_id';
} else {
$leftcol = 'h.' . $type . '_object_id';
}
$joinOn = $leftcol $joinOn = $leftcol
. ' = ' . ' = '
. $alias . $alias
@ -100,7 +114,7 @@ abstract class AbstractQuery extends Query
protected function beforeCreatingSelectQuery() protected function beforeCreatingSelectQuery()
{ {
$this->setRealColumns(); $this->setRealColumns();
Benchmark::measure(sprintf('%s is going to SELECT', get_class($this))); Benchmark::measure(sprintf('%s ready to run', array_pop(explode('\\', get_class($this)))));
} }
protected function applyAllFilters() protected function applyAllFilters()
@ -274,6 +288,10 @@ abstract class AbstractQuery extends Query
// Go through all given values // Go through all given values
foreach ($value as $val) { foreach ($value as $val) {
if ($val === '') {
// TODO: REALLY??
continue;
}
// Value starting with minus: negation // Value starting with minus: negation
if ($val[0] === '-') { if ($val[0] === '-') {
$val = substr($val, 1); $val = substr($val, 1);

View File

@ -8,15 +8,16 @@ class CommentQuery extends AbstractQuery
'comments' => array( 'comments' => array(
'comment_data' => 'cm.comment_data', 'comment_data' => 'cm.comment_data',
'comment_author' => 'cm.author_name', 'comment_author' => 'cm.author_name',
'comment_timestamp' => 'UNIX_TIMESTAMP(cm.entry_time)', //'comment_timestamp' => 'UNIX_TIMESTAMP(cm.entry_time)',
'comment_timestamp' => 'UNIX_TIMESTAMP(cm.comment_time)',
'comment_type' => "CASE cm.entry_type WHEN 1 THEN 'comment' WHEN 2 THEN 'downtime' WHEN 3 THEN 'flapping' WHEN 4 THEN 'ack' END", 'comment_type' => "CASE cm.entry_type WHEN 1 THEN 'comment' WHEN 2 THEN 'downtime' WHEN 3 THEN 'flapping' WHEN 4 THEN 'ack' END",
), ),
'hosts' => array( 'hosts' => array(
'host_name' => 'ho.name1', 'host_name' => 'ho.name1',
), ),
'services' => array( 'services' => array(
'service_host_name' => 'so.name1', 'service_host_name' => 'so.name1 COLLATE latin1_general_ci',
'service_description' => 'so.name2', 'service_description' => 'so.name2 COLLATE latin1_general_ci',
) )
); );

View File

@ -6,17 +6,17 @@ class ContactQuery extends AbstractQuery
{ {
protected $columnMap = array( protected $columnMap = array(
'contacts' => array( 'contacts' => array(
'contact_name' => 'co.name1', 'contact_name' => 'co.name1 COLLATE latin1_general_ci',
'contact_alias' => 'c.alias', 'contact_alias' => 'c.alias',
'contact_email' => 'c.email_address', 'contact_email' => 'c.email_address',
'contact_pager' => 'c.pager_address', 'contact_pager' => 'c.pager_address',
), ),
'hosts' => array( 'hosts' => array(
'host_name' => 'ho.name1', 'host_name' => 'ho.name1 COLLATE latin1_general_ci',
), ),
'services' => array( 'services' => array(
'service_host_name' => 'so.name1', 'service_host_name' => 'so.name1 COLLATE latin1_general_ci',
'service_description' => 'so.name2', 'service_description' => 'so.name2 COLLATE latin1_general_ci',
) )
); );

View File

@ -6,18 +6,18 @@ class ContactgroupQuery extends AbstractQuery
{ {
protected $columnMap = array( protected $columnMap = array(
'contactgroups' => array( 'contactgroups' => array(
'contactgroup_name' => 'cgo.name1', 'contactgroup_name' => 'cgo.name1 COLLATE latin1_general_ci',
'contactgroup_alias' => 'cg.alias', 'contactgroup_alias' => 'cg.alias',
), ),
'contacts' => array( 'contacts' => array(
'contact_name' => 'co.name1', 'contact_name' => 'co.name1 COLLATE latin1_general_ci',
), ),
'hosts' => array( 'hosts' => array(
'host_name' => 'ho.name1', 'host_name' => 'ho.name1',
), ),
'services' => array( 'services' => array(
'service_host_name' => 'so.name1', 'service_host_name' => 'so.name1 COLLATE latin1_general_ci',
'service_description' => 'so.name2', 'service_description' => 'so.name2 COLLATE latin1_general_ci',
) )
); );

View File

@ -12,9 +12,10 @@ class CustomvarQuery extends AbstractQuery
'varvalue' => 'cvs.varvalue', 'varvalue' => 'cvs.varvalue',
), ),
'objects' => array( 'objects' => array(
'host_name' => 'cvo.name1', 'host_name' => 'cvo.name1 COLLATE latin1_general_ci',
'service_description' => 'cvo.name2', 'service_host_name' => 'cvo.name1 COLLATE latin1_general_ci',
'contact_name' => 'cvo.name1', 'service_description' => 'cvo.name2 COLLATE latin1_general_ci',
'contact_name' => 'cvo.name1 COLLATE latin1_general_ci',
'object_type' => "CASE cvo.objecttype_id WHEN 1 THEN 'host' WHEN 2 THEN 'service' WHEN 10 THEN 'contact' ELSE 'invalid' END" 'object_type' => "CASE cvo.objecttype_id WHEN 1 THEN 'host' WHEN 2 THEN 'service' WHEN 10 THEN 'contact' ELSE 'invalid' END"
// 'object_type' => "CASE cvo.objecttype_id WHEN 1 THEN 'host' WHEN 2 THEN 'service' WHEN 3 THEN 'hostgroup' WHEN 4 THEN 'servicegroup' WHEN 5 THEN 'hostescalation' WHEN 6 THEN 'serviceescalation' WHEN 7 THEN 'hostdependency' WHEN 8 THEN 'servicedependency' WHEN 9 THEN 'timeperiod' WHEN 10 THEN 'contact' WHEN 11 THEN 'contactgroup' WHEN 12 THEN 'command' ELSE 'other' END" // 'object_type' => "CASE cvo.objecttype_id WHEN 1 THEN 'host' WHEN 2 THEN 'service' WHEN 3 THEN 'hostgroup' WHEN 4 THEN 'servicegroup' WHEN 5 THEN 'hostescalation' WHEN 6 THEN 'serviceescalation' WHEN 7 THEN 'hostdependency' WHEN 8 THEN 'servicedependency' WHEN 9 THEN 'timeperiod' WHEN 10 THEN 'contact' WHEN 11 THEN 'contactgroup' WHEN 12 THEN 'command' ELSE 'other' END"
), ),

View File

@ -10,10 +10,10 @@ class EventHistoryQuery extends AbstractQuery
protected $columnMap = array( protected $columnMap = array(
'eventhistory' => array( 'eventhistory' => array(
'host' => 'eho.name1', 'host' => 'eho.name1 COLLATE latin1_general_ci',
'service' => 'eho.name2', 'service' => 'eho.name2 COLLATE latin1_general_ci',
'host_name' => 'eho.name1', 'host_name' => 'eho.name1 COLLATE latin1_general_ci',
'service_description' => 'eho.name2', 'service_description' => 'eho.name2 COLLATE latin1_general_ci',
'object_type' => "CASE WHEN eho.objecttype_id = 1 THEN 'host' ELSE 'service' END", 'object_type' => "CASE WHEN eho.objecttype_id = 1 THEN 'host' ELSE 'service' END",
'timestamp' => 'UNIX_TIMESTAMP(eh.state_time)', 'timestamp' => 'UNIX_TIMESTAMP(eh.state_time)',
'raw_timestamp' => 'eh.state_time', 'raw_timestamp' => 'eh.state_time',
@ -69,7 +69,8 @@ class EventHistoryQuery extends AbstractQuery
'type' => "('dt_start')", 'type' => "('dt_start')",
'state' => '(NULL)', 'state' => '(NULL)',
'state_type' => '(NULL)', 'state_type' => '(NULL)',
'output' => "CONCAT('[', author_name, '] ', comment_data)", // 'output' => "CONCAT('[', author_name, '] ', comment_data)",
'output' => "('[' || author_name || '] ' || comment_data)",
'attempt' => '(NULL)', 'attempt' => '(NULL)',
'max_attempts' => '(NULL)', 'max_attempts' => '(NULL)',
) )
@ -90,7 +91,8 @@ class EventHistoryQuery extends AbstractQuery
'type' => "('dt_end')", 'type' => "('dt_end')",
'state' => '(NULL)', 'state' => '(NULL)',
'state_type' => '(NULL)', 'state_type' => '(NULL)',
'output' => "CONCAT('[', author_name, '] ', comment_data)", // 'output' => "CONCAT('[', author_name, '] ', comment_data)",
'output' => "('[' || author_name || '] ' || comment_data)",
'attempt' => '(NULL)', 'attempt' => '(NULL)',
'max_attempts' => '(NULL)', 'max_attempts' => '(NULL)',
) )
@ -110,7 +112,8 @@ class EventHistoryQuery extends AbstractQuery
'type' => "(CASE entry_type WHEN 1 THEN 'comment' WHEN 2 THEN 'dt_comment' WHEN 3 THEN 'flapping' WHEN 4 THEN 'ack' END)", 'type' => "(CASE entry_type WHEN 1 THEN 'comment' WHEN 2 THEN 'dt_comment' WHEN 3 THEN 'flapping' WHEN 4 THEN 'ack' END)",
'state' => '(NULL)', 'state' => '(NULL)',
'state_type' => '(NULL)', 'state_type' => '(NULL)',
'output' => "CONCAT('[', author_name, '] ', comment_data)", // 'output' => "CONCAT('[', author_name, '] ', comment_data)",
'output' => "('[' || author_name || '] ' || comment_data)",
'attempt' => '(NULL)', 'attempt' => '(NULL)',
'max_attempts' => '(NULL)', 'max_attempts' => '(NULL)',
) )
@ -123,12 +126,37 @@ class EventHistoryQuery extends AbstractQuery
$comments->where('comment_time <= ?', $end); $comments->where('comment_time <= ?', $end);
} }
// This is one of the db-specific workarounds that could be abstracted
// in a better way:
switch ($this->ds->getConnection()->getDbType()) {
case 'mysql':
$concat_contacts = "GROUP_CONCAT(c.alias ORDER BY c.alias SEPARATOR ', ')";
break;
case 'pgsql':
// TODO: Find a way to "ORDER" these:
$concat_contacts = "ARRAY_TO_STRING(ARRAY_AGG(c.alias), ', ')";
break;
case 'oracle':
// TODO: This is only valid for Oracle >= 11g Release 2.
$concat_contacts = "LISTAGG(c.alias, ', ') WITHIN GROUP (ORDER BY c.alias)";
// Alternatives:
//
// RTRIM(XMLAGG(XMLELEMENT(e, column_name, ',').EXTRACT('//text()')),
//
// not supported and not documented but works since 10.1,
// however it is NOT always present;
// WM_CONCAT(c.alias)
break;
default:
die('Not yet'); // TODO: Proper Exception
}
$cndetails = $this->db->select()->from( $cndetails = $this->db->select()->from(
array('cn' => $this->prefix . 'contactnotifications'), array('cn' => $this->prefix . 'contactnotifications'),
array( array(
'notification_id' => 'notification_id',
'cnt' => 'COUNT(*)', 'cnt' => 'COUNT(*)',
'contacts' => 'GROUP_CONCAT(c.alias)', 'contacts' => $concat_contacts
'notification_id' => 'notification_id'
) )
)->join( )->join(
array('c' => $this->prefix . 'contacts'), array('c' => $this->prefix . 'contacts'),
@ -144,7 +172,8 @@ class EventHistoryQuery extends AbstractQuery
'type' => "('notify')", 'type' => "('notify')",
'state' => 'state', 'state' => 'state',
'state_type' => '(NULL)', 'state_type' => '(NULL)',
'output' => "CONCAT('[', cndetails.contacts, '] ', n.output)", // 'output' => "CONCAT('[', cndetails.contacts, '] ', n.output)",
'output' => "('[' || cndetails.contacts || '] ' || n.output)",
'attempt' => '(NULL)', 'attempt' => '(NULL)',
'max_attempts' => '(NULL)', 'max_attempts' => '(NULL)',
) )

View File

@ -6,11 +6,12 @@ class HostgroupQuery extends AbstractQuery
{ {
protected $columnMap = array( protected $columnMap = array(
'hostgroups' => array( 'hostgroups' => array(
'hostgroup_name' => 'hgo.name1', 'hostgroup_name' => 'hgo.name1 COLLATE latin1_general_ci',
'hostgroup_alias' => 'hg.alias', 'hostgroup_alias' => 'hg.alias',
'id' => 'hg.hostgroup_id',
), ),
'hosts' => array( 'hosts' => array(
'host_name' => 'ho.name1' 'host_name' => 'ho.name1 COLLATE latin1_general_ci'
) )
); );

View File

@ -0,0 +1,197 @@
<?php
namespace Icinga\Monitoring\Backend\Ido\Query;
class HoststatusQuery extends AbstractQuery
{
protected $allowCustomVars = true;
protected $columnMap = array(
'hosts' => array(
'host' => 'ho.name1 COLLATE latin1_general_ci',
'host_name' => 'ho.name1 COLLATE latin1_general_ci',
'host_display_name' => 'h.display_name',
'host_alias' => 'h.alias',
'host_address' => 'h.address',
'host_ipv4' => 'INET_ATON(h.address)',
'host_icon_image' => 'h.icon_image',
),
'hoststatus' => array(
'host_state' => 'CASE WHEN hs.has_been_checked = 0 OR hs.has_been_checked IS NULL THEN 99 ELSE hs.current_state END',
'host_output' => 'hs.output',
'host_long_output' => 'hs.long_output',
'host_perfdata' => 'hs.perfdata',
'host_problem' => 'CASE WHEN hs.current_state = 0 THEN 0 ELSE 1 END',
'host_acknowledged' => 'hs.problem_has_been_acknowledged',
'host_in_downtime' => 'CASE WHEN (hs.scheduled_downtime_depth = 0) THEN 0 ELSE 1 END',
'host_handled' => 'CASE WHEN (hs.problem_has_been_acknowledged + hs.scheduled_downtime_depth) > 0 THEN 1 ELSE 0 END',
'host_does_active_checks' => 'hs.active_checks_enabled',
'host_accepts_passive_checks' => 'hs.passive_checks_enabled',
'host_last_state_change' => 'UNIX_TIMESTAMP(hs.last_state_change)',
'host_last_hard_state' => 'hs.last_hard_state',
'host_check_command' => 'hs.check_command',
'host_last_check' => 'UNIX_TIMESTAMP(hs.last_check)',
'host_next_check' => 'CASE WHEN hs.should_be_scheduled THEN UNIX_TIMESTAMP(hs.next_check) ELSE NULL END',
'host_check_execution_time' => 'hs.execution_time',
'host_check_latency' => 'hs.latency',
'host_notifications_enabled' => 'hs.notifications_enabled',
'host_last_time_up' => 'hs.last_time_up',
'host_last_time_down' => 'hs.last_time_down',
'host_last_time_unreachable' => 'hs.last_time_unreachable',
'host_severity' => 'CASE WHEN hs.current_state = 0
THEN
CASE WHEN hs.has_been_checked = 0 OR hs.has_been_checked IS NULL
THEN 16
ELSE 0
END
+
CASE WHEN hs.problem_has_been_acknowledged = 1
THEN 2
ELSE
CASE WHEN hs.scheduled_downtime_depth > 0
THEN 1
ELSE 4
END
END
ELSE
CASE WHEN hs.has_been_checked = 0 OR hs.has_been_checked IS NULL THEN 16
WHEN hs.current_state = 1 THEN 32
WHEN hs.current_state = 2 THEN 64
ELSE 256
END
+
CASE WHEN hs.problem_has_been_acknowledged = 1
THEN 2
ELSE
CASE WHEN hs.scheduled_downtime_depth > 0
THEN 1
ELSE 4
END
END
END',
),
'hostgroups' => array(
'hostgroups' => 'hgo.name1',
),
);
protected function getDefaultColumns()
{
return $this->columnMap['hosts']
+ $this->columnMap['hoststatus'];
}
protected function joinBaseTables()
{
// TODO: Shall we always add hostobject?
$this->baseQuery = $this->db->select()->from(
array('ho' => $this->prefix . 'objects'),
array()
)->join(
array('hs' => $this->prefix . 'hoststatus'),
'ho.' . $this->object_id . ' = hs.host_object_id AND ho.is_active = 1 AND ho.objecttype_id = 1',
array()
)->join(
array('h' => $this->prefix . 'hosts'),
'hs.host_object_id = h.host_object_id',
array()
);
$this->joinedVirtualTables = array(
'hosts' => true,
'hoststatus' => true,
);
}
protected function joinStatus()
{
$this->requireVirtualTable('services');
}
protected function joinServiceStatus()
{
$this->requireVirtualTable('services');
}
protected function joinServices()
{
$this->baseQuery->join(
array('s' => $this->prefix . 'services'),
's.host_object_id = h.host_object_id',
array()
)->join(
array('so' => $this->prefix . 'objects'),
"so.$this->object_id = s.service_object_id AND so.is_active = 1",
array()
)->joinLeft(
array('ss' => $this->prefix . 'servicestatus'),
"so.$this->object_id = ss.service_object_id",
array()
);
}
protected function joinHostgroups()
{
if ($this->hasJoinedVirtualTable('services')) {
return $this->joinServiceHostgroups();
} else {
return $this->joinHostHostgroups();
}
}
protected function joinHostHostgroups()
{
$this->baseQuery->join(
array('hgm' => $this->prefix . 'hostgroup_members'),
'hgm.host_object_id = h.host_object_id',
array()
)->join(
array('hg' => $this->prefix . 'hostgroups'),
"hgm.hostgroup_id = hg.$this->hostgroup_id",
array()
);
return $this;
}
protected function joinServiceHostgroups()
{
$this->baseQuery->join(
array('hgm' => $this->prefix . 'hostgroup_members'),
'hgm.host_object_id = s.host_object_id',
array()
)->join(
array('hg' => $this->prefix . 'hostgroups'),
'hgm.hostgroup_id = hg.' . $this->hostgroup_id,
array()
)->join(
array('hgo' => $this->prefix . 'objects'),
'hgo.' . $this->object_id. ' = hg.hostgroup_object_id'
. ' AND hgo.is_active = 1',
array()
);
return $this;
}
protected function joinServicegroups()
{
$this->requireVirtualTable('services');
$this->baseQuery->join(
array('sgm' => $this->prefix . 'servicegroup_members'),
'sgm.service_object_id = s.service_object_id',
array()
)->join(
array('sg' => $this->prefix . 'servicegroups'),
'sgm.servicegroup_id = sg.' . $this->servicegroup_id,
array()
)->join(
array('sgo' => $this->prefix . 'objects'),
'sgo.' . $this->object_id. ' = sg.servicegroup_object_id'
. ' AND sgo.is_active = 1',
array()
);
return $this;
}
}

View File

@ -6,12 +6,13 @@ class ServicegroupQuery extends AbstractQuery
{ {
protected $columnMap = array( protected $columnMap = array(
'servicegroups' => array( 'servicegroups' => array(
'servicegroup_name' => 'sgo.name1', 'servicegroup_name' => 'sgo.name1 COLLATE latin1_general_ci',
'servicegroup_alias' => 'sg.alias', 'servicegroup_alias' => 'sg.alias',
), ),
'services' => array( 'services' => array(
'host_name' => 'so.name1', 'host_name' => 'so.name1 COLLATE latin1_general_ci',
'service_description' => 'so.name2' 'service_host_name' => 'so.name1 COLLATE latin1_general_ci',
'service_description' => 'so.name2 COLLATE latin1_general_ci'
) )
); );
@ -34,11 +35,11 @@ class ServicegroupQuery extends AbstractQuery
{ {
$this->baseQuery->join( $this->baseQuery->join(
array('sgm' => $this->prefix . 'servicegroup_members'), array('sgm' => $this->prefix . 'servicegroup_members'),
'sgm.servicegroup_id = sg.servicegroup_id', 'sgm.' . $this->servicegroup_id . ' = sg.' . $this->servicegroup_id,
array() array()
)->join( )->join(
array('so' => $this->prefix . 'objects'), array('so' => $this->prefix . 'objects'),
'sgm.service_object_id = so.object_id AND so.is_active = 1', 'sgm.service_object_id = so.' . $this->object_id . ' AND so.is_active = 1',
array() array()
); );
} }

View File

@ -0,0 +1,167 @@
<?php
namespace Icinga\Monitoring\Backend\Ido\Query;
class ServicestatusQuery extends AbstractQuery
{
protected $allowCustomVars = true;
protected $columnMap = array(
'services' => array(
'service_host_name' => 'so.name1 COLLATE latin1_general_ci',
'service' => 'so.name2 COLLATE latin1_general_ci',
'service_description' => 'so.name2 COLLATE latin1_general_ci',
'service_display_name' => 's.display_name',
'service_icon_image' => 's.icon_image',
),
'servicestatus' => array(
'current_state' => 'CASE WHEN ss.has_been_checked = 0 OR ss.has_been_checked IS NULL THEN 99 ELSE ss.current_state END',
'service_state' => 'CASE WHEN ss.has_been_checked = 0 OR ss.has_been_checked IS NULL THEN 99 ELSE ss.current_state END',
'service_hard_state' => 'CASE WHEN ss.has_been_checked = 0 OR ss.has_been_checked IS NULL THEN 99 ELSE CASE WHEN ss.state_type = 1 THEN ss.current_state ELSE ss.last_hard_state END END',
'service_state_type' => 'ss.state_type',
'service_output' => 'ss.output',
'service_long_output' => 'ss.long_output',
'service_perfdata' => 'ss.perfdata',
'service_acknowledged' => 'ss.problem_has_been_acknowledged',
'service_in_downtime' => 'CASE WHEN (ss.scheduled_downtime_depth = 0) THEN 0 ELSE 1 END',
'service_handled' => 'CASE WHEN (ss.problem_has_been_acknowledged + ss.scheduled_downtime_depth + COALESCE(hs.current_state, 0)) > 0 THEN 1 ELSE 0 END',
'service_does_active_checks' => 'ss.active_checks_enabled',
'service_accepts_passive_checks' => 'ss.passive_checks_enabled',
'service_last_state_change' => 'UNIX_TIMESTAMP(ss.last_state_change)',
'service_last_hard_state' => 'ss.last_hard_state',
'service_last_hard_state_change' => 'UNIX_TIMESTAMP(ss.last_hard_state_change)',
'service_check_command' => 'ss.check_command',
'service_last_check' => 'UNIX_TIMESTAMP(ss.last_check)',
'service_next_check' => 'CASE WHEN ss.should_be_scheduled THEN UNIX_TIMESTAMP(ss.next_check) ELSE NULL END',
'service_check_execution_time' => 'ss.execution_time',
'service_check_latency' => 'ss.latency',
'service_notifications_enabled' => 'ss.notifications_enabled',
'service_last_time_ok' => 'ss.last_time_ok',
'service_last_time_warning' => 'ss.last_time_warning',
'service_last_time_critical' => 'ss.last_time_critical',
'service_last_time_unknown' => 'ss.last_time_unknown',
),
'servicegroups' => array(
'servicegroups' => 'sgo.name1',
),
);
protected function getDefaultColumns()
{
return $this->columnMap['services']
+ $this->columnMap['servicestatus'];
}
protected function joinBaseTables()
{
// TODO: Shall we always add hostobject?
$this->baseQuery = $this->db->select()->from(
array('so' => $this->prefix . 'objects'),
array()
)->join(
array('ss' => $this->prefix . 'servicestatus'),
'so.object_id = ss.service_object_id AND so.is_active = 1 AND so.objecttype_id = 2',
array()
)->join(
array('s' => $this->prefix . 'services'),
'ss.service_object_id = s.service_object_id',
array()
);
$this->joinedVirtualTables = array(
'services' => true,
'servicestatus' => true,
);
}
protected function joinStatus()
{
$this->requireVirtualTable('services');
}
protected function joinServiceStatus()
{
$this->requireVirtualTable('services');
}
protected function joinServices()
{
$this->baseQuery->join(
array('s' => $this->prefix . 'services'),
's.host_object_id = h.host_object_id',
array()
)->join(
array('so' => $this->prefix . 'objects'),
"so.$this->object_id = s.service_object_id AND so.is_active = 1",
array()
)->joinLeft(
array('ss' => $this->prefix . 'servicestatus'),
"so.$this->object_id = ss.service_object_id",
array()
);
}
protected function joinHostgroups()
{
if ($this->hasJoinedVirtualTable('services')) {
return $this->joinServiceHostgroups();
} else {
return $this->joinHostHostgroups();
}
}
protected function joinHostHostgroups()
{
$this->baseQuery->join(
array('hgm' => $this->prefix . 'hostgroup_members'),
'hgm.host_object_id = h.host_object_id',
array()
)->join(
array('hg' => $this->prefix . 'hostgroups'),
"hgm.hostgroup_id = hg.$this->hostgroup_id",
array()
);
return $this;
}
protected function joinServiceHostgroups()
{
$this->baseQuery->join(
array('hgm' => $this->prefix . 'hostgroup_members'),
'hgm.host_object_id = s.host_object_id',
array()
)->join(
array('hg' => $this->prefix . 'hostgroups'),
'hgm.hostgroup_id = hg.' . $this->hostgroup_id,
array()
)->join(
array('hgo' => $this->prefix . 'objects'),
'hgo.' . $this->object_id. ' = hg.hostgroup_object_id'
. ' AND hgo.is_active = 1',
array()
);
return $this;
}
protected function joinServicegroups()
{
$this->requireVirtualTable('services');
$this->baseQuery->join(
array('sgm' => $this->prefix . 'servicegroup_members'),
'sgm.service_object_id = s.service_object_id',
array()
)->join(
array('sg' => $this->prefix . 'servicegroups'),
'sgm.servicegroup_id = sg.' . $this->servicegroup_id,
array()
)->join(
array('sgo' => $this->prefix . 'objects'),
'sgo.' . $this->object_id. ' = sg.servicegroup_object_id'
. ' AND sgo.is_active = 1',
array()
);
return $this;
}
}

View File

@ -8,8 +8,8 @@ class StatusQuery extends AbstractQuery
protected $columnMap = array( protected $columnMap = array(
'hosts' => array( 'hosts' => array(
'host' => 'ho.name1', 'host' => 'ho.name1 COLLATE latin1_general_ci',
'host_name' => 'ho.name1', 'host_name' => 'ho.name1 COLLATE latin1_general_ci',
'host_display_name' => 'h.display_name', 'host_display_name' => 'h.display_name',
'host_alias' => 'h.alias', 'host_alias' => 'h.alias',
'host_address' => 'h.address', 'host_address' => 'h.address',
@ -53,8 +53,8 @@ class StatusQuery extends AbstractQuery
'host_flap_detection_enabled' => 'hs.flap_detection_enabled', 'host_flap_detection_enabled' => 'hs.flap_detection_enabled',
'host_is_flapping' => 'hs.is_flapping', 'host_is_flapping' => 'hs.is_flapping',
'host_percent_state_change' => 'hs.percent_state_change', 'host_percent_state_change' => 'hs.percent_state_change',
'host_latency' => 'hs.latency', 'host_check_latency' => 'hs.latency',
'host_execution_time' => 'hs.execution_time', 'host_check_execution_time' => 'hs.execution_time',
'host_scheduled_downtime_depth' => 'hs.scheduled_downtime_depth', 'host_scheduled_downtime_depth' => 'hs.scheduled_downtime_depth',
'host_failure_prediction_enabled' => 'hs.failure_prediction_enabled', 'host_failure_prediction_enabled' => 'hs.failure_prediction_enabled',
'host_process_performance_data' => 'hs.process_performance_data', 'host_process_performance_data' => 'hs.process_performance_data',
@ -102,10 +102,13 @@ class StatusQuery extends AbstractQuery
'hostgroups' => array( 'hostgroups' => array(
'hostgroups' => 'hgo.name1', 'hostgroups' => 'hgo.name1',
), ),
'servicegroups' => array(
'servicegroups' => 'sgo.name1',
),
'services' => array( 'services' => array(
'service_host_name' => 'so.name1', 'service_host_name' => 'so.name1 COLLATE latin1_general_ci',
'service' => 'so.name2', 'service' => 'so.name2 COLLATE latin1_general_ci',
'service_description' => 'so.name2', 'service_description' => 'so.name2 COLLATE latin1_general_ci',
'service_display_name' => 's.display_name', 'service_display_name' => 's.display_name',
'service_icon_image' => 's.icon_image', 'service_icon_image' => 's.icon_image',
), ),
@ -123,12 +126,18 @@ class StatusQuery extends AbstractQuery
'service_does_active_checks' => 'ss.active_checks_enabled', 'service_does_active_checks' => 'ss.active_checks_enabled',
'service_accepts_passive_checks' => 'ss.passive_checks_enabled', 'service_accepts_passive_checks' => 'ss.passive_checks_enabled',
'service_last_state_change' => 'UNIX_TIMESTAMP(ss.last_state_change)', 'service_last_state_change' => 'UNIX_TIMESTAMP(ss.last_state_change)',
'service_last_hard_state' => 'ss.last_hard_state',
'service_last_hard_state_change' => 'UNIX_TIMESTAMP(ss.last_hard_state_change)', 'service_last_hard_state_change' => 'UNIX_TIMESTAMP(ss.last_hard_state_change)',
'service_check_command' => 'ss.check_command', 'service_check_command' => 'ss.check_command',
'service_last_check' => 'UNIX_TIMESTAMP(ss.last_check)', 'service_last_check' => 'UNIX_TIMESTAMP(ss.last_check)',
'service_next_check' => 'CASE WHEN ss.should_be_scheduled THEN UNIX_TIMESTAMP(ss.next_check) ELSE NULL END', 'service_next_check' => 'CASE WHEN ss.should_be_scheduled = 1 THEN UNIX_TIMESTAMP(ss.next_check) ELSE NULL END',
'service_check_execution_time' => 'ss.execution_time', 'service_check_execution_time' => 'ss.execution_time',
'service_check_latency' => 'ss.latency', 'service_check_latency' => 'ss.latency',
'service_notifications_enabled' => 'ss.notifications_enabled',
'service_last_time_ok' => 'ss.last_time_ok',
'service_last_time_warning' => 'ss.last_time_warning',
'service_last_time_critical' => 'ss.last_time_critical',
'service_last_time_unknown' => 'ss.last_time_unknown',
), ),
'status' => array( 'status' => array(
'problems' => 'CASE WHEN ss.current_state = 0 THEN 0 ELSE 1 END', 'problems' => 'CASE WHEN ss.current_state = 0 THEN 0 ELSE 1 END',
@ -165,7 +174,7 @@ class StatusQuery extends AbstractQuery
END END
END END
END', END',
) ),
); );
public function group($col) public function group($col)
@ -191,7 +200,7 @@ class StatusQuery extends AbstractQuery
array() array()
)->join( )->join(
array('hs' => $this->prefix . 'hoststatus'), array('hs' => $this->prefix . 'hoststatus'),
'ho.object_id = hs.host_object_id AND ho.is_active = 1', 'ho.object_id = hs.host_object_id AND ho.is_active = 1 AND ho.objecttype_id = 1',
array() array()
)->join( )->join(
array('h' => $this->prefix . 'hosts'), array('h' => $this->prefix . 'hosts'),
@ -278,18 +287,20 @@ class StatusQuery extends AbstractQuery
protected function joinServicegroups() protected function joinServicegroups()
{ {
$this->requireVirtualTable('services');
$this->baseQuery->join( $this->baseQuery->join(
array('sgm' => $this->prefix . 'servicegroup_members'), array('sgm' => $this->prefix . 'servicegroup_members'),
'sgm.service_object_id = s.service_object_id', 'sgm.service_object_id = s.service_object_id',
array() array()
)->join( )->join(
array('sg' => $this->prefix . 'servicegroups'), array('sg' => $this->prefix . 'servicegroups'),
'sgm.servicegroup_id = sg.' . $this->servicegroup_id, 'sgm.servicegroup_id = sg.' . $this->servicegroup_id,
array() array()
)->join( )->join(
array('hgo' => $this->prefix . 'objects'), array('sgo' => $this->prefix . 'objects'),
'hgo.' . $this->object_id . ' = hg.' . $this->hostgroup_id 'sgo.' . $this->object_id. ' = sg.servicegroup_object_id'
. ' AND hgo.is_active = 1', . ' AND sgo.is_active = 1',
array() array()
); );

View File

@ -23,6 +23,10 @@ class StatusQuery extends AbstractQuery
'host_accepts_passive_checks' => 'host_accept_passive_checks', 'host_accepts_passive_checks' => 'host_accept_passive_checks',
'host_last_state_change', 'host_last_state_change',
'host_problems' => 'is_flapping',
'service_in_downtime' => 'is_flapping',
'service_handled' => 'is_flapping',
// Service config // Service config
'service_description' => 'description', 'service_description' => 'description',
'service_display_name' => 'display_name', 'service_display_name' => 'display_name',
@ -37,12 +41,34 @@ class StatusQuery extends AbstractQuery
'service_last_state_change' => 'last_state_change', 'service_last_state_change' => 'last_state_change',
// Service comments // Service comments
'comments_with_info', //'comments_with_info',
'downtimes_with_info', //'downtimes_with_info',
); );
public function init()
{
$this->query = $this->createQuery();
//print_r($this->ds->getConnection()->fetchAll($this->query));
//die('asdf');
}
public function count()
{
return $this->ds->getConnection()->count($this->query);
}
public function fetchAll()
{
return $this->ds->getConnection()->fetchAll($this->query);
}
public function fetchRow()
{
return array_shift($this->ds->getConnection()->fetchAll($this->query));
}
protected function createQuery() protected function createQuery()
{ {
return $this->connection->getConnection()->select()->from('services', $this->available_columns); return $this->ds->getConnection()->select()->from('services', $this->available_columns);
} }
} }

View File

@ -3,6 +3,8 @@
namespace Icinga\Monitoring; namespace Icinga\Monitoring;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Web\Session;
use Exception;
class Environment class Environment
{ {

View File

@ -0,0 +1,146 @@
<?php
namespace Icinga\Monitoring\Object;
use Icinga\Data\AbstractQuery as Query;
use Icinga\Monitoring\Backend\AbstractBackend;
abstract class AbstractObject
{
protected $backend;
protected $type;
protected $name1;
protected $name2;
protected $properties;
protected $foreign = array(
// 'hostgroups' => null,
// 'contacts' => null,
// 'contactgroups' => null,
// 'servicegroups' => null,
// 'customvars' => null,
// 'comments' => null,
);
public function __construct(AbstractBackend $backend, $name1, $name2 = null)
{
$this->backend = $backend;
$this->name1 = $name1;
$this->name2 = $name2;
$this->properties = (array) $this->fetchObject();
}
public static function fetch(AbstractBackend $backend, $name1, $name2 = null)
{
return new static($backend, $name1, $name2);
}
abstract protected function fetchObject();
public function __isset($key)
{
return $this->$key !== null;
}
public function __get($key)
{
if (isset($this->properties[$key])) {
return $this->properties[$key];
}
if (array_key_exists($key, $this->foreign)) {
if ($this->foreign[$key] === null) {
$func = 'fetch' . ucfirst($key);
if (! method_exists($this, $func)) {
return null;
}
$this->$func($key);
}
return $this->foreign[$key];
}
return null;
}
public function prefetch()
{
return $this;
}
abstract protected function applyObjectFilter(Query $query);
protected function fetchHostgroups()
{
$this->foreign['hostgroups'] = $this->applyObjectFilter(
$this->backend->select()->from('hostgroup', array(
'hostgroup_name',
'hostgroup_alias'
))
)->fetchPairs();
return $this;
}
protected function fetchServicegroups()
{
$this->foreign['servicegroups'] = $this->applyObjectFilter(
$this->backend->select()->from('servicegroup', array(
'servicegroup_name',
'servicegroup_alias'
))
)->fetchPairs();
return $this;
}
protected function fetchContacts()
{
$this->foreign['contacts'] = $this->applyObjectFilter(
$this->backend->select()->from('contact', array(
'contact_name',
'contact_alias',
'contact_email',
'contact_pager',
))
)->fetchAll();
return $this;
}
protected function fetchContactgroups()
{
$this->foreign['contactgroups'] = $this->applyObjectFilter(
$this->backend->select()->from('contactgroup', array(
'contactgroup_name',
'contactgroup_alias',
))
)->fetchAll();
return $this;
}
protected function fetchComments()
{
$this->foreign['comments'] = $this->applyObjectFilter(
$this->backend->select()->from('comment', array(
'comment_timestamp',
'comment_author',
'comment_data',
'comment_type',
))
)->fetchAll();
return $this;
}
protected function fetchCustomvars()
{
$this->foreign['customvars'] = $this->applyObjectFilter(
$this->backend->select()->from('customvar', array(
'varname',
'varvalue'
))
->where('varname', '-*PW*,-*PASS*,-*COMMUNITY*')
->where('object_type', 'host')
)->fetchPairs();
return $this;
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace Icinga\Monitoring\Object;
use Icinga\Data\AbstractQuery as Query;
class Host extends AbstractObject
{
protected $foreign = array(
'hostgroups' => null,
'contacts' => null,
'contactgroups' => null,
'customvars' => null,
'comments' => null,
);
public function stateName()
{
// TODO
}
protected function applyObjectFilter(Query $query)
{
return $query->where('host_name', $this->name1);
}
public function prefetch()
{
return $this->fetchHostgroups()
->fetchContacts()
->fetchContactgroups()
->fetchCustomvars()
->fetchComments();
}
protected function fetchObject()
{
return $this->backend->select()->from('status', array(
'host_name',
'host_alias',
'host_address',
'host_state',
'host_handled',
'host_in_downtime',
'host_acknowledged',
'host_last_state_change',
'last_check' => 'host_last_check',
'next_check' => 'host_next_check',
'check_execution_time' => 'host_check_execution_time',
'check_latency' => 'host_check_latency',
'output' => 'host_output',
'long_output' => 'host_long_output',
'check_command' => 'host_check_command',
'perfdata' => 'host_perfdata',
))->where('host_name', $this->name1)->fetchRow();
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace Icinga\Monitoring\Object;
use Icinga\Data\AbstractQuery as Query;
class Service extends AbstractObject
{
protected $foreign = array(
'servicegroups' => null,
'contacts' => null,
'contactgroups' => null,
'customvars' => null,
'comments' => null,
);
public function stateName()
{
// TODO
}
protected function applyObjectFilter(Query $query)
{
return $query->where('service_host_name', $this->name1)
->where('service_description', $this->name2);
}
public function prefetch()
{
return $this->fetchServicegroups()
->fetchContacts()
->fetchContactgroups()
->fetchCustomvars()
->fetchComments()
;
}
protected function fetchObject()
{
return $this->backend->select()->from('status', array(
'host_name',
'host_alias',
'host_address',
'host_state',
'host_handled',
'host_in_downtime',
'host_acknowledged',
'host_last_state_change',
'service_description',
'service_state',
'service_handled',
'service_acknowledged',
'service_in_downtime',
'service_last_state_change',
'last_check' => 'service_last_check',
'next_check' => 'service_next_check',
'check_execution_time' => 'service_check_execution_time',
'check_latency' => 'service_check_latency',
'output' => 'service_output',
'long_output' => 'service_long_output',
'check_command' => 'service_check_command',
'perfdata' => 'service_perfdata',
))
->where('host_name', $this->name1)
->where('service_description', $this->name2)
->fetchRow();
}
}

View File

@ -11,6 +11,9 @@ class CommentView extends MonitoringView
'comment_author', 'comment_author',
'comment_timestamp', 'comment_timestamp',
'comment_type', 'comment_type',
'host_name',
'service_host_name',
'service_description',
); );
protected $specialFilters = array(); protected $specialFilters = array();

View File

@ -0,0 +1,82 @@
<?php
namespace Icinga\Monitoring\View;
class HoststatusView extends MonitoringView
{
protected $query;
protected $searchColumn = 'host';
protected $availableColumns = array(
// Hosts
'host',
'host_name',
'host_display_name',
'host_alias',
'host_address',
'host_ipv4',
'host_icon_image',
// Hoststatus
'host_state',
'host_problem',
'host_severity',
'host_state_type',
'host_output',
'host_long_output',
'host_perfdata',
'host_acknowledged',
'host_in_downtime',
'host_handled',
'host_does_active_checks',
'host_accepts_passive_checks',
'host_last_state_change',
'host_last_hard_state',
'host_last_hard_state_change',
'host_notifications_enabled',
'host_last_time_up',
'host_last_time_down',
'host_last_time_unreachable',
);
protected $specialFilters = array(
'hostgroups',
'servicegroups'
);
protected $sortDefaults = array(
'host_name' => array(
'columns' => array(
'host_name',
),
'default_dir' => self::SORT_ASC
),
'host_address' => array(
'columns' => array(
'host_ipv4',
'service_description'
),
'default_dir' => self::SORT_ASC
),
'host_last_state_change' => array(
'default_dir' => self::SORT_DESC
),
'host_severity' => array(
'columns' => array(
'host_severity',
'host_last_state_change',
),
'default_dir' => self::SORT_DESC
)
);
public function isValidFilterColumn($column)
{
if ($column[0] === '_'
&& preg_match('~^_host~', $column)
) {
return true;
}
return parent::isValidFilterColumn($column);
}
}

View File

@ -5,43 +5,169 @@ namespace Icinga\Monitoring\View;
use Icinga\Data\AbstractQuery; use Icinga\Data\AbstractQuery;
use Icinga\Data\Filter; use Icinga\Data\Filter;
/**
* MonitoringView provides consistent views to our Icinga Backends
*
* TODO: * This could be renamed to AbstractView
* * We might need more complex filters (let's do the simple ones first)
*
* You should not directly instantiate such a view but always go through the
* Monitoring Backend. Using the Backend's select() method selecting from
* 'virtualtable' returns a Icinga\Monitoring\View\VirtualtableView instance.
*
* Usage example:
* <code>
* use Icinga\Monitoring\Backend;
* $backend = Backend::getInstance();
* $query = $backend->select()->from('viewname', array(
* 'one_column',
* 'another_column',
* 'alias' => 'whatever_column',
* ))->where('any_column', $search);
*
* print_r($query->fetchAll());
* </code>
*
* What we see in the example is that:
* * you can (and should) use a defined set of columns when issueing a query
* * you can use proper alias names to have an influence on column names
* in the result set
* * the MonitoringView behaves like any Query object and provides useful
* methods such as fetchAll, count, fetchPairs and so on
*
* If you want to fill a dropdown form element with all your hostgroups
* starting with "net", using the hostgroup name as the form elements value but
* showing the hostgroup aliases in the dropdown you would probably do this as
* follows:
*
* <code>
* $pairs = $backend->select->from(
* 'hostgroups',
* array('hostgroup_name', 'hostgroup_alias')
* )->where('hostgroup_name', 'net*')->fetchPairs();
* $formElement->setMultiOptions($pairs);
* </code>
*
* MonitoringView is a proxy to your Backend Query. While both are Query objects
* providing partially the same interface, they are not directly related to
* each other.
*/
class MonitoringView extends AbstractQuery class MonitoringView extends AbstractQuery
{ {
/**
* Stores the backend-specific Query Object
* @var AbstractQuery
*/
protected $query; protected $query;
/**
* All the columns provided by this View MUST be specified here
* @var Array
*/
protected $availableColumns = array(); protected $availableColumns = array();
/**
* Columns available for search only but not in result sets
* @var Array
*/
protected $specialFilters = array(); protected $specialFilters = array();
/**
* All views COULD have a generic column called 'search', if available the
* real column name is defined here.
* TODO: This may be subject to change as a "search" could involve multiple
* columns
* @var string
*/
protected $searchColumn;
/**
* Defines default sorting rules for specific column names. This helps in
* providing "intelligent" sort defaults for different columns (involving
* also other columns where needed)
* @var Array
*/
protected $sortDefaults = array(); protected $sortDefaults = array();
/**
* Whether this view provides a specific column name
*
* @param string $column Column name
* @return bool
*/
public function hasColumn($column) public function hasColumn($column)
{ {
return in_array($column, $this->availableColumns); return in_array($column, $this->availableColumns);
} }
/**
* Get a list of all available column names
*
* This might be useful for dynamic frontend tables or similar
*
* @return Array
*/
public function getAvailableColumns() public function getAvailableColumns()
{ {
return $this->availableColumns; return $this->availableColumns;
} }
/**
* Extract and apply filters and sort rules from a given request object
*
* TODO: Enforce Icinga\Web\Request (or similar) as soon as we replaced
* Zend_Controller_Request
*
* @param mixed $request The request object
* @return self
*/
public function applyRequest($request) public function applyRequest($request)
{ {
return $this->applyRequestFilters($request) return $this->applyRequestFilters($request)
->applyRequestSorting($request); ->applyRequestSorting($request);
} }
/**
* Extract and apply sort column and directon from given request object
*
* @param mixed $request The request object
* @return self
*/
protected function applyRequestSorting($request) protected function applyRequestSorting($request)
{ {
return $this->order( return $this->order(
// TODO: Use first sortDefaults entry if available, fall back to
// column if not
$request->getParam('sort', $this->availableColumns[0]), $request->getParam('sort', $this->availableColumns[0]),
$request->getParam('dir') $request->getParam('dir')
); );
} }
/**
* Extract and apply filters from a given request object
*
* Columns not fitting any defined available column or special filter column
* will be silently ignored.
*
* @param mixed $request The request object
* @return self
*/
protected function applyRequestFilters($request) protected function applyRequestFilters($request)
{ {
foreach ($request->getParams() as $key => $value) { foreach ($request->getParams() as $key => $value) {
if ($key === 'search' && $value !== '') {
if (strpos($value, '=') === false) {
if ($this->searchColumn !== null) {
$this->where($this->searchColumn, $value);
}
} else {
list($k, $v) = preg_split('~\s*=\s*~', $value, 2);
if ($this->isValidFilterColumn($k)) {
$this->where($k, $v);
}
}
continue;
}
if ($this->isValidFilterColumn($key)) { if ($this->isValidFilterColumn($key)) {
$this->where($key, $value); $this->where($key, $value);
} }
@ -50,7 +176,16 @@ class MonitoringView extends AbstractQuery
} }
// TODO: applyAuthFilters(Auth $auth = null) // TODO: applyAuthFilters(Auth $auth = null)
// MonitoringView will enforce restrictions as provided by the Auth
// backend
/**
* Apply an array of filters. This might become obsolete or even improved
* and accept Filter objects - this is still to be defined.
*
* @param Array $filters Filter array
* @return self
*/
public function applyFilters($filters) public function applyFilters($filters)
{ {
foreach ($filters as $col => $filter) { foreach ($filters as $col => $filter) {
@ -59,11 +194,26 @@ class MonitoringView extends AbstractQuery
return $this; return $this;
} }
/**
* Gives you a filter object with all applied filters excluding auth filters
* Might be used to build URLs fitting query objects.
*
* Please take care, as Url has been improved the Filter object might
* become subject to change
*
* @return Filter
*/
public function getAppliedFilter() public function getAppliedFilter()
{ {
return new Filter($this->filters); return new Filter($this->filters);
} }
/**
* Default sort direction for given column, ASCending if not defined
*
* @param String $col Column name
* @return int
*/
protected function getDefaultSortDir($col) protected function getDefaultSortDir($col)
{ {
if (isset($this->sortDefaults[$col]['default_dir'])) { if (isset($this->sortDefaults[$col]['default_dir'])) {
@ -72,11 +222,21 @@ class MonitoringView extends AbstractQuery
return self::SORT_ASC; return self::SORT_ASC;
} }
/**
* getQuery gives you an instance of the Query object implementing this
* view for the chosen backend.
*
* @return AbstractQuery
*/
public function getQuery() public function getQuery()
{ {
if ($this->query === null) { if ($this->query === null) {
$class = substr(array_pop(preg_split('|\\\|', get_class($this))), 0, -4) . 'Query'; $class = substr(
array_pop(preg_split('|\\\|', get_class($this))),
0,
-4
) . 'Query';
$class = '\\' . get_class($this->ds) . '\\Query\\' . $class; $class = '\\' . get_class($this->ds) . '\\Query\\' . $class;
$query = new $class($this->ds, $this->columns); $query = new $class($this->ds, $this->columns);

View File

@ -5,9 +5,11 @@ namespace Icinga\Monitoring\View;
class StatusView extends MonitoringView class StatusView extends MonitoringView
{ {
protected $query; protected $query;
// protected $searchColumn = 'host'; -> besser in der Query, 'search' mitgeben
protected $availableColumns = array( protected $availableColumns = array(
// Hosts // Hosts
'host',
'host_name', 'host_name',
'host_display_name', 'host_display_name',
'host_alias', 'host_alias',
@ -29,9 +31,15 @@ class StatusView extends MonitoringView
'host_does_active_checks', 'host_does_active_checks',
'host_accepts_passive_checks', 'host_accepts_passive_checks',
'host_last_state_change', 'host_last_state_change',
'host_last_hard_state',
'host_last_hard_state_change', 'host_last_hard_state_change',
'host_notifications_enabled',
'host_last_time_up',
'host_last_time_down',
'host_last_time_unreachable',
// Services // Services
'service',
'service_description', 'service_description',
'service_display_name', 'service_display_name',
@ -49,7 +57,15 @@ class StatusView extends MonitoringView
'service_does_active_checks', 'service_does_active_checks',
'service_accepts_passive_checks', 'service_accepts_passive_checks',
'service_last_state_change', 'service_last_state_change',
'service_last_hard_state',
'service_last_hard_state_change', 'service_last_hard_state_change',
'service_notifications_enabled',
'service_last_time_ok',
'service_last_time_warning',
'service_last_time_critical',
'service_last_time_unknown',
'object_type',
// Status // Status
'problems', 'problems',
@ -66,6 +82,12 @@ class StatusView extends MonitoringView
'host_name' => array( 'host_name' => array(
'columns' => array( 'columns' => array(
'host_name', 'host_name',
),
'default_dir' => self::SORT_ASC
),
'service_host_name' => array(
'columns' => array(
'service_host_name',
'service_description' 'service_description'
), ),
'default_dir' => self::SORT_ASC 'default_dir' => self::SORT_ASC
@ -86,6 +108,7 @@ class StatusView extends MonitoringView
'severity' => array( 'severity' => array(
'columns' => array( 'columns' => array(
'severity', 'severity',
'host_name',
'service_last_state_change', 'service_last_state_change',
), ),
'default_dir' => self::SORT_DESC 'default_dir' => self::SORT_DESC

View File

@ -1,6 +1,7 @@
SetEnv APPLICATION_ENV development SetEnv APPLICATION_ENV development
RewriteEngine on RewriteEngine on
RewriteRule ^css/icinga.css css.php
RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d RewriteCond %{REQUEST_FILENAME} -d

View File

@ -1,13 +1,21 @@
<?php <?php
namespace Tests\Icinga\Protocol\Livestatus; namespace Tests\Icinga\Protocol\Livestatus;
use Icinga\Protocol\Livestatus\Connection;
use Icinga\Protocol\Livestatus\Query;
use PHPUnit_Framework_TestCase as TestCase;
require_once('../../library/Icinga/Protocol/AbstractQuery.php');
require_once('../../library/Icinga/Protocol/Livestatus/Connection.php');
require_once('../../library/Icinga/Protocol/Livestatus/Query.php');
/** /**
* *
* Test class for Connection * Test class for Connection
* Created Wed, 16 Jan 2013 15:15:16 +0000
* *
**/ **/
class ConnectionTest extends \PHPUnit_Framework_TestCase class ConnectionTest extends TestCase
{ {
/** /**
@ -25,7 +33,10 @@ class ConnectionTest extends \PHPUnit_Framework_TestCase
**/ **/
public function testSelect() public function testSelect()
{ {
$this->markTestIncomplete('testSelect is not implemented yet'); $socket = tempnam(sys_get_temp_dir(), 'IcingaTest');
$connection = new Connection($socket);
$this->assertTrue($connection->select() instanceof Query);
unlink($socket);
} }
/** /**

View File

@ -1,13 +1,22 @@
<?php <?php
namespace Tests\Icinga\Protocol\Livestatus; namespace Tests\Icinga\Protocol\Livestatus;
use Icinga\Protocol\Livestatus\Connection;
use Icinga\Protocol\Livestatus\Query;
use PHPUnit_Framework_TestCase as TestCase;
require_once('../../library/Icinga/Protocol/AbstractQuery.php');
require_once('../../library/Icinga/Protocol/Livestatus/Query.php');
require_once('../../library/Icinga/Protocol/Livestatus/Connection.php');
/** /**
* *
* Test class for Query * Test class for Query
* Created Wed, 16 Jan 2013 15:15:16 +0000
* *
**/ **/
class QueryTest extends \PHPUnit_Framework_TestCase class QueryTest extends TestCase
{ {
/** /**

View File

@ -108,9 +108,9 @@ def main():
command_options.append('--log-junit') command_options.append('--log-junit')
command_options.append(os.path.join(report_directory, command_options.append(os.path.join(report_directory,
'phpunit_results.xml')) 'phpunit_results.xml'))
command_options.append('--coverage-clover') command_options.append('--coverage-html')
command_options.append(os.path.join(report_directory, command_options.append(os.path.join(report_directory,
'phpunit_coverage.xml')) 'php_html_coverage'))
if options.include: if options.include:
command_options.append('--filter') command_options.append('--filter')
command_options.append(options.include) command_options.append(options.include)