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
test/frontend/static/public
# Generated API documentation
doc/api

View File

@ -1,60 +1,20 @@
<?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\Application\Icinga;
use Zend_Controller_Action_Exception as ActionException;
use Icinga\Application\Icinga,
Zend_Controller_Action_Exception as ActionException;
use Icinga\Application\Benchmark;
/**
* Class StaticController
* @package Icinga\Web\Form
*/
class StaticController extends ActionController
{
/**
* @var bool
*/
protected $handlesAuthentication = true;
/**
*
*/
public function init()
{
$this->_helper->viewRenderer->setNoRender(true);
$this->_helper->layout()->disableLayout();
}
/**
* @return array
*/
private function getModuleList()
{
$modules = Icinga::app()->moduleManager()->getLoadedModules();
@ -64,35 +24,64 @@ class StaticController extends ActionController
foreach ($modules as $name => $module) {
$hasJs = file_exists($module->getBasedir() . "/public/js/$name.js");
$result[] = array(
'name' => $name,
'active' => true,
'type' => 'generic',
'name' => $name,
'active' => true,
'type' => 'generic',
'behaviour' => $hasJs
);
}
return $result;
}
/**
*
*/
public function modulelistAction()
{
$this->_helper->viewRenderer->setNoRender(true);
$this->_helper->layout()->disableLayout();
$this->getResponse()->setHeader("Content-Type", "application/json");
echo "define(function() { return " . json_encode($this->getModuleList(), true) . "; })";
$this->getResponse()->setHeader("Content-Type","application/json");
echo "define(function() { return ".json_encode($this->getModuleList(),true)."; })";
exit;
}
/**
* @throws \Zend_Controller_Action_Exception
*/
public function imgAction()
{
$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()
{
$module = $this->_getParam('module_name');
$file = $this->_getParam('file');
$module = $this->_getParam('moduleName');
$file = $this->_getParam('file');
$basedir = Icinga::app()->getModule($module)->getBaseDir();
$filePath = $basedir . '/public/js/' . $file;
@ -126,8 +115,7 @@ class StaticController extends ActionController
} else {
readfile($filePath);
}
return;
$this->_viewRenderer->setNoRender();
}
}
// @codingStandardsIgnoreEnd
}

View File

@ -6,9 +6,7 @@
<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 ?>
<?= $this->render('inline.phtml') ?>
</div>
<div id="icinga-detail" class="icinga-container " container-id="icinga-detail">

View File

@ -12,14 +12,19 @@
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="<?php echo $this->baseUrl('css/normalize.min.css') ?>">
<link rel="stylesheet" href="<?php echo $this->baseUrl('css/vendor/bootstrap.css') ?>">
<link rel="stylesheet" href="<?php echo $this->baseUrl('css/main.css') ?>">
<link rel="stylesheet" href="<?php echo $this->baseUrl('css/vendor/jquery.qtip.min.css') ?>">
<link rel="stylesheet" href="<?= $this->baseUrl('css/normalize.min.css') ?>">
<link rel="stylesheet" href="<?= $this->baseUrl('css/vendor/bootstrap.css') ?>">
<link rel="stylesheet" href="<?= $this->baseUrl('css/vendor/ui-lightness/jquery-ui-1.10.3.custom.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">
var base_url = '<?php echo $this->baseUrl() ?>';
var base_url = '<?= $this->baseUrl() ?>';
ICINGA_DEBUG = true;
</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>
<link rel="stylesheet" href="<?php echo $this->baseUrl('css.php') ?>">
<link rel="stylesheet" href="<?php echo $this->baseUrl('css/vendor/bootstrap-responsive.min.css') ?>">
@ -30,29 +35,7 @@
<script data-main="<?php echo $this->baseUrl('js/main.js')?>" src="<?php echo $this->baseUrl('js/vendor/require.js') ?>"></script>
</head>
<body class="cranberry">
<?php echo $this->render('parts/topbar.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 class="cranberry">
<?= $this->render('just-the-body.phtml') ?>
</body>
</html>

View File

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

View File

@ -1,7 +1,7 @@
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner ">
<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>
<?php if ($this->auth()->isAuthenticated()): ?>
<ul class="nav pull-right" >
@ -11,13 +11,14 @@
</form>
</li>
<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())
?> <i class="icon-user icon-white" style="margin-top:0.2em"></i>
<b class="caret"></b>
</a>
<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>
</li>
</ul>

View File

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

View File

@ -1,61 +1,58 @@
<? if ($this->pageCount > 1): ?>
<?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;
?>
<div class="pagination pagination-mini" style="margin:0px">
$limit = $this->itemCountPerPage;
$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>
<!-- Previous page link -->
<? if (isset($this->previous)): ?>
<li><a href="<?= $this->url($preserve + array('page' => $this->previous)); ?>" title="<?=
sprintf($fromto,
($this->current - 2) * $this->itemCountPerPage + 1,
($this->current - 1) * $this->itemCountPerPage,
$this->totalItemCount)
?>">« <?= t('Back') ?></a></li>
<? else: ?>
<li class="disabled"><span>« <?= t('Back') ?></span></li>
<?php
endif;
?>
<?php if (isset($this->previous)): ?>
<li><a href="<?= $this->url()->setParam('page', $this->previous) ?>" title="<?= $titleprev ?>">« <?= $this->translate('Back') ?></a></li>
<?php else: ?>
<li class="disabled"><span>« <?= $this->translate('Back') ?></span></li>
<?php endif ?>
<!-- Numbered page links -->
<? foreach ($this->pagesInRange as $page): ?>
<?php
$start = ($page - 1) * $this->itemCountPerPage + 1;
$end = $page * $this->itemCountPerPage;
if ($end > $total) {
$end = $total;
}
$title = sprintf($fromto, $start, $end, $total);
$active_class = $page === $this->current ? ' class="active"' : '';
foreach ($this->pagesInRange as $page):
?>
<?php if ($page === '...'): ?>
$start = ($page - 1) * $limit + 1;
$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>
<?php else: ?>
<li<?= $active_class ?>><a href="<?= $this->url(
$preserve + array('page' => $page)
); ?>" title="<?= $title ?>"><?= $page; ?></a></li>
<? endif ?>
<? endforeach ?>
<li<?= $class ?>><a href="<?= $this->url()->setParam('page', $page) ?>" title="<?= $title ?>"><?= $page ?></a></li>
<?php
endif;
endforeach;
?>
<!-- Next page link -->
<? if (isset($this->next)): ?>
<li><a href="<?= $this->url($preserve + array('page' => $this->next)); ?>" title="<?=
sprintf($fromto,
($this->current) * $this->itemCountPerPage + 1,
($this->current + 1) * $this->itemCountPerPage,
$total)
?>"><?= t('Next') ?> »</a></li>
<li><a href="<?= $this->url()->setParam('page', $this->next) ?>" title="<?= $title_next ?>"><?= t('Next') ?> »</a></li>
<? else: ?>
<li class="disabled"><span><?= t('Next') ?> »</span></li>
<? endif ?>
</ul>
</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
# {{{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
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;
use Icinga\Application\Modules\Module;
use Zend_Config_Ini;
use Zend_Config;
@ -32,17 +33,19 @@ class Config extends Zend_Config_Ini
return parent::__construct($filename, $section, $options);
}
public function getModuleConfig($key, $module)
public static function module($name, $file = null)
{
$manager = Icinga::app()->moduleManager();
$res = null;
if ($manager->hasInstalled($module)) {
$filename = $manager->getModuleConfigDir($module) . "/$key.ini";
if (file_exists($filename)) {
return $this->$key = new Config($filename);
}
if ($file === null) {
$file = $name . '.ini'; // TODO: default should be module/config.ini
}
return $res;
$filename = Module::get($name)->getConfigDir() . '/' . $file;
if (file_exists($filename)) {
$config = new Config($filename);
// Compat: $config->$module->$whatever
self::getInstance()->$name = $config;
return $config;
}
return null;
}
public function __get($key)

View File

@ -3,6 +3,7 @@
namespace Icinga\Application\Modules;
use Icinga\Application\ApplicationBootstrap;
use Icinga\Application\Icinga;
use Icinga\Web\Hook;
use Zend_Controller_Router_Route as Route;
@ -38,6 +39,23 @@ class Module
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()
{
return file_exists($this->getCssFilename());
@ -58,6 +76,13 @@ class Module
return $this->configdir;
}
public function getConfig($file = null)
{
return $this->app
->getConfig()
->module($this->name, $file);
}
protected function registerLibrary()
{
if (file_exists($this->libdir) && is_dir($this->libdir)) {
@ -88,17 +113,14 @@ class Module
}
$this->registerLocales()
->registerRoutes()
->registerMenuEntries();
->registerRoutes();
// ->registerMenuEntries();
return $this;
}
protected function registerMenuEntries()
{
$cfg = $this->app
->getConfig()
->getModuleConfig('menu', $this->name);
$cfg = $this->getConfig('menu.ini');
$view = $this->app->getView();
if ($cfg) {
$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;
}

View File

@ -3,21 +3,21 @@
/**
* 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>
*/
@ -32,6 +32,7 @@ use Zend_Paginator as Paginator;
use Zend_View_Helper_PaginationControl as PaginationControl;
use Zend_Controller_Action_HelperBroker as ActionHelper;
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
@ -169,7 +170,7 @@ class Web extends ApplicationBootstrap
protected function prepareView()
{
$view = ActionHelper::getStaticHelper('viewRenderer');
$view->initView();
$view->setView(new IcingaView());
$view->view->addHelperPath($this->appdir . '/views/helpers');
// TODO: find out how to avoid this additional helper path:

View File

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

View File

@ -36,6 +36,22 @@ class ArrayDatasource implements DatasourceInterface
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)
{
$result = $this->getResult($query);
@ -70,11 +86,14 @@ class ArrayDatasource implements DatasourceInterface
$result[] = $row;
} else {
$c_row = (object) array();
foreach ($columns as $key) {
foreach ($columns as $alias => $key) {
if (is_int($alias)) {
$alias = $key;
}
if (isset($row->$key)) {
$c_row->$key = $row->$key;
$c_row->$alias = $row->$key;
} else {
$c_row->$key = null;
$c_row->$alias = null;
}
}
$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
namespace Icinga\Pdf;
namespace Icinga\File;
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_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', '/develop'); // ???
define('K_PATH_URL', (string) Url::create('/') === '/' ? '' : (string) Url::create('/')); // ???'/'));
define('K_PATH_MAIN', dirname(ICINGA_LIBDIR) . '/public');
define('K_PATH_FONTS', ICINGA_LIBDIR . '/vendor/tcpdf/fonts/');
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_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_THAI_TOPCHARS', false);
require_once 'vendor/tcpdf/tcpdf.php';
class File extends TCPDF
class Pdf extends TCPDF
{
protected $cell_height_ratio = 1.25;
public function __construct(

View File

@ -1,31 +1,10 @@
<?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;
use Icinga\Exception\ConfigurationError as ConfigError;
use Icinga\Application\Platform;
use Icinga\Application\Config;
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>
* @author Icinga-Web Team <info@icinga.org>
* @package Icinga\Protocol\Ldap
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
*/
class Connection
{
/**
* @var string
*/
const LDAP_NO_SUCH_OBJECT = 0x20;
protected $ds;
/**
* @var string
*/
protected $hostname;
/**
* @var string
*/
protected $port = 389;
protected $bind_dn;
/**
* @var string
*/
protected $bind_pw;
/**
* @var string
*/
protected $root_dn;
/**
* @var string
*/
protected $count;
/**
* @var 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.3' => '11.3', // Whoami
// '1.3.6.1.1.8' => '8', // Cancel Extended Request
);
protected $use_tls = false;
protected $force_tls = false;
/**
* @var array
*/
protected $ms_capability = array(
// Prefix LDAP_CAP_
// Source: http://msdn.microsoft.com/en-us/library/cc223359.aspx
// 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
// and of performing subsequent binds on a signed or sealed connection.
'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
'1.2.840.113556.1.4.1670' => 'ACTIVE_DIRECTORY_V51_OID',
// If AD LDS: accepts DIGEST-MD5 binds for AD LDSsecurity principals
'1.2.840.113556.1.4.1880' => 'ACTIVE_DIRECTORY_ADAM_DIGEST',
// Running Active Directory as AD LDS
'1.2.840.113556.1.4.1851' => 'ACTIVE_DIRECTORY_ADAM_OID',
// If AD DS: it's a Read Only DC (RODC)
'1.2.840.113556.1.4.1920' => 'ACTIVE_DIRECTORY_PARTIAL_SECRETS_OID',
// Running at least W2K8
'1.2.840.113556.1.4.1935' => 'ACTIVE_DIRECTORY_V60_OID',
// Running at least W2K8r2
'1.2.840.113556.1.4.2080' => 'ACTIVE_DIRECTORY_V61_R2_OID',
// Running at least W2K12
'1.2.840.113556.1.4.2237' => 'ACTIVE_DIRECTORY_W8_OID',
);
/**
* @var string
*/
protected $root;
protected $supports_v3 = false;
protected $supports_tls = false;
/**
* Constructor
*
@ -139,24 +94,17 @@ class Connection
public function __construct($config)
{
$this->hostname = $config->hostname;
$this->bind_dn = $config->bind_dn;
$this->bind_pw = $config->bind_pw;
$this->root_dn = $config->root_dn;
$this->use_tls = isset($config->tls) ? $config->tls : false;
$this->force_tls = $this->use_tls;
$this->bind_dn = $config->bind_dn;
$this->bind_pw = $config->bind_pw;
$this->root_dn = $config->root_dn;
}
/**
* @return string
*/
public function getDN()
{
return $this->root_dn;
}
/**
* @return Root|string
*/
public function root()
{
if ($this->root === null) {
@ -165,36 +113,22 @@ class Connection
return $this->root;
}
/**
* @return Query
*/
public function select()
{
return new Query($this);
}
/**
* @param $query
* @param array $fields
* @return mixed
*/
public function fetchOne($query, $fields = array())
{
$row = (array)$this->fetchRow($query, $fields);
$row = (array) $this->fetchRow($query, $fields);
return array_shift($row);
}
/**
* @param $query
* @param array $fields
* @return mixed
* @throws Exception
*/
public function fetchDN($query, $fields = array())
{
$rows = $this->fetchAll($query, $fields);
if (count($rows) !== 1) {
throw new Exception(
throw new \Exception(
sprintf(
'Cannot fetch single DN for %s',
$query
@ -204,11 +138,7 @@ class Connection
return key($rows);
}
/**
* @param $query
* @param array $fields
* @return mixed
*/
public function fetchRow($query, $fields = array())
{
// TODO: This is ugly, make it better!
@ -216,31 +146,28 @@ class Connection
return array_shift($results);
}
/**
* @param Query $query
* @return int
*/
public function count(Query $query)
{
$results = $this->runQuery($query, '+');
if (! $results) {
return 0;
}
return ldap_count_entries($this->ds, $results);
}
/**
* @param $query
* @param array $fields
* @return array
*/
public function fetchAll($query, $fields = array())
{
$offset = null;
$limit = null;
if ($query->hasLimit()) {
$offset = $query->getOffset();
$limit = $query->getLimit();
$limit = $query->getLimit();
}
$entries = array();
$results = $this->runQuery($query, $fields);
if (! $results) {
return array();
}
$entry = ldap_first_entry($this->ds, $results);
$count = 0;
while ($entry) {
@ -248,7 +175,8 @@ class Connection
&& ($limit === null || ($offset + $limit) >= $count)
) {
$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++;
$entry = ldap_next_entry($this->ds, $entry);
@ -257,13 +185,9 @@ class Connection
return $entries;
}
/**
* @param $attrs
* @return object
*/
public function cleanupAttributes(& $attrs)
{
$clean = (object)array();
$clean = (object) array();
for ($i = 0; $i < $attrs['count']; $i++) {
$attr_name = $attrs[$i];
if ($attrs[$attr_name]['count'] === 1) {
@ -277,12 +201,6 @@ class Connection
return $clean;
}
/**
* @param $query
* @param $fields
* @return resource
* @throws Exception
*/
protected function runQuery($query, $fields)
{
$this->connect();
@ -293,17 +211,19 @@ class Connection
// We do not support pagination right now, and there is no chance to
// do so for PHP < 5.4. Warnings about "Sizelimit exceeded" will
// therefore not be hidden right now.
Log::debug("Query: %s", $query->__toString());
$results = ldap_search(
$results = @ldap_search(
$this->ds,
$this->root_dn,
(string)$query,
(string) $query,
$fields,
0, // Attributes and values
0 // No limit - at least where possible
0 // No limit - at least where possible
);
if (!$results) {
throw new Exception(
if ($results === false) {
if (ldap_errno($this->ds) === self::LDAP_NO_SUCH_OBJECT) {
return false;
}
throw new \Exception(
sprintf(
'LDAP query "%s" (root %s) failed: %s',
$query,
@ -311,7 +231,6 @@ class Connection
ldap_error($this->ds)
)
);
die('Query failed');
}
$list = array();
@ -323,186 +242,229 @@ class Connection
return $results;
}
/**
* @param $username
* @param $password
* @return bool
*/
public function testCredentials($username, $password)
{
Log::debug("Trying to connect to %s", $this->hostname);
$ds = ldap_connect($this->hostname);
Log::debug("ldap_bind (%s)", $username);
$ds = $this->prepareNewConnection();
$r = @ldap_bind($ds, $username, $password);
if ($r) {
log::debug(
'Successfully tested LDAP credentials (%s / %s)',
$username,
'***'
);
return true;
} else {
log::fatal(
'LDAP connection (%s / %s) failed: %s',
log::debug(
'Testing LDAP credentials (%s / %s) failed: %s',
$username,
'***',
ldap_error($ds)
);
return false;
/* TODO: Log failure
throw new Exception(sprintf(
'LDAP connection (%s / %s) failed: %s',
$username,
'***',
ldap_error($ds)
));
*/
}
}
/**
* @return string
*/
protected function getConfigDir()
protected function getConfigDir($sub = null)
{
return Config::getInstance()->getConfigDir() . '/ldap';
$dir = Config::getInstance()->getConfigDir() . '/ldap';
if ($sub !== null) {
$dir .= '/' . $sub;
}
return $dir;
}
/**
* @param $domain
*/
protected function discoverServerlistForDomain($domain)
{
$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 prepareTlsEnvironment()
protected function prepareNewConnection()
{
$strict_tls = true;
$use_local_ca = true;
if (Platform::isWindows()) {
} else {
$cfg_dir = $this->getConfigDir();
if ($strict_tls) {
putenv(sprintf('LDAPRC=%s/%s', $cfg_dir, 'ldap_ca.conf'));
$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 {
putenv(sprintf('LDAPRC=%s/%s', $cfg_dir, 'ldap_nocert.conf'));
// TODO: Log noticy -> TLS enabled but not announced
}
}
// file_put_contents('/tmp/tom_LDAP.conf', "TLS_REQCERT never\n");
// 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;
}
/**
* @return object
*/
protected function fetchRootDseDetails()
protected function prepareTlsEnvironment()
{
$query = $this->select()->from('*', array('+'));
/*, array(
'defaultNamingContext',
'namingContexts',
'supportedSaslMechanisms',
'dnsHostName',
'schemaNamingContext',
'supportedLDAPVersion', // => array(3, 2)
'supportedCapabilities'
))*/
$strict_tls = true;
// TODO: allow variable known CA location (system VS Icinga)
if (Platform::isWindows()) {
// putenv('LDAP...')
} else {
if ($strict_tls) {
$ldap_conf = $this->getConfigDir('ldap_ca.conf');
} else {
$ldap_conf = $this->getConfigDir('ldap_nocert.conf');
}
putenv('LDAPRC=' . $ldap_conf);
if (getenv('LDAPRC') !== $ldap_conf) {
throw new Exception('putenv failed');
}
}
}
protected function discoverCapabilities($ds)
{
$query = $this->select()->from(
'*',
array(
'defaultNamingContext',
'namingContexts',
'vendorName',
'vendorVersion',
'supportedSaslMechanisms',
'dnsHostName',
'schemaNamingContext',
'supportedLDAPVersion', // => array(3, 2)
'supportedCapabilities',
'supportedExtension',
'+'
)
);
$fields = $query->listFields();
$result = ldap_read(
$this->ds,
$result = @ldap_read(
$ds,
'',
(string)$query,
$query->listFields(),
0,
0
(string) $query,
$query->listFields()
);
$entry = ldap_first_entry($this->ds, $result);
$result = $this->cleanupAttributes(ldap_get_attributes($this->ds, $entry));
if (! $result) {
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)) {
foreach ($result->supportedCapabilities as $oid) {
if (array_key_exists($oid, $this->ms_capability)) {
echo $this->ms_capability[$oid] . "\n";
// echo $this->ms_capability[$oid] . "\n";
}
}
}
if (isset($result->supportedExtension)) {
foreach ($result->supportedExtension as $oid) {
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()
{
if ($this->ds !== null) {
return;
}
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);
$this->ds = $this->prepareNewConnection();
$r = @ldap_bind($this->ds, $this->bind_dn, $this->bind_pw);
if (!$r) {
log::fatal(
'LDAP connection (%s / %s) failed: %s',
$this->bind_dn,
'***',
ldap_error($this->ds)
);
throw new ConfigError(
if (! $r) {
throw new \Exception(
sprintf(
'Could not connect to the authentication server, please '.
'review your LDAP connection settings.'
'LDAP connection to %s:%s (%s / %s) failed: %s',
$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
// {{{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;
use Icinga\Web\Paginator\Adapter\QueryAdapter;
/**
* Search class
*
* @package Icinga\Protocol\Ldap
*/
/**
* Search abstraction class
*
@ -38,52 +18,24 @@ use Icinga\Web\Paginator\Adapter\QueryAdapter;
*
* @copyright Copyright (c) 2013 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
* @package Icinga\Protocol\Ldap
*/
class Query
{
/**
* @var Connection
*/
protected $connection;
/**
* @var array
*/
protected $filters = array();
/**
* @var array
*/
protected $fields = array();
/**
* @var
*/
protected $limit_count;
/**
* @var
*/
protected $limit_offset;
/**
* @var array
*/
protected $sort_columns = array();
/**
* @var
*/
protected $count;
/**
* Constructor
*
* @param \Icinga\Protocol\Ldap\Connection $connection LDAP Connection object
* @return \Icinga\Protocol\Ldap\Query
* @param Connection LDAP Connection object
* @return void
*/
public function __construct(Connection $connection)
{
@ -106,14 +58,11 @@ class Query
/**
* Count result set, ignoring limits
*
* @param null $count
* @param null $offset
* @throws Exception
* @return int
*/
public function limit($count = null, $offset = null)
{
if (!preg_match('~^\d+~', $count . $offset)) {
if (! preg_match('~^\d+~', $count . $offset)) {
throw new Exception(
sprintf(
'Got invalid limit: %s, %s',
@ -122,8 +71,8 @@ class Query
)
);
}
$this->limit_count = (int)$count;
$this->limit_offset = (int)$offset;
$this->limit_count = (int) $count;
$this->limit_offset = (int) $offset;
return $this;
}
@ -176,15 +125,12 @@ class Query
{
$result = $this->fetchAll();
$sorted = array();
$quotedDn = preg_quote($this->connection->getDN(), '/');
foreach ($result as $key => & $item) {
$new_key = LdapUtils::implodeDN(
array_reverse(
LdapUtils::explodeDN(
preg_replace(
'/,' . preg_quote($this->connection->getDN(), '/') . '$/',
'',
$key
)
preg_replace('/,' . $quotedDn . '$/', '', $key)
)
)
);
@ -194,7 +140,11 @@ class Query
ksort($sorted);
$tree = Root::forConnection($this->connection);
$root_dn = $tree->getDN();
foreach ($sorted as $sort_key => & $key) {
if ($key === $root_dn) {
continue;
}
$tree->createChildByDN($key, $result[$key]);
}
return $tree;
@ -246,8 +196,6 @@ class Query
*
* This creates an objectClass filter
*
* @param $objectClass
* @param array $fields
* @return Query
*/
public function from($objectClass, $fields = array())
@ -308,8 +256,6 @@ class Query
/**
* Return a pagination adapter for the current query
*
* @param null $limit
* @param null $page
* @return \Zend_Paginator
*/
public function paginate($limit = null, $page = null)
@ -325,7 +271,7 @@ class Query
}
$paginator = new \Zend_Paginator(
// TODO: Adapter doesn't fit yet:
new QueryAdapter($this)
new \Icinga\Web\Paginator\Adapter\QueryAdapter($this)
);
$paginator->setItemCountPerPage($limit);
$paginator->setCurrentPageNumber($page);
@ -364,7 +310,7 @@ class Query
}
/**
* Destructor
* Descructor
*/
public function __destruct()
{

View File

@ -1,6 +1,28 @@
<?php
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
{
const TYPE_UNIX = 1;
@ -49,19 +71,23 @@ class Connection
$this->assertPhpExtensionLoaded('sockets');
if ($socket[0] === '/') {
if (! is_writable($socket)) {
throw new \Exception(sprintf(
'Cannot write to livestatus socket "%s"',
$socket
));
throw new \Exception(
sprintf(
'Cannot write to livestatus socket "%s"',
$socket
)
);
}
$this->socket_type = self::TYPE_UNIX;
$this->socket_path = $socket;
} else {
if (! preg_match('~^tcp://([^:]+):(\d+)~', $socket, $m)) {
throw new \Exception(sprintf(
'Invalid TCP socket syntax: "%s"',
$socket
));
throw new \Exception(
sprintf(
'Invalid TCP socket syntax: "%s"',
$socket
)
);
}
// TODO: Better syntax checks
$this->socket_host = $m[1];
@ -80,17 +106,17 @@ class Connection
{
$count = clone($query);
$count->count();
\Icinga\Benchmark::measure('Sending Livestatus Count Query');
$data = $this->_fetch((string) $count);
\Icinga\Benchmark::measure('Got Livestatus count result');
Benchmark::measure('Sending Livestatus Count Query');
$data = $this->doFetch((string) $count);
Benchmark::measure('Got Livestatus count result');
return $data[0][0];
}
public function fetchAll(Query $query)
{
\Icinga\Benchmark::measure('Sending Livestatus Query');
$data = $this->_fetch((string) $query);
\Icinga\Benchmark::measure('Got Livestatus Data');
Benchmark::measure('Sending Livestatus Query');
$data = $this->doFetch((string) $query);
Benchmark::measure('Got Livestatus Data');
if ($query->hasColumns()) {
$headers = $query->getColumnAliases();
} else {
@ -114,12 +140,12 @@ class Connection
$query->getLimit()
);
}
\Icinga\Benchmark::measure('Data sorted, limits applied');
Benchmark::measure('Data sorted, limits applied');
return $result;
}
protected function _fetch($raw_query)
protected function doFetch($raw_query)
{
$conn = $this->getConnection();
$this->writeToSocket($raw_query);
@ -128,15 +154,17 @@ class Connection
$length = (int) trim(substr($header, 4));
$body = $this->readFromSocket($length);
if ($status !== 200) {
throw new \Exception(sprintf(
'Problem while reading %d bytes from livestatus: %s',
$length,
$body
));
throw new Exception(
sprintf(
'Problem while reading %d bytes from livestatus: %s',
$length,
$body
)
);
}
$result = json_decode($body);
if ($result === null) {
throw new \Exception('Got invalid response body from livestatus');
throw new Exception('Got invalid response body from livestatus');
}
return $result;
@ -147,13 +175,15 @@ class Connection
$offset = 0;
$buffer = '';
while($offset < $length) {
while ($offset < $length) {
$data = socket_read($this->connection, $length - $offset);
if ($data === false) {
throw new \Exception(sprintf(
'Failed to read from livestatus socket: %s',
socket_strerror(socket_last_error($this->connection))
));
throw new Exception(
sprintf(
'Failed to read from livestatus socket: %s',
socket_strerror(socket_last_error($this->connection))
)
);
}
$size = strlen($data);
$offset += $size;
@ -164,10 +194,13 @@ class Connection
}
}
if ($offset !== $length) {
throw new \Exception(sprintf(
'Got only %d instead of %d bytes from livestatus socket',
$offset, $length
));
throw new \Exception(
sprintf(
'Got only %d instead of %d bytes from livestatus socket',
$offset,
$length
)
);
}
return $buffer;
@ -185,10 +218,12 @@ class Connection
protected function assertPhpExtensionLoaded($name)
{
if (! extension_loaded($name)) {
throw new \Exception(sprintf(
'The extension "%s" is not loaded',
$name
));
throw new \Exception(
sprintf(
'The extension "%s" is not loaded',
$name
)
);
}
}
@ -213,24 +248,28 @@ class Connection
$this->connection = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (! @socket_connect($this->connection, $this->socket_host, $this->socket_port)) {
throw new \Exception(sprintf(
'Cannot connect to livestatus TCP socket "%s:%d": %s',
$this->socket_host,
$this->socket_port,
socket_strerror(socket_last_error($this->connection))
));
throw new \Exception(
sprintf(
'Cannot connect to livestatus TCP socket "%s:%d": %s',
$this->socket_host,
$this->socket_port,
socket_strerror(socket_last_error($this->connection))
)
);
}
socket_set_option($this->connection, SOL_TCP, TCP_NODELAY, 1);
}
protected function establishSocketConnection()
{
$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)) {
throw new \Exception(sprintf(
'Cannot connect to livestatus local socket "%s"',
$this->socket_path
));
throw new \Exception(
sprintf(
'Cannot connect to livestatus local socket "%s"',
$this->socket_path
)
);
}
}
@ -246,4 +285,3 @@ class Connection
$this->disconnect();
}
}

View File

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

View File

@ -1,35 +1,13 @@
<?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;
use Icinga\Authentication\Manager;
use Icinga\Authentication\Manager as AuthManager;
use Icinga\Application\Benchmark;
use Icinga\Exception;
use Icinga\Application\Config;
use Icinga\Pdf\File;
use Icinga\Web\Notification;
use Zend_Layout as ZfLayout;
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_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
*
@ -45,21 +28,16 @@ use Zend_Controller_Action_HelperBroker as ZfActionHelper;
* @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
* @package Icinga\Web
*/
class ActionController extends ZfController
{
/**
* The Icinga Config object is available in all controllers. This is the
* modules config for module action controllers.
*
* @var Config
*/
protected $config;
/**
* @var bool
*/
protected $replaceLayout = false;
/**
@ -84,20 +62,12 @@ class ActionController extends ZfController
*/
protected $action_name;
/**
* @var bool
*/
// @TODO(el): Should be true, is/was disabled for testing purpose
protected $handlesAuthentication = false;
/**
* @var bool
*/
protected $modifiesSession = false;
/**
* @var bool
*/
protected $allowAccess = false;
private $allowAccess = false;
/**
* The constructor starts benchmarking, loads the configuration and sets
@ -113,30 +83,22 @@ class ActionController extends ZfController
array $invokeArgs = array()
) {
Benchmark::measure('Action::__construct()');
$this->module_name = $request->getModuleName();
$this->module_name = $request->getModuleName();
$this->controller_name = $request->getControllerName();
$this->action_name = $request->getActionName();
$this->action_name = $request->getActionName();
$this->loadConfig();
$this->setRequest($request)
->setResponse($response)
->_setInvokeArgs($invokeArgs);
->setResponse($response)
->_setInvokeArgs($invokeArgs);
$this->_helper = new ZfActionHelper($this);
/*
* --------------------------------------------
* Authentication is disabled to test bootstrap
* --------------------------------------------
*
* @todo remove this!
*/
if ($this->handlesAuthentication() ||
Manager::getInstance(
AuthManager::getInstance(
null,
array(
"writeSession" => $this->modifiesSession
'writeSession' => $this->modifiesSession
)
)->isAuthenticated()
) {
@ -171,7 +133,7 @@ class ActionController extends ZfController
* Helper function creating a new widget
*
* @param string $name The widget name
* @param array|string $properties Optional widget properties
* @param string $properties Optional widget properties
*
* @return Widget\AbstractWidget
*/
@ -185,9 +147,8 @@ class ActionController extends ZfController
*
* TODO: This has not been implemented yet
*
* @param $uri
* @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
*/
@ -204,18 +165,31 @@ class ActionController extends ZfController
* @param string $permission Permission name
* @param string $object No idea what this should have been :-)
*
* @throws \Exception
* @return self
*/
final protected function assertPermission($permission, $object = null)
{
if (!$this->hasPermission($permission, $object)) {
if (! $this->hasPermission($permission, $object)) {
// TODO: Log violation, create dedicated Exception class
throw new \Exception('Permission denied');
}
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
*
@ -224,8 +198,7 @@ class ActionController extends ZfController
public function preDispatch()
{
Benchmark::measure('Action::preDispatch()');
if (!$this->allowAccess) {
if (! $this->allowAccess) {
$this->_request->setModuleName('default')
->setControllerName('authentication')
->setActionName('login')
@ -237,21 +210,27 @@ class ActionController extends ZfController
$this->view->controller_name = $this->controller_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');
}
/**
* @param $url
* @param array $params
*/
public function redirectNow($url, array $params = array())
{
if ($url instanceof Url) {
$url = $url->getRelative();
}
$this->_helper->Redirector->gotoUrlAndExit($url);
}
/**
* @return bool
*/
public function handlesAuthentication()
{
return $this->handlesAuthentication;
@ -265,8 +244,8 @@ class ActionController extends ZfController
protected function renderBenchmark()
{
return '<pre class="benchmark">'
. Benchmark::renderToHtml()
. '</pre>';
. Benchmark::renderToHtml()
. '</pre>';
}
/**
@ -284,7 +263,6 @@ class ActionController extends ZfController
{
Benchmark::measure('Action::postDispatch()');
// TODO: Move this elsewhere, this is just an ugly test:
if ($this->_request->getParam('filetype') === 'pdf') {
@ -293,40 +271,56 @@ class ActionController extends ZfController
require_once 'vendor/lessphp/lessc.inc.php';
$less = new \lessc;
$cssdir = dirname(ICINGA_LIBDIR) . '/public/css';
// 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');
/*
foreach ($app->moduleManager()->getLoadedModules() as $name => $module) {
if ($module->hasCss()) {
$css .= $less->compile(
'.icinga-module.module-'
. $name
. " {\n"
. file_get_contents($module->getCssFilename())
. "}\n\n"
);
}
}
*/
/*
foreach (\Icinga\Application\Icinga::app()->moduleManager()->getLoadedModules() as $name => $module) {
if ($module->hasCss()) {
$css .= $less->compile(
'.icinga-module.module-'
. $name
. " {\n"
. file_get_contents($module->getCssFilename())
. "}\n\n"
);
}
}
*/
// END of CSS test
$this->render(
$this->render(
null,
$this->_helper->viewRenderer->getResponseSegment(),
$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->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');
exit;
}
// END of PDF test
if ($this->_request->isXmlHttpRequest()) {
if ($this->replaceLayout || $this->_getParam('_render') === 'body') {
$this->_helper->layout()->setLayout('just-the-body');
@ -335,24 +329,23 @@ class ActionController extends ZfController
$this->_helper->layout()->setLayout('inline');
}
}
$notification = Notification::getInstance();
if ($notification->hasMessages()) {
$nhtml = '<ul class="notification">';
foreach ($notification->getMessages() as $msg) {
$nhtml .= '<li>['
. $msg->type
. '] '
. htmlspecialchars($msg->message);
. $msg->type
. '] '
. htmlspecialchars($msg->message);
}
$nhtml .= '</ul>';
$this->getResponse()->append('notification', $nhtml);
}
/*if (Session::getInstance()->show_benchmark) {
if (AuthManager::getInstance()->getSession()->get('show_benchmark')) {
Benchmark::measure('Response ready');
$this->getResponse()->append('benchmark', $this->renderBenchmark());
}*/
$this->_helper->layout()->benchmark = $this->renderBenchmark();
}
}
}

View File

@ -1,28 +1,8 @@
<?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;
use Icinga\Application\Config;
@ -84,7 +64,7 @@ class ModuleActionController extends ActionController
*/
protected function loadConfig()
{
$this->config = Config::getInstance()->{$this->module_name};
$this->config = Config::module($this->module_name);
}
/**

View File

@ -1,106 +1,47 @@
<?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;
use Icinga\Exception\ProgrammingError;
use Icinga\Application\Platform;
use Icinga\Application\Logger as Log;
use Icinga\Authentication\Manager as AuthManager;
/**
* Class Notification
* @package Icinga\Web
* // @TODO(eL): Use Notification not as Singleton but within request:
* <code>
* <?php
* $request->[getUser()]->notify('some message', Notification::INFO);
* </code>
*/
class Notification
{
/**
* @var Notification
*/
private static $instance;
protected static $instance;
protected $isCli = false;
/**
* @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)
{
self::getInstance()->addMessage($msg, 'info');
}
/**
* @param $msg
*/
public static function success($msg)
{
self::getInstance()->addMessage($msg, 'success');
}
/**
* @param $msg
*/
public static function warning($msg)
{
self::getInstance()->addMessage($msg, 'warning');
}
/**
* @param $msg
*/
public static function error($msg)
{
self::getInstance()->addMessage($msg, 'error');
}
/**
* @param $message
* @param string $type
* @throws \Icinga\Exception\ProgrammingError
*/
public function addMessage($message, $type = 'info')
protected function addMessage($message, $type = 'info')
{
if (!in_array(
if (! in_array(
$type,
array(
'info',
@ -108,8 +49,7 @@ class Notification
'warning',
'success'
)
)
) {
)) {
throw new ProgrammingError(
sprintf(
'"%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);
switch ($type) {
case 'info':
@ -135,8 +75,8 @@ class Notification
return;
}
$mo = (object)array(
'type' => $type,
$mo = (object) array(
'type' => $type,
'message' => $message,
);
@ -146,17 +86,11 @@ class Notification
$this->session->messages = $msgs;
}
/**
* @return bool
*/
public function hasMessages()
{
return !empty($this->session->messages);
return ! empty($this->session->messages);
}
/**
* @return mixed
*/
public function getMessages()
{
$msgs = $this->session->messages;
@ -164,24 +98,18 @@ class Notification
return $msgs;
}
/**
* Create a new Notification object
*/
final private function __construct()
{
//$this->session = new SessionNamespace('IcingaNotification');
//if (!is_array($this->session->messages)) {
$this->session = AuthManager::getInstance()->getSession();
if (!is_array($this->session->get('messages'))) {
$this->session->messages = array();
//}
}
if (Platform::isCli()) {
$this->cliFlag = true;
$this->is_cli = true;
}
}
/**
* @return Notification
*/
public static function getInstance()
{
if (self::$instance === null) {

View File

@ -8,31 +8,39 @@ use Icinga\Exception\ProgrammingError;
class Url
{
protected $params = array();
protected $url;
protected $path;
protected $baseUrl;
protected $request;
public function __construct($url, $params = null)
public function __construct($url, $params = null, $request = null)
{
if (($split = strpos($url, '?')) === false) {
$this->url = $url;
if (! empty($params)) {
$this->params = $params;
}
if ($request === null) {
$this->request = Icinga::app()->frontController()->getRequest();
} else {
$this->url = substr($url, 0, $split);
parse_str(substr($url, $split + 1), $urlParams);
$this->params = $urlParams;
if (! empty($params)) {
$this->params += $params;
// TODO: Test += behavior!
// 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);
$this->params = $urlParams;
}
}
if (! empty($params)) {
$this->setParams($params);
}
}
public static function create($url, $params = null)
public static function create($url, $params = null, $request = null)
{
$u = new Url($url, $params);
$u = new Url($url, $params, $request);
return $u;
}
@ -43,30 +51,15 @@ class Url
return $this;
}
public static function current()
public static function current($request = null)
{
$app = Icinga::app();
$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);
$url = new Url(null, null, $request);
return $url;
}
public function getScript()
public function getPath()
{
return $this->url;
return $this->path;
}
public function getRelative()
@ -79,9 +72,9 @@ class Url
$args[] = rawurlencode($name) . '=' . rawurlencode($value);
}
}
$url = vsprintf($this->url, $params);
$url = vsprintf($this->path, $params);
if (! empty($args)) {
$url .= '?' . implode('&', $args);
$url .= '?' . implode('&amp;', $args);
}
return $url;
}
@ -94,17 +87,19 @@ class Url
public function setParams($params)
{
$this->params = $params;
if ($params === null) {
$this->params = array();
} else {
$this->params = $params;
}
return $this;
}
public function set($key, $val)
public function getParams()
{
$this->params[$key] = $val;
return $this;
return $this->params;
}
public function hasParam($key)
{
return array_key_exists($key, $this->params);
@ -118,31 +113,42 @@ class Url
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()
{
if (! is_array($keys)) {
$keys = array($keys);
}
foreach ($keys as $key) {
if (array_key_exists($key, $this->params)) {
unset($this->params[$key]);
$args = func_get_args();
foreach ($args as $keys) {
if (! is_array($keys)) {
$keys = array($keys);
}
foreach ($keys as $key) {
if (array_key_exists($key, $this->params)) {
unset($this->params[$key]);
}
}
}
return $this;
}
public function without()
{
$url = clone($this);
$args = func_get_args();
return call_user_func_array(array($url, 'remove'), $args);
}
public function __toString()
{
$url = $this->getRelative();
$base = is_null($this->baseUrl)
? Icinga::app()->getView()->view->baseUrl()
$base = null === $this->baseUrl
? $this->request->getBaseUrl()
: $this->baseUrl;
if ($base === '') {
if ($base === '' && $url[0]!== '/') {
// Otherwise all URLs would be relative to wherever you are
$base = '/';
}
@ -152,3 +158,4 @@ class 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
// {{{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;
use Icinga\Exception\ProgrammingError;
@ -45,16 +25,16 @@ use Icinga\Exception\ProgrammingError;
*/
class Widget
{
/**
* Create a new widget
*
* @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
*/
public static function create($name, $options = array())
public static function create($name, $options = array(), $module_name = null)
{
$class = 'Icinga\\Web\\Widget\\' . ucfirst($name);
@ -67,7 +47,7 @@ class Widget
);
}
$widget = new $class($options);
$widget = new $class($options, $module_name);
return $widget;
}
}

View File

@ -8,6 +8,8 @@ namespace Icinga\Web\Widget;
use Icinga\Exception\ProgrammingError;
use Icinga\Web\Url;
use Countable;
/**
* Navigation tab widget
*
@ -17,7 +19,7 @@ use Icinga\Web\Url;
* @author Icinga-Web Team <info@icinga.org>
* @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
@ -201,25 +203,37 @@ class Tabs extends AbstractWidget implements \Countable
$special = array();
$special[] = $this->view()->qlink(
'PDF',
$this->view()->img('img/classic/application-pdf.png') . ' PDF',
Url::current(),
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(
'Basket',
$this->view()->img('img/classic/basket.png') . ' URL Basket',
Url::create('basket/add'),
array('url' => Url::current()->getRelative())
array('url' => Url::current()->getRelative()),
array('quote' => false)
);
$special[] = $this->view()->qlink(
'Dashboard',
$this->view()->img('img/classic/dashboard.png') . ' Dashboard',
Url::create('dashboard/addurl'),
array('url' => Url::current()->getRelative())
array('url' => Url::current()->getRelative()),
array('quote' => false)
);
// @todo rework auth
// $auth = Auth::getInstance();
// if ($this->specialActions && ! empty($special) && $auth->isAuthenticated() && $auth->getUsername() === 'admin') {
if ($this->specialActions) {
@ -241,10 +255,6 @@ class Tabs extends AbstractWidget implements \Countable
return $html;
}
/**
* Counting registered tabs
* @return int
*/
public function count()
{
return count($this->tabs);

View File

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

View File

@ -2,6 +2,7 @@
use Icinga\Web\ModuleActionController;
use Icinga\Web\Hook;
use Icinga\File\Csv;
use Icinga\Monitoring\Backend;
use Icinga\Application\Benchmark;
@ -83,9 +84,10 @@ class Monitoring_ListController extends ModuleActionController
$state_column = 'service_hard_state';
$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_problems',
'service_description',
'service_state' => $state_column,
'service_in_downtime',
@ -93,36 +95,12 @@ class Monitoring_ListController extends ModuleActionController
'service_handled',
'service_output',
'service_last_state_change' => $state_change_column
));
if ($search = $this->_getParam('search')) {
$this->_setParam('search', null);
if (strpos($search, '=') === false) {
$this->_setParam('service_description', $search);
} 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->preserve('sort')
->preserve('backend')
->preserve('extracolumns');
$this->view->sort = $this->_getParam('sort');
if ($this->view->compact) {
$this->_helper->viewRenderer('services-compact');
}
}
@ -181,19 +159,58 @@ class Monitoring_ListController extends ModuleActionController
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()
{
$tabs = $this->widget('tabs');
$tabs->add('services', array(
'title' => 'Services',
'title' => 'All services',
'icon' => 'img/classic/service.png',
'url' => 'monitoring/list/services',
));
$tabs->add('hosts', array(
'title' => 'Hosts',
'title' => 'All hosts',
'icon' => 'img/classic/server.png',
'url' => 'monitoring/list/hosts',
));
/*
$tabs->add('hostgroups', array(
'title' => 'Hostgroups',
'icon' => 'img/classic/servers-network.png',
@ -214,6 +231,7 @@ class Monitoring_ListController extends ModuleActionController
'icon' => 'img/classic/servers-network.png',
'url' => 'monitoring/list/contactgroups',
));
*/
return $tabs;
}
}

View File

@ -5,7 +5,8 @@
use Icinga\Monitoring\Backend;
use Icinga\Web\ModuleActionController;
use Icinga\Web\Hook;
use Icinga\Application\Benchmark;
use Icinga\Monitoring\Object\Host;
use Icinga\Monitoring\Object\Service;
/**
* Class Monitoring_ShowController
@ -26,13 +27,18 @@ class Monitoring_ShowController extends ModuleActionController
{
$host = $this->_getParam('host');
$service = $this->_getParam('service');
$this->backend = Backend::getInstance($this->_getParam('backend'));
$object = null;
// TODO: Do not allow wildcards in names!
if ($host !== null) {
// TODO: $this->assertPermission('host/read', $host);
if ($this->action_name !== 'host' && $service !== null && $service !== '*') {
// TODO: $this->assertPermission('service/read', $service);
$object = Service::fetch($this->backend, $host, $service);
} else {
$object = Host::fetch($this->backend, $host);
}
}
if ($service !== null) {
// TODO: $this->assertPermission('service/read', $service);
}
// TODO: don't allow wildcards
$this->backend = Backend::getInstance($this->_getParam('backend'));
if ($service !== null && $service !== '*') {
@ -42,30 +48,14 @@ class Monitoring_ShowController extends ModuleActionController
$this->view->host = $this->backend->fetchHost($host, true);
}
$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();
// 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})~';
}
$this->prepareTicketHook();
}
/**
@ -144,6 +134,8 @@ class Monitoring_ShowController extends ModuleActionController
->where('object_type', 'service')
->fetchPairs();
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('object_type', 'host')
->fetchPairs();
$this->view->object->prefetch();
$this->prepareGrapherHook();
}
/**
@ -250,9 +244,6 @@ class Monitoring_ShowController extends ModuleActionController
*/
public function historyAction()
{
if ($this->view->host) {
$this->view->tabs->activate('history')->enableSpecialActions();
}
$this->view->history = $this->backend->select()
->from(
'eventHistory',
@ -269,7 +260,6 @@ class Monitoring_ShowController extends ModuleActionController
)
)->applyRequest($this->_request);
$this->view->preserve = $this->view->history->getAppliedFilter()->toParams();
if ($this->_getParam('dump') === 'sql') {
echo '<pre>' . htmlspecialchars($this->view->history->getQuery()->dump()) . '</pre>';
@ -278,6 +268,7 @@ class Monitoring_ShowController extends ModuleActionController
if ($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()
{
$this->_setParam('service', null);
// Ugly and slow:
$this->view->services = $this->view->action(
'services',
@ -295,6 +287,9 @@ class Monitoring_ShowController extends ModuleActionController
//'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()
{
$this->view->tabs->activate('ticket')->enableSpecialActions();
$id = $this->_getParam('ticket');
// Still hardcoded, TODO: get URL:
if (Hook::has('ticket')) {
// TODO: Still hardcoded, should ask for URL:
$id = $this->_getParam('ticket');
$ticketModule = 'rt';
$this->render();
$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
* @return \Icinga\Web\Widget\AbstractWidget
*/
protected function createTabs()
{
$object = $this->view->object;
$tabs = $this->widget('tabs');
if (!$this->view->host) {
return $tabs;
@ -335,11 +371,10 @@ class Monitoring_ShowController extends ModuleActionController
if ($backend = $this->_getParam('backend')) {
$params['backend'] = $backend;
}
if (isset($this->view->service)) {
$params['service'] = $this->view->service->service_description;
$hostParams = $params + array('active' => 'host');
} else {
$hostParams = $params;
if ($object instanceof Service) {
$params['service'] = $object->service_description;
} elseif ($service = $this->_getParam('service')) {
$params['service'] = $service;
}
$tabs->add(
'host',
@ -347,7 +382,7 @@ class Monitoring_ShowController extends ModuleActionController
'title' => 'Host',
'icon' => 'img/classic/server.png',
'url' => 'monitoring/show/host',
'urlParams' => $hostParams,
'urlParams' => $params,
)
);
$tabs->add(
@ -390,13 +425,16 @@ class Monitoring_ShowController extends ModuleActionController
)
);
}
/*
$tabs->add('contacts', array(
'title' => 'Contacts',
'icon' => 'img/classic/customer.png',
'url' => 'monitoring/detail/contacts',
'urlParams' => $params,
));
$tabs->activate($this->action_name)->enableSpecialActions();
/**
$tabs->add('contacts', array(
'title' => 'Contacts',
'icon' => 'img/classic/customer.png',
'url' => 'monitoring/detail/contacts',
'urlParams' => $params,
));
*/
// TODO: Inventory 'img/classic/facts.gif'
// Ticket 'img/classic/ticket.gif'

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

View File

@ -1,39 +1,34 @@
<?php if (empty($this->services)): ?>
<div class="alert alert-info fullpage_infobox">
Sorry, no services found for this search
</div>
<?php return; endif; ?>
<?
$services = $this->services->paginate();
?>
<table class="services action">
<thead>
<tr>
<th style="width: 6em;" >State</th>
<th >Service</th>
</tr>
</thead>
<tbody>
<?php foreach ($services as $service): ?>
<?php
$count = $services->count();
if (! $count) {
echo '- no object is matching this filter -';
return;
}
$services->paginate();
?><table class="services action">
<thead>
<tr>
<th style="width: 6em;" >State</th>
<th >Service</th>
</tr>
</thead>
<tbody>
<?php foreach ($services->fetchAll() as $service):
$icons = array();
if ($service->service_acknowledged) {
$icons['ack.gif'] = 'Problem has been acknowledged';
}
if (! empty($service->service_downtimes_with_info)) {
$icons['downtime.gif'] = implode("\n",
$this->resolveComments($service->service_downtimes_with_info)
);
if ($service->service_in_downtime) {
$icons['downtime.gif'] = 'Service is in a scheduled downtime';
}
if (! empty($service->service_comments_with_info)) {
$icons['comment.gif'] = implode("\n",
$this->resolveComments($service->service_comments_with_info)
);
if ($service->host_problems) {
$icons['server.png'] = 'This services host has a problem';
}
$state_classes = array($this->monitoringState($service, 'service'));
@ -53,21 +48,55 @@ if (isset($this->preserve['backend'])) {
$params['backend'] = $this->preserve['backend'];
}
?>
<tr class="<?= implode(' ', $state_classes) ?>">
<td class="state" title="<?= $state_title ?>">
<?= $this->qlink(
$this->timeSince($service->service_last_state_change),
'monitoring/show/history', $params, array('quote' => false)) ?>
</td>
<td>
<?= $this->qlink(
$service->host_name,
'monitoring/show/host',
$params
); ?>:
<?= $this->qlink($service->service_description, 'monitoring/show/service', $params, array('class' => 'row-action')) ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<tr class="<?= implode(' ', $state_classes) ?>">
<td class="state" title="<?= $state_title ?>"><?=
$this->qlink(
$this->timeSince($service->service_last_state_change),
'monitoring/show/history',
$params,
array('quote' => false)
) ?></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,
'monitoring/show/host',
$params
) . ': ' . $this->qlink(
$service->service_description,
'monitoring/show/service',
$params,
array('class' => 'row-action')
) ?></td>
</tr>
<?php endforeach ?>
</tbody>
</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 ?>
<?php
$this->services->limit(10);
$services = $this->services->paginate();
$paginator = $services->paginate();
function getRowProperties(&$service, &$last_host, $scope) {
if ($last_host !== $service->host_name) {
@ -21,10 +21,14 @@ function getRowProperties(&$service, &$last_host, $scope) {
$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';
}
if ($service->host_problems) {
$icons['server.png'] = 'This services host has a problem';
}
$state_classes = array($scope->monitoringState($service));
if ($service->service_handled) {
@ -63,9 +67,9 @@ if (isset($_GET['dir'])) {
}
?>
<div class="dontprint">
<?php if (! empty($fparams)): ?>
<? if (! empty($fparams)): ?>
<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(
'x',
'monitoring/list/services',
@ -74,9 +78,9 @@ if (isset($_GET['dir'])) {
'style' => array('color' => 'red')
)
) ?> <?= $this->escape("$k = $v") ?></br>
<?php endforeach; ?>
<? endforeach ?>
</div>
<?php endif; ?>
<? endif ?>
<form method="get" action="<?= $this->qUrl(
'monitoring/list/services?' . http_build_query(
$this->services->getAppliedFilter()->toParams()
@ -93,11 +97,21 @@ Sort by <?= $this->formSelect(
array(
'severity' => 'Severity',
'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',
'service_description' => 'Service',
)
) ?>
<input type="text" placeholder="Add filter..." name="search" />
<?= $this->formText(
'search',
$this->search,
array(
'placeholder' => 'Add filllter...',
)
) ?>
</form>
</div>
<?php if (empty($services)): ?>
@ -106,11 +120,11 @@ Sort by <?= $this->formSelect(
Sorry, no services found for this search
</div>
<?php return; endif; ?>
<?= $this->paginationControl($services, null, null, array('preserve' => $this->preserve )); ?>
<?php return; endif ?>
<?= $this->paginationControl($paginator, null, null, array('preserve' => $this->preserve )); ?>
<table class="action">
<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); ?>
<tr class="<?= implode(' ', $state_classes) ?>">
<td class="state" title="<?= $state_title ?>">
@ -148,6 +162,9 @@ Sort by <?= $this->formSelect(
</span>
<?= $graph ?>
</td>
<? foreach ($this->extraColumns as $col): ?>
<td><?= $this->escape($service->$col) ?></td>
<? endforeach ?>
</tr>
<?php endforeach; ?>
</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();
foreach ($this->comments as $comment) {
if ($this->ticket_pattern) {
foreach ($object->comments as $comment) {
if ($ticket_pattern) {
$text = preg_replace(
$this->ticket_pattern,
$this->ticket_link,
$ticket_pattern,
$ticket_link,
$this->escape($comment->comment_data)
);
} else {
@ -29,4 +30,3 @@ foreach ($this->comments as $comment) {
<blockquote> <?= implode('<br />', $list) ?></blockquote>
</div>
<?php } ?>

View File

@ -30,4 +30,4 @@
echo '<strong>Contactgroups:</strong> ';
echo implode(', ', $contactGroupList);
}
?>
?>

View File

@ -19,4 +19,4 @@
</table>
</div>
</div>
<?php } ?>
<?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(
'host' => $this->host,
'service' => $this->service,
'tabs' => $this->tabs
)); ?><?php
$this->history->limit(10);
$history = $this->history->paginate();
<?= $this->render('show/components/header.phtml') ?>
<?php
$history->limit(10);
$hhistory = $this->history->paginate();
?>
<?php if (empty($history)): ?>
<?php if (empty($hhistory)): ?>
There are no matching history entries right now
<?php else: ?>
<?= $this->paginationControl($history, null, null, array('preserve' => $this->preserve)); ?>
<?= $this->paginationControl($hhistory, null, null, array('preserve' => $preserve)); ?>
<table class="paginatable">
<tbody>
<?php foreach ($history as $event): ?>
<?php foreach ($hhistory as $event): ?>
<?php
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>
<?php if (! isset($this->host)): ?>
<? if (! $object): ?>
<td><?= $this->escape($event->host_name) ?></td>
<?php endif; ?>
<?php if (! isset($this->service)): ?>
<td><?php if (isset($this->host)): ?>
<? endif ?>
<? if (! $object instanceof Icinga\Monitoring\Object\Service): ?>
<td><? if ($object): ?>
<?= $this->qlink(
$event->service_description,
'monitoring/show/service',
array(
'host' => $this->host->host_name,
'host' => $object->host_name,
'service' => $event->service_description
)
) ?>
<?php else: ?>
<? else: ?>
<?= $this->escape($event->service_description) ?>
<?php endif; ?>
<? endif ?>
</td>
<?php endif; ?>
<? endif ?>
<td style="padding: 0.2em;"><?php
$imgparams = array(
@ -83,17 +80,17 @@ switch ($event->type) {
break;
}
?>
<?php if ($event->attempt !== null): ?>
<? if ($event->attempt !== null): ?>
[ <?= $event->attempt ?>/<?=$event->max_attempts ?> ]
<?php endif; ?>
<?= $this->ticket_pattern ? preg_replace(
$this->ticket_pattern,
$this->ticket_link,
<? endif ?>
<?= $ticket_pattern ? preg_replace(
$ticket_pattern,
$ticket_link,
$this->pluginOutput($event->output)
) : $this->pluginOutput($event->output) ?>
</td>
</tr>
<?php endforeach; ?>
<? endforeach ?>
</tbody>
</table>
<?php endif; ?>
<? endif ?>

View File

@ -1,31 +1,10 @@
<?= $this->partial('show/header.phtml', array(
'host' => $this->host,
'service' => $this->service,
'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/header.phtml') ?>
<?= $this->render('show/components/output.phtml') ?>
<?= $this->render('show/components/servicegroups.phtml') ?>
<?= $this->render('show/components/contacts.phtml') ?>
<?= $this->render('show/components/comments.phtml') ?>
<?php foreach ($this->customvars as $name => $value): ?>
<b><?= $this->escape($name) ?>:</b> <?= $this->escape($value) ?><br />
<?php endforeach; ?>
<?= $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->render('show/components/customvars.phtml') ?>
<?= $this->render('show/components/perfdata.phtml') ?>
<?= $this->render('show/components/checkstatistics.phtml') ?>
<?= $this->render('show/components/command.phtml') ?>
<?= $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 ?>
<?php endif; ?>
<?php if (empty($this->summary)): ?>
<? endif ?>
<? if (empty($this->summary)): ?>
There are no such services right now
<?php else: ?>
<? else: ?>
<?php
$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)); ?>
<?php endif; ?>
<? endif ?>
<table class="pivot action">
<thead>
<tr>
@ -20,11 +20,11 @@ $now = time();
<th style="width: 5em;">Critical</th>
<th style="width: 5em;">Unknown</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>
</thead>
<tbody>
<?php foreach ($this->summary as $row): ?>
<? foreach ($this->summary as $row): ?>
<?php
$class_ok = '';
@ -149,19 +149,19 @@ if (isset($row->hostgroup_name)) {
}
?>
<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>
<?php else: ?>
<? else: ?>
<?= $this->timeSince($row->last_state_change) ?>
<?php endif; ?></td>-->
<? endif ?></td>-->
<td style="text-align: left;"><?= $name_html ?></td>
<td class="<?= $class_critical ?>"><?= $html_critical ?></td>
<td class="<?= $class_unknown ?>"><?= $html_unknown ?></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>
<?php endforeach; ?>
<? endforeach ?>
</tbody>
</table>
<?php endif; ?>
<?php if ($this->compact): ?><a href="<?= $this->baseUrl('monitoring/summary/group') ?>">more</a><?php endif; ?>
<? 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_acknowledged',
'host_check_command',
'host_check_command',
'host_last_state_change',
'host_alias',
'host_output',
@ -138,8 +139,8 @@ class AbstractBackend implements DatasourceInterface
'host_flap_detection_enabled',
'host_is_flapping',
'host_percent_state_change',
'host_latency',
'host_execution_time',
'host_check_latency',
'host_check_execution_time',
'host_scheduled_downtime_depth',
'host_failure_prediction_enabled',
'host_process_performance_data',

View File

@ -19,6 +19,8 @@ abstract class AbstractQuery extends Query
protected $joinedVirtualTables = array();
protected $object_id = 'object_id';
protected $host_id = 'host_id';
protected $service_id = 'service_id';
protected $hostgroup_id = 'hostgroup_id';
protected $servicegroup_id = 'servicegroup_id';
@ -31,10 +33,18 @@ abstract class AbstractQuery extends Query
$this->prefix = $this->ds->getPrefix();
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 ($column as $key => & $value) {
foreach ($columns as $key => & $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
// ($leftcol s.host_object_id vs h.host_object_id
$leftcol = 's.' . $type . '_object_id';
if ($this->hasJoinedVirtualTable('services')) {
$leftcol = 's.' . $type . '_object_id';
} else {
$leftcol = 'h.' . $type . '_object_id';
}
$joinOn = $leftcol
. ' = '
. $alias
@ -100,7 +114,7 @@ abstract class AbstractQuery extends Query
protected function beforeCreatingSelectQuery()
{
$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()
@ -274,6 +288,10 @@ abstract class AbstractQuery extends Query
// Go through all given values
foreach ($value as $val) {
if ($val === '') {
// TODO: REALLY??
continue;
}
// Value starting with minus: negation
if ($val[0] === '-') {
$val = substr($val, 1);

View File

@ -8,15 +8,16 @@ class CommentQuery extends AbstractQuery
'comments' => array(
'comment_data' => 'cm.comment_data',
'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",
),
'hosts' => array(
'host_name' => 'ho.name1',
),
'services' => array(
'service_host_name' => 'so.name1',
'service_description' => 'so.name2',
'service_host_name' => 'so.name1 COLLATE latin1_general_ci',
'service_description' => 'so.name2 COLLATE latin1_general_ci',
)
);

View File

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

View File

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

View File

@ -12,9 +12,10 @@ class CustomvarQuery extends AbstractQuery
'varvalue' => 'cvs.varvalue',
),
'objects' => array(
'host_name' => 'cvo.name1',
'service_description' => 'cvo.name2',
'contact_name' => 'cvo.name1',
'host_name' => 'cvo.name1 COLLATE latin1_general_ci',
'service_host_name' => 'cvo.name1 COLLATE latin1_general_ci',
'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 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(
'eventhistory' => array(
'host' => 'eho.name1',
'service' => 'eho.name2',
'host_name' => 'eho.name1',
'service_description' => 'eho.name2',
'host' => 'eho.name1 COLLATE latin1_general_ci',
'service' => 'eho.name2 COLLATE latin1_general_ci',
'host_name' => 'eho.name1 COLLATE latin1_general_ci',
'service_description' => 'eho.name2 COLLATE latin1_general_ci',
'object_type' => "CASE WHEN eho.objecttype_id = 1 THEN 'host' ELSE 'service' END",
'timestamp' => 'UNIX_TIMESTAMP(eh.state_time)',
'raw_timestamp' => 'eh.state_time',
@ -69,7 +69,8 @@ class EventHistoryQuery extends AbstractQuery
'type' => "('dt_start')",
'state' => '(NULL)',
'state_type' => '(NULL)',
'output' => "CONCAT('[', author_name, '] ', comment_data)",
// 'output' => "CONCAT('[', author_name, '] ', comment_data)",
'output' => "('[' || author_name || '] ' || comment_data)",
'attempt' => '(NULL)',
'max_attempts' => '(NULL)',
)
@ -90,7 +91,8 @@ class EventHistoryQuery extends AbstractQuery
'type' => "('dt_end')",
'state' => '(NULL)',
'state_type' => '(NULL)',
'output' => "CONCAT('[', author_name, '] ', comment_data)",
// 'output' => "CONCAT('[', author_name, '] ', comment_data)",
'output' => "('[' || author_name || '] ' || comment_data)",
'attempt' => '(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)",
'state' => '(NULL)',
'state_type' => '(NULL)',
'output' => "CONCAT('[', author_name, '] ', comment_data)",
// 'output' => "CONCAT('[', author_name, '] ', comment_data)",
'output' => "('[' || author_name || '] ' || comment_data)",
'attempt' => '(NULL)',
'max_attempts' => '(NULL)',
)
@ -121,14 +124,39 @@ class EventHistoryQuery extends AbstractQuery
}
if ($end !== null) {
$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(
array('cn' => $this->prefix . 'contactnotifications'),
array(
'notification_id' => 'notification_id',
'cnt' => 'COUNT(*)',
'contacts' => 'GROUP_CONCAT(c.alias)',
'notification_id' => 'notification_id'
'contacts' => $concat_contacts
)
)->join(
array('c' => $this->prefix . 'contacts'),
@ -144,7 +172,8 @@ class EventHistoryQuery extends AbstractQuery
'type' => "('notify')",
'state' => 'state',
'state_type' => '(NULL)',
'output' => "CONCAT('[', cndetails.contacts, '] ', n.output)",
// 'output' => "CONCAT('[', cndetails.contacts, '] ', n.output)",
'output' => "('[' || cndetails.contacts || '] ' || n.output)",
'attempt' => '(NULL)',
'max_attempts' => '(NULL)',
)
@ -238,4 +267,4 @@ class EventHistoryQuery extends AbstractQuery
$query->where('object_id IN (?)', $objectId);
}
}
}
}

View File

@ -6,11 +6,12 @@ class HostgroupQuery extends AbstractQuery
{
protected $columnMap = array(
'hostgroups' => array(
'hostgroup_name' => 'hgo.name1',
'hostgroup_name' => 'hgo.name1 COLLATE latin1_general_ci',
'hostgroup_alias' => 'hg.alias',
'id' => 'hg.hostgroup_id',
),
'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(
'servicegroups' => array(
'servicegroup_name' => 'sgo.name1',
'servicegroup_name' => 'sgo.name1 COLLATE latin1_general_ci',
'servicegroup_alias' => 'sg.alias',
),
'services' => array(
'host_name' => 'so.name1',
'service_description' => 'so.name2'
'host_name' => 'so.name1 COLLATE latin1_general_ci',
'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(
array('sgm' => $this->prefix . 'servicegroup_members'),
'sgm.servicegroup_id = sg.servicegroup_id',
'sgm.' . $this->servicegroup_id . ' = sg.' . $this->servicegroup_id,
array()
)->join(
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()
);
}

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,13 +8,13 @@ class StatusQuery extends AbstractQuery
protected $columnMap = array(
'hosts' => array(
'host' => 'ho.name1',
'host_name' => 'ho.name1',
'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',
'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',
@ -53,8 +53,8 @@ class StatusQuery extends AbstractQuery
'host_flap_detection_enabled' => 'hs.flap_detection_enabled',
'host_is_flapping' => 'hs.is_flapping',
'host_percent_state_change' => 'hs.percent_state_change',
'host_latency' => 'hs.latency',
'host_execution_time' => 'hs.execution_time',
'host_check_latency' => 'hs.latency',
'host_check_execution_time' => 'hs.execution_time',
'host_scheduled_downtime_depth' => 'hs.scheduled_downtime_depth',
'host_failure_prediction_enabled' => 'hs.failure_prediction_enabled',
'host_process_performance_data' => 'hs.process_performance_data',
@ -102,12 +102,15 @@ class StatusQuery extends AbstractQuery
'hostgroups' => array(
'hostgroups' => 'hgo.name1',
),
'servicegroups' => array(
'servicegroups' => 'sgo.name1',
),
'services' => array(
'service_host_name' => 'so.name1',
'service' => 'so.name2',
'service_description' => 'so.name2',
'service_display_name' => 's.display_name',
'service_icon_image' => 's.icon_image',
'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',
@ -122,13 +125,19 @@ class StatusQuery extends AbstractQuery
'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_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_check_command' => 'ss.check_command',
'service_last_check' => 'UNIX_TIMESTAMP(ss.last_check)',
'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_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(
'problems' => 'CASE WHEN ss.current_state = 0 THEN 0 ELSE 1 END',
@ -165,7 +174,7 @@ class StatusQuery extends AbstractQuery
END
END
END',
)
),
);
public function group($col)
@ -190,14 +199,14 @@ class StatusQuery extends AbstractQuery
array('ho' => $this->prefix . 'objects'),
array()
)->join(
array('hs' => $this->prefix . 'hoststatus'),
'ho.object_id = hs.host_object_id AND ho.is_active = 1',
array()
)->join(
array('h' => $this->prefix . 'hosts'),
'hs.host_object_id = h.host_object_id',
array()
);
array('hs' => $this->prefix . 'hoststatus'),
'ho.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,
@ -278,20 +287,22 @@ class StatusQuery extends AbstractQuery
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('hgo' => $this->prefix . 'objects'),
'hgo.' . $this->object_id . ' = hg.' . $this->hostgroup_id
. ' AND hgo.is_active = 1',
array()
);
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

@ -23,6 +23,10 @@ class StatusQuery extends AbstractQuery
'host_accepts_passive_checks' => 'host_accept_passive_checks',
'host_last_state_change',
'host_problems' => 'is_flapping',
'service_in_downtime' => 'is_flapping',
'service_handled' => 'is_flapping',
// Service config
'service_description' => 'description',
'service_display_name' => 'display_name',
@ -37,12 +41,34 @@ class StatusQuery extends AbstractQuery
'service_last_state_change' => 'last_state_change',
// Service comments
'comments_with_info',
'downtimes_with_info',
//'comments_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()
{
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;
use Icinga\Application\Config;
use Icinga\Web\Session;
use Exception;
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_timestamp',
'comment_type',
'host_name',
'service_host_name',
'service_description',
);
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\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
{
/**
* Stores the backend-specific Query Object
* @var AbstractQuery
*/
protected $query;
/**
* All the columns provided by this View MUST be specified here
* @var Array
*/
protected $availableColumns = array();
/**
* Columns available for search only but not in result sets
* @var 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();
/**
* Whether this view provides a specific column name
*
* @param string $column Column name
* @return bool
*/
public function hasColumn($column)
{
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()
{
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)
{
return $this->applyRequestFilters($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)
{
return $this->order(
// TODO: Use first sortDefaults entry if available, fall back to
// column if not
$request->getParam('sort', $this->availableColumns[0]),
$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)
{
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)) {
$this->where($key, $value);
}
@ -50,7 +176,16 @@ class MonitoringView extends AbstractQuery
}
// 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)
{
foreach ($filters as $col => $filter) {
@ -59,11 +194,26 @@ class MonitoringView extends AbstractQuery
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()
{
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)
{
if (isset($this->sortDefaults[$col]['default_dir'])) {
@ -72,11 +222,21 @@ class MonitoringView extends AbstractQuery
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()
{
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;
$query = new $class($this->ds, $this->columns);

View File

@ -5,9 +5,11 @@ namespace Icinga\Monitoring\View;
class StatusView extends MonitoringView
{
protected $query;
// protected $searchColumn = 'host'; -> besser in der Query, 'search' mitgeben
protected $availableColumns = array(
// Hosts
'host',
'host_name',
'host_display_name',
'host_alias',
@ -29,9 +31,15 @@ class StatusView extends MonitoringView
'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',
// Services
'service',
'service_description',
'service_display_name',
@ -49,7 +57,15 @@ class StatusView extends MonitoringView
'service_does_active_checks',
'service_accepts_passive_checks',
'service_last_state_change',
'service_last_hard_state',
'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
'problems',
@ -66,6 +82,12 @@ class StatusView extends MonitoringView
'host_name' => array(
'columns' => array(
'host_name',
),
'default_dir' => self::SORT_ASC
),
'service_host_name' => array(
'columns' => array(
'service_host_name',
'service_description'
),
'default_dir' => self::SORT_ASC
@ -86,6 +108,7 @@ class StatusView extends MonitoringView
'severity' => array(
'columns' => array(
'severity',
'host_name',
'service_last_state_change',
),
'default_dir' => self::SORT_DESC

View File

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

View File

@ -1,13 +1,21 @@
<?php
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
* 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()
{
$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
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
* 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(os.path.join(report_directory,
'phpunit_results.xml'))
command_options.append('--coverage-clover')
command_options.append('--coverage-html')
command_options.append(os.path.join(report_directory,
'phpunit_coverage.xml'))
'php_html_coverage'))
if options.include:
command_options.append('--filter')
command_options.append(options.include)