Move libraries from incubator to working tree for evaluation
Add all untested files from incubator's library/Icinga to working tree library/Icinga refs #4257
This commit is contained in:
parent
1b600a8dd3
commit
54ccb9b12a
|
@ -0,0 +1,268 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Icinga Application Bootstrap class
|
||||
*
|
||||
* @package Icinga\Application
|
||||
*/
|
||||
namespace Icinga\Application;
|
||||
|
||||
use Icinga\Application\Modules\Manager as ModuleManager;
|
||||
use Icinga\Application\Platform;
|
||||
use Zend_Loader_Autoloader as ZendLoader;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
|
||||
/**
|
||||
* This class bootstraps a thin Icinga application layer
|
||||
*
|
||||
* Usage example for CLI:
|
||||
* <code>
|
||||
* use Icinga\Application\Cli;
|
||||
* Cli::start();
|
||||
* </code>
|
||||
*
|
||||
* Usage example for Icinga Web application:
|
||||
* <code>
|
||||
* use Icinga\Application\Web;
|
||||
* Web::start()->dispatch();
|
||||
* </code>
|
||||
*
|
||||
* Usage example for Icinga-Web 1.x compatibility mode:
|
||||
* <code>
|
||||
* use Icinga\Application\LegacyWeb;
|
||||
* LegacyWeb::start()->setIcingaWebBasedir(ICINGAWEB_BASEDIR)->dispatch();
|
||||
* </code>
|
||||
*
|
||||
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
|
||||
* @author Icinga-Web Team <info@icinga.org>
|
||||
* @package Icinga\Application
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
|
||||
*/
|
||||
abstract class ApplicationBootstrap
|
||||
{
|
||||
protected $loader;
|
||||
protected $libdir;
|
||||
protected $config;
|
||||
protected $configFile;
|
||||
protected $appdir;
|
||||
protected $moduleManager;
|
||||
protected $isCli = false;
|
||||
protected $isWeb = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* The constructor is protected to avoid incorrect usage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function __construct($configFile = null)
|
||||
{
|
||||
$this->checkPrerequisites();
|
||||
$this->libdir = realpath(dirname(dirname(dirname(__FILE__))));
|
||||
|
||||
require $this->libdir . '/Icinga/Application/Loader.php';
|
||||
if (! defined('ICINGA_LIBDIR')) {
|
||||
define('ICINGA_LIBDIR', $this->libdir);
|
||||
}
|
||||
// TODO: Make appdir configurable for packagers
|
||||
$this->appdir = realpath(dirname($this->libdir) . '/application');
|
||||
if (! defined('ICINGA_APPDIR')) {
|
||||
define('ICINGA_APPDIR', $this->appdir);
|
||||
}
|
||||
|
||||
$this->loader = Loader::register();
|
||||
$this->registerZendAutoloader();
|
||||
Benchmark::measure('Bootstrap, autoloader registered');
|
||||
|
||||
Icinga::setApp($this);
|
||||
|
||||
// Unfortunately this is needed to get the Zend Plugin loader working:
|
||||
set_include_path(
|
||||
implode(
|
||||
PATH_SEPARATOR,
|
||||
array($this->libdir, get_include_path())
|
||||
)
|
||||
);
|
||||
|
||||
if ($configFile === null) {
|
||||
$configFile = dirname($this->libdir) . '/config/icinga.ini';
|
||||
}
|
||||
$this->configFile = $configFile;
|
||||
require_once dirname(__FILE__) . '/functions.php';
|
||||
}
|
||||
|
||||
abstract protected function bootstrap();
|
||||
|
||||
public function moduleManager()
|
||||
{
|
||||
if ($this->moduleManager === null) {
|
||||
$this->moduleManager = new ModuleManager($this);
|
||||
}
|
||||
return $this->moduleManager;
|
||||
}
|
||||
|
||||
public function getLoader()
|
||||
{
|
||||
return $this->loader;
|
||||
}
|
||||
|
||||
protected function loadEnabledModules()
|
||||
{
|
||||
$this->moduleManager()->loadEnabledModules();
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isCli()
|
||||
{
|
||||
return $this->isCli;
|
||||
}
|
||||
|
||||
public function isWeb()
|
||||
{
|
||||
return $this->isWeb;
|
||||
}
|
||||
|
||||
public function getApplicationDir($subdir = null)
|
||||
{
|
||||
$dir = $this->appdir;
|
||||
if ($subdir !== null) {
|
||||
$dir .= '/' . ltrim($subdir, '/');
|
||||
}
|
||||
return $dir;
|
||||
}
|
||||
|
||||
public function hasModule($name)
|
||||
{
|
||||
return $this->moduleManager()->hasLoaded($name);
|
||||
}
|
||||
|
||||
public function getModule($name)
|
||||
{
|
||||
return $this->moduleManager()->getModule($name);
|
||||
}
|
||||
|
||||
public function loadModule($name)
|
||||
{
|
||||
return $this->moduleManager()->loadModule($name);
|
||||
}
|
||||
|
||||
public function getConfig()
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
public static function start($config = null)
|
||||
{
|
||||
$class = get_called_class();
|
||||
$obj = new $class();
|
||||
$obj->bootstrap();
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the Zend Autoloader
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function registerZendAutoloader()
|
||||
{
|
||||
require_once 'Zend/Loader/Autoloader.php';
|
||||
ZendLoader::getInstance();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether we have all we need
|
||||
*
|
||||
* Pretty useless right now as a namespaces class would not work
|
||||
* with PHP 5.3
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function checkPrerequisites()
|
||||
{
|
||||
if (version_compare(phpversion(), '5.3.0', '<') === true) {
|
||||
die('PHP > 5.3.0 required');
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given PHP extension is available
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function hasExtension($name)
|
||||
{
|
||||
if (!extension_loaded($name)) {
|
||||
if (! @ dl($name)) {
|
||||
throw new ConfigurationError(
|
||||
sprintf(
|
||||
'The PHP extension %s is not available',
|
||||
$name
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load Configuration
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function loadConfig()
|
||||
{
|
||||
// TODO: add an absolutely failsafe config loader
|
||||
if (! @is_readable($this->configFile)) {
|
||||
throw new \Exception('Cannot read config file: ' . $this->configFile);
|
||||
}
|
||||
$this->config = Config::getInstance($this->configFile);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configure cache settings
|
||||
*
|
||||
* TODO: Right now APC is hardcoded, make this configurable
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function configureCache()
|
||||
{
|
||||
// TODO: Provide Zend_Cache_Frontend_File for statusdat
|
||||
//$this->cache = \Zend_Cache::factory('Core', 'Apc');
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error handling configuration
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function configureErrorHandling()
|
||||
{
|
||||
if ($this->config->global->environment == 'development') {
|
||||
error_reporting(E_ALL | E_NOTICE);
|
||||
ini_set('display_startup_errors', 1);
|
||||
ini_set('display_errors', 1);
|
||||
}
|
||||
Logger::create($this->config->logging);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set timezone settings
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function setTimezone()
|
||||
{
|
||||
date_default_timezone_set(
|
||||
$this->config->{'global'}->get('timezone', 'UTC')
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,293 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Icinga\Application\Benchmark class
|
||||
*/
|
||||
namespace Icinga\Application;
|
||||
|
||||
use Icinga\Util\Format;
|
||||
|
||||
/**
|
||||
* This class provides a simple and lightweight benchmark class
|
||||
*
|
||||
* <code>
|
||||
* Benchmark::measure('Program started');
|
||||
* // ...do something...
|
||||
* Benchmark::measure('Task finieshed');
|
||||
* Benchmark::dump();
|
||||
* </code>
|
||||
*/
|
||||
class Benchmark
|
||||
{
|
||||
const TIME = 0x01;
|
||||
const MEMORY = 0x02;
|
||||
|
||||
protected static $instance;
|
||||
protected $start;
|
||||
protected $measures = array();
|
||||
|
||||
/**
|
||||
* Add a measurement to your benchmark
|
||||
*
|
||||
* The same identifier can also be used multiple times
|
||||
*
|
||||
* @param string A comment identifying the current measurement
|
||||
* @return void
|
||||
*/
|
||||
public static function measure($message)
|
||||
{
|
||||
self::getInstance()->measures[] = (object) array(
|
||||
'timestamp' => microtime(true),
|
||||
'memory_real' => memory_get_usage(true),
|
||||
'memory' => memory_get_usage(),
|
||||
'message' => $message
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws all measurements away
|
||||
*
|
||||
* This empties your measurement table and allows you to restart your
|
||||
* benchmark from scratch
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function reset()
|
||||
{
|
||||
self::$instance = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rerieve benchmark start time
|
||||
*
|
||||
* This will give you the timestamp of your first measurement
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public static function getStartTime()
|
||||
{
|
||||
return self::getInstance()->start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump benchmark data
|
||||
*
|
||||
* Will dump a text table if running on CLI and a simple HTML table
|
||||
* otherwise. Use Benchmark::TIME and Benchmark::MEMORY to choose whether
|
||||
* you prefer to show either time or memory or both in your output
|
||||
*
|
||||
* @param int Whether to get time and/or memory summary
|
||||
* @return string
|
||||
*/
|
||||
public static function dump($what = null)
|
||||
{
|
||||
if (Icinga::app()->isCli()) {
|
||||
echo self::renderToText($what);
|
||||
} else {
|
||||
echo self::renderToHtml($what);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render benchmark data to a simple text table
|
||||
*
|
||||
* Use Benchmark::TIME and Icinga::MEMORY to choose whether you prefer to
|
||||
* show either time or memory or both in your output
|
||||
*
|
||||
* @param int Whether to get time and/or memory summary
|
||||
* @return string
|
||||
*/
|
||||
public static function renderToText($what = null)
|
||||
{
|
||||
$data = self::prepareDataForRendering($what);
|
||||
$sep = '+';
|
||||
$title = '|';
|
||||
foreach ($data->columns as & $col) {
|
||||
$col->format = ' %'
|
||||
. ($col->align === 'right' ? '' : '-')
|
||||
. $col->maxlen . 's |';
|
||||
|
||||
$sep .= str_repeat('-', $col->maxlen) . '--+';
|
||||
$title .= sprintf($col->format, $col->title);
|
||||
}
|
||||
|
||||
$out = $sep . "\n" . $title . "\n" . $sep . "\n";
|
||||
foreach ($data->rows as & $row) {
|
||||
$r = '|';
|
||||
foreach ($data->columns as $key => & $col) {
|
||||
$r .= sprintf($col->format, $row[$key]);
|
||||
}
|
||||
$out .= $r . "\n";
|
||||
}
|
||||
|
||||
$out .= $sep . "\n";
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render benchmark data to a simple HTML table
|
||||
*
|
||||
* Use Benchmark::TIME and Benchmark::MEMORY to choose whether you prefer
|
||||
* to show either time or memory or both in your output
|
||||
*
|
||||
* @param int Whether to get time and/or memory summary
|
||||
* @return string
|
||||
*/
|
||||
public static function renderToHtml($what = null)
|
||||
{
|
||||
$data = self::prepareDataForRendering($what);
|
||||
|
||||
// TODO: Move formatting to CSS file
|
||||
$style = 'font-family: monospace; font-size: 1.5em; width: 100%';
|
||||
$html = '<table style="' . $style . '">' . "\n" . '<tr>';
|
||||
foreach ($data->columns as & $col) {
|
||||
$html .= sprintf(
|
||||
'<td align="%s">%s</td>',
|
||||
$col->align,
|
||||
htmlspecialchars($col->title)
|
||||
);
|
||||
}
|
||||
$html .= "</tr>\n";
|
||||
|
||||
foreach ($data->rows as & $row) {
|
||||
$html .= '<tr>';
|
||||
foreach ($data->columns as $key => & $col) {
|
||||
$html .= sprintf(
|
||||
'<td align="%s">%s</td>',
|
||||
$col->align,
|
||||
$row[$key]
|
||||
);
|
||||
}
|
||||
$html .= "</tr>\n";
|
||||
}
|
||||
$html .= "</table>\n";
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares benchmark data for output
|
||||
*
|
||||
* Use Benchmark::TIME and Benchmark::MEMORY to choose whether you prefer
|
||||
* to have either time or memory or both in your output
|
||||
*
|
||||
* @param int Whether to get time and/or memory summary
|
||||
* @return array
|
||||
*/
|
||||
protected static function prepareDataForRendering($what = null)
|
||||
{
|
||||
if ($what === null) {
|
||||
$what = self::TIME | self::MEMORY;
|
||||
}
|
||||
|
||||
$columns = array(
|
||||
(object) array(
|
||||
'title' => 'Time',
|
||||
'align' => 'left',
|
||||
'maxlen' => 4
|
||||
),
|
||||
(object) array(
|
||||
'title' => 'Description',
|
||||
'align' => 'left',
|
||||
'maxlen' => 11
|
||||
)
|
||||
);
|
||||
if ($what & self::TIME) {
|
||||
$columns[] = (object) array(
|
||||
'title' => 'Off (ms)',
|
||||
'align' => 'right',
|
||||
'maxlen' => 11
|
||||
);
|
||||
$columns[] = (object) array(
|
||||
'title' => 'Dur (ms)',
|
||||
'align' => 'right',
|
||||
'maxlen' => 13
|
||||
);
|
||||
}
|
||||
if ($what & self::MEMORY) {
|
||||
$columns[] = (object) array(
|
||||
'title' => 'Mem (diff)',
|
||||
'align' => 'right',
|
||||
'maxlen' => 10
|
||||
);
|
||||
$columns[] = (object) array(
|
||||
'title' => 'Mem (total)',
|
||||
'align' => 'right',
|
||||
'maxlen' => 11
|
||||
);
|
||||
}
|
||||
|
||||
$bench = self::getInstance();
|
||||
$last = $bench->start;
|
||||
$rows = array();
|
||||
$lastmem = 0;
|
||||
foreach ($bench->measures as $m) {
|
||||
$micro = sprintf(
|
||||
'%03d',
|
||||
round(($m->timestamp - floor($m->timestamp)) * 1000)
|
||||
);
|
||||
$vals = array(
|
||||
date('H:i:s', $m->timestamp) . '.' . $micro,
|
||||
$m->message
|
||||
);
|
||||
|
||||
if ($what & self::TIME) {
|
||||
$m->relative = $m->timestamp - $bench->start;
|
||||
$m->offset = $m->timestamp - $last;
|
||||
$last = $m->timestamp;
|
||||
$vals[] = sprintf('%0.3f', $m->relative * 1000);
|
||||
$vals[] = sprintf('%0.3f', $m->offset * 1000);
|
||||
}
|
||||
|
||||
if ($what & self::MEMORY) {
|
||||
$mem = $m->memory - $lastmem;
|
||||
$lastmem = $m->memory;
|
||||
$vals[] = Format::bytes($mem);
|
||||
$vals[] = Format::bytes($m->memory);
|
||||
}
|
||||
|
||||
$row = & $rows[];
|
||||
foreach ($vals as $col => $val) {
|
||||
$row[$col] = $val;
|
||||
$columns[$col]->maxlen = max(
|
||||
strlen($val),
|
||||
$columns[$col]->maxlen
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (object) array(
|
||||
'columns' => $columns,
|
||||
'rows' => $rows
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton
|
||||
*
|
||||
* Benchmark is run only once, but you are not allowed to directly access
|
||||
* the getInstance() method
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new Benchmark();
|
||||
self::$instance->start = microtime(true);
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Singleton usage is enforced, the only way to instantiate Benchmark is by
|
||||
* starting your measurements
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Application;
|
||||
|
||||
require_once dirname(__FILE__) . '/ApplicationBootstrap.php';
|
||||
|
||||
class Cli extends ApplicationBootstrap
|
||||
{
|
||||
protected $isCli = true;
|
||||
|
||||
protected function bootstrap()
|
||||
{
|
||||
$this->assertRunningOnCli();
|
||||
return $this->loadConfig()
|
||||
->configureErrorHandling()
|
||||
->setTimezone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fail if Icinga has not been called on CLI
|
||||
*
|
||||
* @throws Exception
|
||||
* @return void
|
||||
*/
|
||||
private static function assertRunningOnCli()
|
||||
{
|
||||
if (Platform::isCli()) {
|
||||
return;
|
||||
}
|
||||
throw new Exception('Icinga is not running on CLI');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Application;
|
||||
|
||||
use Zend_Config_Ini;
|
||||
use Zend_Config;
|
||||
|
||||
class Config extends Zend_Config_Ini
|
||||
{
|
||||
protected $data;
|
||||
protected static $instance;
|
||||
protected $configDir;
|
||||
|
||||
public function listAll($what)
|
||||
{
|
||||
if ($this->$what === null) {
|
||||
return array();
|
||||
} else {
|
||||
return array_keys($this->$what->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
public function getConfigDir()
|
||||
{
|
||||
return $this->configDir;
|
||||
}
|
||||
|
||||
public function __construct($filename, $section = null, $options = false)
|
||||
{
|
||||
$options['allowModifications'] = true;
|
||||
$this->configDir = dirname($filename);
|
||||
return parent::__construct($filename, $section, $options);
|
||||
}
|
||||
|
||||
public function getModuleConfig($key, $module)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function __get($key)
|
||||
{
|
||||
$res = parent::__get($key);
|
||||
if ($res === null) {
|
||||
$app = Icinga::app();
|
||||
if ($app->hasModule($key)) {
|
||||
$filename = $app->getModule($key)->getConfigDir() . "/$key.ini";
|
||||
} else {
|
||||
$filename = $this->configDir . '/' . $key . '.ini';
|
||||
}
|
||||
if (file_exists($filename)) {
|
||||
$res = $this->$key = new Config($filename);
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
public static function getInstance($configFile = null)
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
if ($configFile === null) {
|
||||
$configFile = dirname(dirname(dirname(dirname(__FILE__))))
|
||||
. '/config/icinga.ini';
|
||||
}
|
||||
self::$instance = new Config($configFile);
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Run embedded in other web applications
|
||||
*
|
||||
* @package Icinga\Application
|
||||
*/
|
||||
namespace Icinga\Application;
|
||||
|
||||
require_once dirname(__FILE__) . '/ApplicationBootstrap.php';
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
||||
/**
|
||||
* Use this if you want to make use of Icinga funtionality in other web projects
|
||||
*
|
||||
* Usage example:
|
||||
* <code>
|
||||
* use Icinga\Application\EmbeddedWeb;
|
||||
* EmbeddedWeb::start();
|
||||
* </code>
|
||||
*
|
||||
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
|
||||
* @author Icinga-Web Team <info@icinga.org>
|
||||
* @package Icinga\Application
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
|
||||
*/
|
||||
class EmbeddedWeb extends ApplicationBootstrap
|
||||
{
|
||||
protected function bootstrap()
|
||||
{
|
||||
return $this->loadConfig()
|
||||
->configureErrorHandling()
|
||||
->setTimezone()
|
||||
->loadEnabledModules();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Application;
|
||||
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
||||
class Icinga
|
||||
{
|
||||
protected static $app;
|
||||
|
||||
public static function app()
|
||||
{
|
||||
if (null === self::$app) {
|
||||
throw new ProgrammingError('Icinga has never been started');
|
||||
}
|
||||
return self::$app;
|
||||
}
|
||||
|
||||
public static function setApp($app)
|
||||
{
|
||||
if (null !== self::$app) {
|
||||
throw new ProgrammingError('Cannot start Icinga twice');
|
||||
}
|
||||
self::$app = $app;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Application;
|
||||
|
||||
require_once dirname(__FILE__) . '/Web.php';
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
||||
class LegacyWeb extends Web
|
||||
{
|
||||
// IcingaWeb 1.x base dir
|
||||
protected $legacyBasedir;
|
||||
|
||||
protected function bootstrap()
|
||||
{
|
||||
parent::bootstrap();
|
||||
throw new ProgrammingError('Not yet');
|
||||
// $this->setupIcingaLegacyWrapper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Icinga-Web 1.x base path
|
||||
*
|
||||
* @throws Exception
|
||||
* @return self
|
||||
*/
|
||||
public function getLecacyBasedir()
|
||||
{
|
||||
return $this->legacyBasedir;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Icinga\Application\Loader class
|
||||
*
|
||||
* @category Icinga\Application
|
||||
*/
|
||||
namespace Icinga\Application;
|
||||
|
||||
use Icinga\Application\Log;
|
||||
|
||||
/**
|
||||
* This class provides a simple Autoloader
|
||||
*
|
||||
* It takes care of loading classes in the Icinga namespace. You shouldn't need
|
||||
* to manually instantiate this class, as bootstrapping code will do so for you.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* <code>
|
||||
* Icinga\Application\Loader::register();
|
||||
* </code>
|
||||
*
|
||||
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
|
||||
* @author Icinga-Web Team <info@icinga.org>
|
||||
* @package Icinga\Application
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
|
||||
*/
|
||||
class Loader
|
||||
{
|
||||
const NS = '\\';
|
||||
protected $moduleDirs = array();
|
||||
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Register the Icinga autoloader
|
||||
*
|
||||
* You could also call getInstance(), this alias function is here to make
|
||||
* code look better
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function register()
|
||||
{
|
||||
return self::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton
|
||||
*
|
||||
* Registers the Icinga autoloader if not already been done
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new Loader();
|
||||
self::$instance->registerAutoloader();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function addModule($name, $dir)
|
||||
{
|
||||
$this->moduleDirs[ucfirst($name)] = $dir;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class loader
|
||||
*
|
||||
* Ignores all but classes in the Icinga namespace.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if (strpos($class, 'Icinga' . self::NS) === false) {
|
||||
return false;
|
||||
}
|
||||
$file = str_replace(self::NS, '/', $class) . '.php';
|
||||
$file = ICINGA_LIBDIR . '/' . $file;
|
||||
if (! @is_file($file)) {
|
||||
$parts = preg_split('~\\\~', $class);
|
||||
array_shift($parts);
|
||||
$module = $parts[0];
|
||||
if (array_key_exists($module, $this->moduleDirs)) {
|
||||
$file = $this->moduleDirs[$module]
|
||||
. '/'
|
||||
. implode('/', $parts) . '.php';
|
||||
if (@is_file($file)) {
|
||||
require_once $file;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Log::debug('File ' . $file . ' not found');
|
||||
return false;
|
||||
}
|
||||
require_once $file;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Effectively registers the autoloader the PHP/SPL way
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function registerAutoloader()
|
||||
{
|
||||
// Not adding ourselves to include_path right now, MAY be faster
|
||||
/*set_include_path(implode(PATH_SEPARATOR, array(
|
||||
realpath(dirname(dirname(__FILE__))),
|
||||
get_include_path(),
|
||||
)));*/
|
||||
spl_autoload_register(array($this, 'loadClass'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Singleton usage is enforced, you are also not allowed to overwrite this
|
||||
* function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final private function __construct()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Application\Modules;
|
||||
|
||||
use Icinga\Application\ApplicationBootstrap;
|
||||
use Icinga\Data\ArrayDatasource;
|
||||
use Icinga\Web\Notification;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
|
||||
// TODO: show whether enabling/disabling modules is allowed by checking enableDir
|
||||
// perms
|
||||
|
||||
class Manager
|
||||
{
|
||||
protected $installedBaseDirs;
|
||||
protected $enabledDirs = array();
|
||||
protected $loadedModules = array();
|
||||
protected $index;
|
||||
protected $app;
|
||||
|
||||
protected $enableDir;
|
||||
|
||||
public function __construct(ApplicationBootstrap $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->prepareEssentials();
|
||||
$this->detectEnabledModules();
|
||||
}
|
||||
|
||||
protected function prepareEssentials()
|
||||
{
|
||||
$this->enableDir = $this->app->getConfig()->getConfigDir()
|
||||
. '/enabledModules';
|
||||
|
||||
if (! file_exists($this->enableDir) || ! is_dir($this->enableDir)) {
|
||||
throw new ProgrammingError(
|
||||
sprintf(
|
||||
'Missing module directory: %s',
|
||||
$this->enableDir
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function detectEnabledModules()
|
||||
{
|
||||
$fh = opendir($this->enableDir);
|
||||
|
||||
while (false !== ($file = readdir($fh))) {
|
||||
|
||||
if ($file[0] === '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$link = $this->enableDir . '/' . $file;
|
||||
if (! is_link($link)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$dir = realpath($link);
|
||||
if (! file_exists($dir) || ! is_dir($dir)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->enabledDirs[$file] = $dir;
|
||||
}
|
||||
}
|
||||
|
||||
public function loadEnabledModules()
|
||||
{
|
||||
foreach ($this->listEnabledModules() as $name) {
|
||||
$this->loadModule($name);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function loadModule($name)
|
||||
{
|
||||
if ($this->hasLoaded($name)) {
|
||||
return $this;
|
||||
}
|
||||
$module = new Module($this->app, $name, $this->getModuleDir($name));
|
||||
$module->register();
|
||||
$this->loadedModules[$name] = $module;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function enableModule($name)
|
||||
{
|
||||
if (! $this->hasInstalled($name)) {
|
||||
throw new ConfigurationError(
|
||||
sprintf(
|
||||
"Cannot enable module '%s' as it isn't installed",
|
||||
$name
|
||||
)
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
$target = $this->installedBaseDirs[$name];
|
||||
$link = $this->enableDir . '/' . $name;
|
||||
if (! is_writable($this->enableDir)) {
|
||||
Notification::error("I do not have permissions to enable modules");
|
||||
return $this;
|
||||
}
|
||||
if (@symlink($target, $link)) {
|
||||
Notification::success("The module $name has been enabled");
|
||||
} else {
|
||||
Notification::error("Enabling module $name failed");
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function disableModule($name)
|
||||
{
|
||||
if (! $this->hasEnabled($name)) {
|
||||
return $this;
|
||||
}
|
||||
if (! is_writable($this->enableDir)) {
|
||||
Notification::error("I do not have permissions to disable modules");
|
||||
return $this;
|
||||
}
|
||||
$link = $this->enableDir . '/' . $name;
|
||||
if (file_exists($link) && is_link($link)) {
|
||||
if (@unlink($link)) {
|
||||
Notification::success("The module $name has been disabled");
|
||||
} else {
|
||||
Notification::error("Disabling module $name failed");
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getModuleConfigDir($name)
|
||||
{
|
||||
return $this->getModuleDir($name, '/config');
|
||||
}
|
||||
|
||||
public function getModuleDir($name, $subdir = '')
|
||||
{
|
||||
if ($this->hasEnabled($name)) {
|
||||
return $this->enabledDirs[$name]. $subdir;
|
||||
}
|
||||
|
||||
if ($this->hasInstalled($name)) {
|
||||
return $this->installedBaseDirs[$name] . $subdir;
|
||||
}
|
||||
|
||||
throw new ProgrammingError(
|
||||
sprintf(
|
||||
'Trying to access uninstalled module dir: %s',
|
||||
$name
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function hasInstalled($name)
|
||||
{
|
||||
if ($this->installedBaseDirs === null) {
|
||||
$this->detectInstalledModules();
|
||||
}
|
||||
return array_key_exists($name, $this->installedBaseDirs);
|
||||
}
|
||||
|
||||
public function hasEnabled($name)
|
||||
{
|
||||
return array_key_exists($name, $this->enabledDirs);
|
||||
}
|
||||
|
||||
public function hasLoaded($name)
|
||||
{
|
||||
return array_key_exists($name, $this->loadedModules);
|
||||
}
|
||||
|
||||
public function getLoadedModules()
|
||||
{
|
||||
return $this->loadedModules;
|
||||
}
|
||||
|
||||
public function getModule($name)
|
||||
{
|
||||
if (! $this->hasLoaded($name)) {
|
||||
throw new ProgrammingError(
|
||||
sprintf(
|
||||
'Cannot access module %s as it hasn\'t been loaded',
|
||||
$name
|
||||
)
|
||||
);
|
||||
}
|
||||
return $this->loadedModules[$name];
|
||||
}
|
||||
|
||||
public function getModuleInfo()
|
||||
{
|
||||
$installed = $this->listInstalledModules();
|
||||
$info = array();
|
||||
foreach ($installed as $name) {
|
||||
$info[] = (object) array(
|
||||
'name' => $name,
|
||||
'path' => $this->installedBaseDirs[$name],
|
||||
'enabled' => $this->hasEnabled($name),
|
||||
'loaded' => $this->hasLoaded($name)
|
||||
);
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
public function select()
|
||||
{
|
||||
$ds = new ArrayDatasource($this->getModuleInfo());
|
||||
return $ds->select();
|
||||
}
|
||||
|
||||
public function listEnabledModules()
|
||||
{
|
||||
return array_keys($this->enabledDirs);
|
||||
}
|
||||
|
||||
public function listLoadedModules()
|
||||
{
|
||||
return array_keys($this->loadedModules);
|
||||
}
|
||||
|
||||
public function listInstalledModules()
|
||||
{
|
||||
if ($this->installedBaseDirs === null) {
|
||||
$this->detectInstalledModules();
|
||||
}
|
||||
return array_keys($this->installedBaseDirs);
|
||||
}
|
||||
|
||||
public function detectInstalledModules()
|
||||
{
|
||||
// TODO: Allow multiple paths for installed modules (e.g. web vs pkg)
|
||||
$basedir = realpath(ICINGA_APPDIR . '/../modules');
|
||||
$fh = @opendir($basedir);
|
||||
if ($fh === false) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
while ($name = readdir($fh)) {
|
||||
if ($name[0] === '.') {
|
||||
continue;
|
||||
}
|
||||
if (is_dir($basedir . '/' . $name)) {
|
||||
$this->installedBaseDirs[$name] = $basedir . '/' . $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Application\Modules;
|
||||
|
||||
use Icinga\Application\ApplicationBootstrap;
|
||||
use Icinga\Web\Hook;
|
||||
use Zend_Controller_Router_Route as Route;
|
||||
|
||||
class Module
|
||||
{
|
||||
protected $name;
|
||||
protected $basedir;
|
||||
protected $cssdir;
|
||||
protected $libdir;
|
||||
protected $localedir;
|
||||
protected $controllerdir;
|
||||
protected $registerscript;
|
||||
protected $app;
|
||||
|
||||
public function __construct(ApplicationBootstrap $app, $name, $basedir)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->name = $name;
|
||||
$this->basedir = $basedir;
|
||||
$this->cssdir = $basedir . '/public/css';
|
||||
$this->libdir = $basedir . '/library';
|
||||
$this->configdir = $basedir . '/config';
|
||||
$this->localedir = $basedir . '/application/locale';
|
||||
$this->controllerdir = $basedir . '/application/controllers';
|
||||
$this->registerscript = $basedir . '/register.php';
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
$this->registerLibrary()
|
||||
->registerWebIntegration()
|
||||
->runRegisterScript();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function hasCss()
|
||||
{
|
||||
return file_exists($this->getCssFilename());
|
||||
}
|
||||
|
||||
public function getCssFilename()
|
||||
{
|
||||
return $this->cssdir . '/module.less';
|
||||
}
|
||||
|
||||
public function getBaseDir()
|
||||
{
|
||||
return $this->basedir;
|
||||
}
|
||||
|
||||
public function getConfigDir()
|
||||
{
|
||||
return $this->configdir;
|
||||
}
|
||||
|
||||
protected function registerLibrary()
|
||||
{
|
||||
if (file_exists($this->libdir) && is_dir($this->libdir)) {
|
||||
$this->app->getLoader()->addModule($this->name, $this->libdir);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function registerLocales()
|
||||
{
|
||||
if (file_exists($this->localedir) && is_dir($this->localedir)) {
|
||||
bindtextdomain($this->name, $this->localedir);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function registerWebIntegration()
|
||||
{
|
||||
if (! $this->app->isWeb()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (file_exists($this->controllerdir) && is_dir($this->controllerdir)) {
|
||||
$this->app->frontController()->addControllerDirectory(
|
||||
$this->controllerdir,
|
||||
$this->name
|
||||
);
|
||||
}
|
||||
|
||||
$this->registerLocales()
|
||||
->registerRoutes()
|
||||
->registerMenuEntries();
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function registerMenuEntries()
|
||||
{
|
||||
$cfg = $this->app
|
||||
->getConfig()
|
||||
->getModuleConfig('menu', $this->name);
|
||||
|
||||
$view = $this->app->getView();
|
||||
if ($cfg) {
|
||||
$view->view->navigation = $cfg->merge($view->view->navigation);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function registerRoutes()
|
||||
{
|
||||
$this->app->frontController()->getRouter()->addRoute(
|
||||
$this->name . '_jsprovider',
|
||||
new Route(
|
||||
'js/' . $this->name . '/:file',
|
||||
array(
|
||||
'controller' => 'static',
|
||||
'action' =>'javascript',
|
||||
'moduleName' => $this->name
|
||||
)
|
||||
)
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function runRegisterScript()
|
||||
{
|
||||
if (file_exists($this->registerscript)
|
||||
&& is_readable($this->registerscript)) {
|
||||
include($this->registerscript);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function registerHook($name, $class)
|
||||
{
|
||||
Hook::register($name, $class);
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Application;
|
||||
|
||||
class Platform
|
||||
{
|
||||
protected static $domain;
|
||||
protected static $hostname;
|
||||
protected static $fqdn;
|
||||
|
||||
public static function isWindows()
|
||||
{
|
||||
return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
|
||||
}
|
||||
|
||||
public static function isLinux()
|
||||
{
|
||||
return strtoupper(substr(PHP_OS, 0, 5)) === 'LINUX';
|
||||
}
|
||||
|
||||
public static function isCli()
|
||||
{
|
||||
if (PHP_SAPI == 'cli') {
|
||||
return true;
|
||||
} elseif ((PHP_SAPI == 'cgi' || PHP_SAPI == 'cgi-fcgi')
|
||||
&& empty($_SERVER['SERVER_NAME'])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function getHostname()
|
||||
{
|
||||
if (self::$hostname === null) {
|
||||
self::discoverHostname();
|
||||
}
|
||||
return self::$hostname;
|
||||
}
|
||||
|
||||
public static function getDomain()
|
||||
{
|
||||
if (self::$domain === null) {
|
||||
self::discoverHostname();
|
||||
}
|
||||
return self::$domain;
|
||||
}
|
||||
|
||||
public static function getFqdn()
|
||||
{
|
||||
if (self::$fqdn === null) {
|
||||
self::discoverHostname();
|
||||
}
|
||||
return self::$fqdn;
|
||||
}
|
||||
|
||||
protected static function discoverHostname()
|
||||
{
|
||||
self::$hostname = gethostname();
|
||||
self::$fqdn = gethostbyaddr(gethostbyname(self::$hostname));
|
||||
|
||||
if (substr(self::$fqdn, 0, strlen(self::$hostname)) === self::$hostname) {
|
||||
self::$domain = substr(self::$fqdn, strlen(self::$hostname) + 1);
|
||||
} else {
|
||||
self::$domain = array_shift(preg_split('~\.~', self::$hostname, 2));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Application;
|
||||
|
||||
class TranslationHelper
|
||||
{
|
||||
protected $basedir;
|
||||
protected $moduledir;
|
||||
protected $tmpfile;
|
||||
protected $potfile;
|
||||
protected $locale;
|
||||
protected $module;
|
||||
|
||||
public function __construct(ApplicationBootstrap $bootstrap, $locale, $module = null)
|
||||
{
|
||||
$this->moduledir = $bootstrap->getModuleDir();
|
||||
if ($module) {
|
||||
$this->basedir = $bootstrap->getModuleDir($module) . '/application';
|
||||
} else {
|
||||
$this->basedir = $bootstrap->getApplicationDir();
|
||||
}
|
||||
$this->locale = $locale;
|
||||
$this->module = $module;
|
||||
$this->targetfile = $this->basedir
|
||||
. '/locale/'
|
||||
. $this->locale
|
||||
. '/LC_MESSAGES/'
|
||||
. ($module ? $module : 'icinga')
|
||||
. '.po';
|
||||
$target_dir = dirname($this->targetfile);
|
||||
if (! is_dir($target_dir)) {
|
||||
mkdir($target_dir, 0755, true);
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->tmpfile !== null) {
|
||||
unlink($this->tmpfile);
|
||||
}
|
||||
if ($this->potfile !== null) {
|
||||
unlink($this->potfile);
|
||||
}
|
||||
}
|
||||
|
||||
public function extractTexts()
|
||||
{
|
||||
$tmpdir = sys_get_temp_dir();
|
||||
$this->potfile = tempnam($tmpdir, 'IcingaPot_');
|
||||
$cmd = '/usr/bin/xgettext'
|
||||
. ' --language=PHP'
|
||||
. ' --from-code=iso-8859-15'
|
||||
. ' --keyword='
|
||||
. ($this->module ? '_mt:2' : '_t')
|
||||
. ' --sort-output'
|
||||
. ' --force-po'
|
||||
. ' --package-name=Icinga'
|
||||
. ' --package-version=0.1'
|
||||
. ' --copyright-holder="Icinga Team"'
|
||||
. ' --msgid-bugs-address="dev@icinga.org"'
|
||||
. ' --files-from=' . $this->tmpfile
|
||||
. ' --output=' . $this->potfile
|
||||
;
|
||||
`$cmd`;
|
||||
$this->fixPotfile();
|
||||
$this->mergeOldTranslations();
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function fixPotfile()
|
||||
{
|
||||
$content = file_get_contents($this->potfile);
|
||||
$fh = fopen($this->potfile, 'w');
|
||||
foreach (preg_split('~\n~', $content) as $line) {
|
||||
// if (preg_match('~^"Language:~', $line)) continue;
|
||||
if (preg_match('~^"Content-Type:~', $line)) {
|
||||
$line = '"Content-Type: text/plain; charset=utf-8\n"';
|
||||
}
|
||||
fwrite($fh, $line . "\n");
|
||||
}
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
protected function mergeOldTranslations()
|
||||
{
|
||||
if (is_file($this->targetfile)) {
|
||||
$cmd = sprintf(
|
||||
'/usr/bin/msgmerge %s %s -o %s 2>&1',
|
||||
$this->targetfile,
|
||||
$this->potfile,
|
||||
$this->targetfile . '.new'
|
||||
);
|
||||
`$cmd`;
|
||||
rename($this->targetfile . '.new', $this->targetfile);
|
||||
} else {
|
||||
file_put_contents($this->targetfile, file_get_contents($this->potfile));
|
||||
}
|
||||
}
|
||||
|
||||
public function createTemporaryFileList()
|
||||
{
|
||||
$tmpdir = sys_get_temp_dir();
|
||||
$this->tmpfile = tempnam($tmpdir, 'IcingaTranslation_');
|
||||
$tmp_fh = fopen($this->tmpfile, 'w');
|
||||
if (! $tmp_fh) {
|
||||
throw new \Exception('Unable to create ' . $this->tmpfile);
|
||||
}
|
||||
if ($this->module) {
|
||||
$blacklist = array();
|
||||
} else {
|
||||
$blacklist = array(
|
||||
$this->moduledir
|
||||
);
|
||||
}
|
||||
$this->getSourceFileNames($this->basedir, $tmp_fh, $blacklist);
|
||||
$this->getSourceFileNames(ICINGA_LIBDIR, $tmp_fh, $blacklist);
|
||||
fclose($tmp_fh);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getSourceFileNames($dir, & $fh, $blacklist = array())
|
||||
{
|
||||
$dh = opendir($dir);
|
||||
if (! $dh) {
|
||||
throw new \Exception("Unable to read files from $dir");
|
||||
}
|
||||
$subdirs = array();
|
||||
while ($filename = readdir($dh)) {
|
||||
if ($filename[0] === '.') {
|
||||
continue;
|
||||
}
|
||||
$fullname = $dir . '/' . $filename;
|
||||
if (preg_match('~\.(?:php|phtml)$~', $filename)) {
|
||||
fwrite($fh, "$fullname\n");
|
||||
} elseif (is_dir($fullname)) {
|
||||
if (in_array($fullname, $blacklist)) {
|
||||
continue;
|
||||
}
|
||||
$subdirs[] = $fullname;
|
||||
}
|
||||
}
|
||||
closedir($dh);
|
||||
foreach ($subdirs as $dir) {
|
||||
$this->getSourceFileNames($dir, $fh, $blacklist);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Application;
|
||||
|
||||
require_once dirname(__FILE__) . '/ApplicationBootstrap.php';
|
||||
use Icinga\Web\Session;
|
||||
use Zend_Controller_Front as FrontController;
|
||||
use Zend_Layout as Layout;
|
||||
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 this if you want to make use of Icinga funtionality in other web projects
|
||||
*
|
||||
* Usage example:
|
||||
* <code>
|
||||
* use Icinga\Application\EmbeddedWeb;
|
||||
* EmbeddedWeb::start();
|
||||
* </code>
|
||||
*
|
||||
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
|
||||
* @author Icinga-Web Team <info@icinga.org>
|
||||
* @package Icinga\Application
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
|
||||
*/
|
||||
class Web extends ApplicationBootstrap
|
||||
{
|
||||
protected $view;
|
||||
protected $frontController;
|
||||
protected $isWeb = true;
|
||||
|
||||
protected function bootstrap()
|
||||
{
|
||||
return $this->loadConfig()
|
||||
->configureErrorHandling()
|
||||
->setTimezone()
|
||||
->configureSession()
|
||||
->configureCache()
|
||||
->prepareZendMvc()
|
||||
->loadTranslations()
|
||||
->loadEnabledModules()
|
||||
->setupSpecialRoutes()
|
||||
->configurePagination();
|
||||
}
|
||||
|
||||
protected function setupSpecialRoutes()
|
||||
{
|
||||
// TODO: Find a better solution
|
||||
$this->frontController->getRouter()->addRoute(
|
||||
'module_overview',
|
||||
new Route(
|
||||
'js/modules/list.js',
|
||||
array(
|
||||
'controller' =>'static',
|
||||
'action' =>'modulelist',
|
||||
)
|
||||
)
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function frontController()
|
||||
{
|
||||
// TODO: ProgrammingError if null
|
||||
return $this->frontController;
|
||||
}
|
||||
|
||||
public function getView()
|
||||
{
|
||||
// TODO: ProgrammingError if null
|
||||
return $this->view;
|
||||
}
|
||||
|
||||
public function dispatch()
|
||||
{
|
||||
$this->dispatchFrontController();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure web session settings
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function configureSession()
|
||||
{
|
||||
Session::setOptions(
|
||||
array(
|
||||
// strict requires Zend_Session::start()
|
||||
'strict' => true,
|
||||
'cookie_secure' => false,
|
||||
'name' => $this->config->{'global'}->get(
|
||||
'session_cookie',
|
||||
'ICINGA_SID'
|
||||
),
|
||||
|
||||
// Obsolete once moved to Icinga\Web\Session:
|
||||
'cookie_httponly' => true,
|
||||
'use_only_cookies' => true,
|
||||
'hash_function' => true,
|
||||
'hash_bits_per_character' => 5,
|
||||
)
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function loadTranslations()
|
||||
{
|
||||
$locale = Session::getInstance()->language;
|
||||
if (! $locale) {
|
||||
$locale = 'en_US';
|
||||
}
|
||||
putenv('LC_ALL=' . $locale . '.UTF-8');
|
||||
setlocale(LC_ALL, $locale . '.UTF-8');
|
||||
bindtextdomain('icinga', ICINGA_APPDIR . '/locale');
|
||||
textdomain('icinga');
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function dispatchFrontController()
|
||||
{
|
||||
Session::getInstance();
|
||||
$this->frontController->dispatch();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare Zend MVC Base
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function prepareZendMvc()
|
||||
{
|
||||
// TODO: Replace Zend_Application:
|
||||
Layout::startMvc(
|
||||
array(
|
||||
'layout' => 'layout',
|
||||
'layoutPath' => $this->appdir . '/layouts/scripts'
|
||||
)
|
||||
);
|
||||
|
||||
return $this->prepareFrontController()
|
||||
->prepareView();
|
||||
}
|
||||
|
||||
protected function prepareFrontController()
|
||||
{
|
||||
$this->frontController = FrontController::getInstance()
|
||||
->setControllerDirectory($this->appdir . '/controllers')
|
||||
// TODO: Create config option for Load balancers etc:
|
||||
// ->setBaseurl()
|
||||
->setParams(
|
||||
array(
|
||||
'displayExceptions' => 1
|
||||
)
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function prepareView()
|
||||
{
|
||||
$view = ActionHelper::getStaticHelper('viewRenderer');
|
||||
$view->initView();
|
||||
|
||||
$view->view->addHelperPath($this->appdir . '/views/helpers');
|
||||
// TODO: find out how to avoid this additional helper path:
|
||||
$view->view->addHelperPath($this->appdir . '/views/helpers/layout');
|
||||
|
||||
$view->view->setEncoding('UTF-8');
|
||||
$view->view->headTitle()->prepend(
|
||||
$this->config->{'global'}->get('project', 'Icinga')
|
||||
);
|
||||
$view->view->headTitle()->setSeparator(' :: ');
|
||||
$view->view->navigation = $this->config->menu;
|
||||
|
||||
$this->view = $view;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure pagination settings
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function configurePagination()
|
||||
{
|
||||
Paginator::addScrollingStylePrefixPath(
|
||||
'Icinga_Web_Paginator_ScrollingStyle',
|
||||
'Icinga/Web/Paginator/ScrollingStyle'
|
||||
);
|
||||
|
||||
Paginator::setDefaultScrollingStyle('SlidingWithBorder');
|
||||
PaginationControl::setDefaultViewPartial(
|
||||
array('mixedPagination.phtml','default')
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
if (function_exists('_')) {
|
||||
function t($messageId = null)
|
||||
{
|
||||
$msg = _($messageId);
|
||||
if (! $msg) {
|
||||
return $messageId;
|
||||
}
|
||||
return $msg;
|
||||
}
|
||||
|
||||
function mt($domain, $messageId = null)
|
||||
{
|
||||
$msg = dgettext($domain, $messageId);
|
||||
if (! $msg) {
|
||||
return $messageId;
|
||||
}
|
||||
return $msg;
|
||||
}
|
||||
} else {
|
||||
function t($messageId = null)
|
||||
{
|
||||
return $messageId;
|
||||
}
|
||||
|
||||
function mt($domain, $messageId = null)
|
||||
{
|
||||
return $messageId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Authentication;
|
||||
|
||||
use Icinga\Exception;
|
||||
use Zend_Session_Namespace as SessionNamespace;
|
||||
|
||||
class Auth
|
||||
{
|
||||
protected static $instance;
|
||||
protected $userInfo;
|
||||
protected $session;
|
||||
|
||||
final private function __construct()
|
||||
{
|
||||
$this->session = new SessionNamespace('IcingaAuth');
|
||||
}
|
||||
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new Auth();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function isAuthenticated()
|
||||
{
|
||||
if ($this->userInfo === null) {
|
||||
if ($sessionInfo = $this->session->userInfo) {
|
||||
$this->userInfo = $sessionInfo;
|
||||
}
|
||||
}
|
||||
return is_object($this->userInfo) && ! empty($this->userInfo->username);
|
||||
}
|
||||
|
||||
public function getUsername()
|
||||
{
|
||||
$this->assertIsAuthenticated();
|
||||
return $this->userInfo->username;
|
||||
}
|
||||
|
||||
public function getEmail()
|
||||
{
|
||||
$this->assertIsAuthenticated();
|
||||
return $this->userInfo->email;
|
||||
}
|
||||
|
||||
public function setAuthenticatedUser(User $user)
|
||||
{
|
||||
$this->userInfo = (object) array(
|
||||
'username' => $user->username,
|
||||
'permissions' => $user->getPermissionList(),
|
||||
'email' => $user->email,
|
||||
);
|
||||
$this->session->userInfo = $this->userInfo;
|
||||
}
|
||||
|
||||
public function forgetAuthentication()
|
||||
{
|
||||
unset($this->session->userInfo);
|
||||
$this->userInfo = null;
|
||||
}
|
||||
|
||||
public function hasPermission($route, $flags = 0x01)
|
||||
{
|
||||
$this->assertBeingAuthenticated();
|
||||
if (! array_key_exists($route, $this->userInfo->permissions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->userInfo->permissions[$route] & $flags === $flags;
|
||||
}
|
||||
|
||||
protected function assertIsAuthenticated()
|
||||
{
|
||||
if (! $this->isAuthenticated()) {
|
||||
throw new Exception\ProgrammingError(
|
||||
'Cannot fetch properties of a non-authenticated user'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Authentication;
|
||||
|
||||
class Backend
|
||||
{
|
||||
protected $userBackend;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
$userbackend = ucwords(strtolower($config->users->backend));
|
||||
$class = '\\Icinga\\Authentication\\' . $userbackend . 'UserBackend';
|
||||
$this->userBackend = new $class($config->users);
|
||||
}
|
||||
|
||||
public function hasUsername($username)
|
||||
{
|
||||
return $this->userBackend->hasUsername($username);
|
||||
}
|
||||
|
||||
public function authenticate($username, $password = null)
|
||||
{
|
||||
return $this->userBackend->authenticate($username, $password);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Authentication;
|
||||
|
||||
use Icinga\Protocol\Ldap;
|
||||
|
||||
class LdapUserBackend extends UserBackend
|
||||
{
|
||||
protected $connection;
|
||||
|
||||
protected function init()
|
||||
{
|
||||
$this->connection = new Ldap\Connection($this->config);
|
||||
}
|
||||
|
||||
public function hasUsername($username)
|
||||
{
|
||||
if (! $username) {
|
||||
return false;
|
||||
}
|
||||
return $this->connection->fetchOne(
|
||||
$this->selectUsername($username)
|
||||
) === $username;
|
||||
}
|
||||
|
||||
protected function stripAsterisks($string)
|
||||
{
|
||||
return str_replace('*', '', $string);
|
||||
}
|
||||
|
||||
protected function selectUsername($username)
|
||||
{
|
||||
return $this->connection->select()
|
||||
->from('user', array('sAMAccountName'))
|
||||
->where('sAMAccountName', $this->stripAsterisks($username));
|
||||
}
|
||||
|
||||
public function authenticate($username, $password = null)
|
||||
{
|
||||
if (empty($username) || empty($password)) {
|
||||
return false;
|
||||
}
|
||||
if (! $this->connection->testCredentials(
|
||||
$this->connection->fetchDN($this->selectUsername($username)),
|
||||
$password
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
$user = User::create(
|
||||
$this,
|
||||
array(
|
||||
'username' => $username,
|
||||
)
|
||||
);
|
||||
return $user;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Icinga Authentication Storable class
|
||||
*
|
||||
* @package Icinga\Authentication
|
||||
*/
|
||||
namespace Icinga\Authentication;
|
||||
|
||||
/**
|
||||
* This class represents an abstract storable object
|
||||
*
|
||||
* Use this only for objects with unique identifiers. Do not persist such
|
||||
* objects, they shall be loaded at each request. Storable doesn't care about
|
||||
* race conditions and doesn't care about the current data in your backend.
|
||||
*
|
||||
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
|
||||
* @author Icinga-Web Team <info@icinga.org>
|
||||
* @package Icinga\Application
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
|
||||
*/
|
||||
abstract class Storable
|
||||
{
|
||||
protected $key;
|
||||
|
||||
/**
|
||||
* Current Storable properties
|
||||
*/
|
||||
protected $props;
|
||||
|
||||
/**
|
||||
* Default property values for this Storable
|
||||
*
|
||||
* All allowed properties have to be defined here, otherwise they will be
|
||||
* rejected
|
||||
*/
|
||||
protected $defaultProps = array();
|
||||
|
||||
/**
|
||||
* Properties as they have been once loaded from backend
|
||||
*/
|
||||
protected $storedProps = array();
|
||||
|
||||
/**
|
||||
* Whether this storable has been stored in the current state
|
||||
*/
|
||||
protected $stored = false;
|
||||
|
||||
/**
|
||||
* Create a new Storable instance, with data loaded from backend
|
||||
*
|
||||
* You should NEVER directly use this function unless you are absolutely
|
||||
* sure on what you are doing.
|
||||
*
|
||||
* @param Backend The backend used to load this object from
|
||||
* @param Array Property array
|
||||
* @return Storable
|
||||
*/
|
||||
public static function create(UserBackend $backend, $props = array())
|
||||
{
|
||||
$class = get_called_class();
|
||||
$object = new $class($props);
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this function for custom cross-value checks before storing it
|
||||
*
|
||||
* @return boolean Whether the Storable is valid
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The constructor is protected, you should never override it
|
||||
*
|
||||
* Use the available hooks for all the things you need to do at construction
|
||||
* time
|
||||
*
|
||||
* @param Array Property array
|
||||
* @return void
|
||||
*/
|
||||
final protected function __construct($properties = array())
|
||||
{
|
||||
$this->assertKeyHasBeenDefined();
|
||||
$this->props = $this->defaultProps;
|
||||
foreach ($properties as $key => $val) {
|
||||
$this->set($key, $val);
|
||||
}
|
||||
$this->assertKeyExists();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get property value, fail unless it exists
|
||||
*
|
||||
* @param string Property name
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key)
|
||||
{
|
||||
$this->assertPropertyExists($key);
|
||||
return $this->props[$key];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set property value, fail unless it exists
|
||||
*
|
||||
* @param string Property name
|
||||
* @param mixed New property value
|
||||
* @return Storable
|
||||
*/
|
||||
protected function set($key, $val)
|
||||
{
|
||||
$this->assertPropertyExists($key);
|
||||
$this->props[$key] = $val;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter
|
||||
*
|
||||
* @param string Property name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
return $this->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
* @param string Property name
|
||||
* @param mixed New property value
|
||||
* @return void
|
||||
*/
|
||||
public function __set($key, $val)
|
||||
{
|
||||
$this->set($key, $val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given property name exist
|
||||
*
|
||||
* @param string Property name
|
||||
* @return boolean
|
||||
*/
|
||||
public function __isset($key)
|
||||
{
|
||||
return array_key_exists($key, $this->props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that the Storable got it's unique key
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return Storable
|
||||
*/
|
||||
protected function assertKeyExists()
|
||||
{
|
||||
return $this->assertPropertyExists($this->key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the given property is allowed
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return Storable
|
||||
*/
|
||||
protected function assertPropertyExists($key)
|
||||
{
|
||||
if (! array_key_exists($key, $this->props)) {
|
||||
throw new \Exception(
|
||||
sprintf(
|
||||
'Storable (%s) has no "%s" property',
|
||||
get_class($this),
|
||||
$key
|
||||
)
|
||||
);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that the class inheriting Storable defined it's key column
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return Storable
|
||||
*/
|
||||
protected function assertKeyHasBeenDefined()
|
||||
{
|
||||
if ($this->key === null) {
|
||||
throw new \Exception(
|
||||
'Implementation error, Storable needs a valid key'
|
||||
);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Icinga Authentication User class
|
||||
*
|
||||
* @package Icinga\Authentication
|
||||
*/
|
||||
namespace Icinga\Authentication;
|
||||
|
||||
/**
|
||||
* This class represents a user object
|
||||
*
|
||||
* TODO: Show some use cases
|
||||
*
|
||||
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
|
||||
* @author Icinga-Web Team <info@icinga.org>
|
||||
* @package Icinga\Application
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
|
||||
*/
|
||||
class User extends Storable
|
||||
{
|
||||
protected $defaultProps = array(
|
||||
'username' => null,
|
||||
'password' => null,
|
||||
'first_name' => null,
|
||||
'last_name' => null,
|
||||
'email' => null,
|
||||
);
|
||||
protected $permissions = array();
|
||||
protected $backend;
|
||||
protected $groups;
|
||||
protected $key = 'username';
|
||||
|
||||
public function listGroups()
|
||||
{
|
||||
if ($this->groups === null) {
|
||||
$this->loadGroups();
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadGroups()
|
||||
{
|
||||
// Whatever
|
||||
}
|
||||
|
||||
public function isMemberOf(Group $group)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function getPermissionList()
|
||||
{
|
||||
return $this->permissions;
|
||||
}
|
||||
|
||||
public function hasPermission($uri, $permission)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function grantPermission($uri, $permission)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function revokePermission($uri, $permission)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Authentication;
|
||||
|
||||
class UserBackend
|
||||
{
|
||||
protected $config;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->init();
|
||||
}
|
||||
|
||||
protected function init()
|
||||
{
|
||||
}
|
||||
|
||||
public function hasUsername($username)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function authenticate($username, $password = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Web\Session;
|
||||
class Backend
|
||||
{
|
||||
protected static $instances = array();
|
||||
|
||||
protected function __construct() {}
|
||||
|
||||
public static function getInstance($name = null)
|
||||
{
|
||||
if (! array_key_exists($name, self::$instances)) {
|
||||
$config = Config::getInstance()->backends;
|
||||
if ($name === null) {
|
||||
$name = Session::getInstance()->backend;
|
||||
}
|
||||
if ($name === null) {
|
||||
$name = array_shift(array_keys($config->toArray()));
|
||||
}
|
||||
if (isset($config->backends->$name)) {
|
||||
$config = $config->backends->$name;
|
||||
$type = $config->type;
|
||||
$type[0] = strtoupper($type[0]);
|
||||
$class = '\\Icinga\\Backend\\' . $type;
|
||||
self::$instances[$name] = new $class($config);
|
||||
} else {
|
||||
throw new \Exception(sprintf(
|
||||
'Got no config for backend %s',
|
||||
$name
|
||||
));
|
||||
}
|
||||
}
|
||||
return self::$instances[$name];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Backend;
|
||||
use Icinga\Backend;
|
||||
class Combo extends AbstractBackend
|
||||
{
|
||||
protected $db;
|
||||
protected $backends;
|
||||
|
||||
// TODO: Create a dummy query object to also catch errors with lazy connections
|
||||
public function from($view, $fields = array())
|
||||
{
|
||||
$backends = $this->listMyBackends();
|
||||
$query = null;
|
||||
$msg = '';
|
||||
while ($query === null) {
|
||||
try {
|
||||
$backend_name = array_shift($backends);
|
||||
$msg .= "Trying $backend_name";
|
||||
$backend = Backend::getInstance($backend_name);
|
||||
if ($backend->hasView($view)) {
|
||||
$query = $backend->from($view, $fields);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$msg .= ' Failed: ' . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
if ($query !== null) $msg .= " Succeeded.\n";
|
||||
|
||||
if ($query === null && empty($backends)) {
|
||||
throw new \Exception('All backends failed: ' . nl2br($msg));
|
||||
}
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function hasView($virtual_table)
|
||||
{
|
||||
$backends = $this->listMyBackends();
|
||||
while ($backend_name = array_shift($backends)) {
|
||||
if (Backend::getInstance($backend_name)->hasView($virtual_table)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function listMyBackends()
|
||||
{
|
||||
return preg_split('~,\s*~', $this->config->backends, -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Icinga IDO Backend
|
||||
*
|
||||
* @package Icinga\Backend
|
||||
*/
|
||||
namespace Icinga\Backend;
|
||||
|
||||
/**
|
||||
* This class provides an easy-to-use interface to the IDO database
|
||||
*
|
||||
* You should usually not directly use this class but go through Icinga\Backend.
|
||||
*
|
||||
* New MySQL indexes:
|
||||
* <code>
|
||||
* CREATE INDEX web2_index ON icinga_scheduleddowntime (object_id, is_in_effect);
|
||||
* CREATE INDEX web2_index ON icinga_comments (object_id);
|
||||
* CREATE INDEX web2_index ON icinga_objects (object_id, is_active); -- (not sure yet)
|
||||
* </code>
|
||||
*
|
||||
* Other possible (history-related) indexes, still subject to tests:
|
||||
* CREATE INDEX web2_index ON icinga_statehistory (object_id, state_time DESC);
|
||||
* CREATE INDEX web2_index ON icinga_notifications (object_id, instance_id, start_time DESC);
|
||||
* CREATE INDEX web2_index ON icinga_downtimehistory (object_id, actual_start_time, actual_end_time);
|
||||
*
|
||||
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
|
||||
* @author Icinga-Web Team <info@icinga.org>
|
||||
* @package Icinga\Application
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
|
||||
*/
|
||||
class Ido extends AbstractBackend
|
||||
{
|
||||
protected $db;
|
||||
protected $dbtype;
|
||||
protected $prefix = 'icinga_';
|
||||
|
||||
/**
|
||||
* Backend initialization starts here
|
||||
*
|
||||
* return void
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our Zend_Db connection
|
||||
*
|
||||
* return \Zend_Db_Adapter_Abstract
|
||||
*/
|
||||
public function getAdapter()
|
||||
{
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
public function getDbType()
|
||||
{
|
||||
return $this->dbtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our IDO table prefix
|
||||
*
|
||||
* return string
|
||||
*/
|
||||
public function getPrefix()
|
||||
{
|
||||
return $this->prefix;
|
||||
}
|
||||
|
||||
// TODO: Move elsewhere. Really? Reasons may be: other backends need IDO
|
||||
// access, even in environments running state details without IDO
|
||||
protected function connect()
|
||||
{
|
||||
$this->dbtype = $this->config->get('dbtype', 'mysql');
|
||||
$options = array(
|
||||
\Zend_Db::AUTO_QUOTE_IDENTIFIERS => false,
|
||||
\Zend_Db::CASE_FOLDING => \Zend_Db::CASE_LOWER
|
||||
);
|
||||
$drv_options = array(
|
||||
\PDO::ATTR_TIMEOUT => 2,
|
||||
// TODO: Check whether LC is useful. Zend_Db does fetchNum for Oci:
|
||||
\PDO::ATTR_CASE => \PDO::CASE_LOWER
|
||||
// TODO: ATTR_ERRMODE => ERRMODE_EXCEPTION vs ERRMODE_SILENT
|
||||
);
|
||||
switch ($this->dbtype) {
|
||||
case 'mysql':
|
||||
$adapter = 'Pdo_Mysql';
|
||||
$drv_options[\PDO::MYSQL_ATTR_INIT_COMMAND] =
|
||||
"SET SESSION SQL_MODE='STRICT_ALL_TABLES,NO_ZERO_IN_DATE,"
|
||||
. "NO_ZERO_DATE,NO_ENGINE_SUBSTITUTION';";
|
||||
// Not using ONLY_FULL_GROUP_BY as of performance impact
|
||||
// TODO: NO_ZERO_IN_DATE as been added with 5.1.11. Is it
|
||||
// ignored by other versions?
|
||||
$port = $this->config->get('port', 3306);
|
||||
break;
|
||||
case 'pgsql':
|
||||
$adapter = 'Pdo_Pgsql';
|
||||
$port = $this->config->get('port', 5432);
|
||||
break;
|
||||
case 'oracle':
|
||||
$adapter = 'Pdo_Oci';
|
||||
// $adapter = 'Oracle';
|
||||
$port = $this->config->get('port', 1521);
|
||||
// $drv_options[\PDO::ATTR_STRINGIFY_FETCHES] = true;
|
||||
|
||||
|
||||
if ($adapter === 'Oracle') {
|
||||
|
||||
putenv('ORACLE_SID=XE');
|
||||
putenv('ORACLE_HOME=/u01/app/oracle/product/11.2.0/xe');
|
||||
putenv('PATH=$PATH:$ORACLE_HOME/bin');
|
||||
putenv('ORACLE_BASE=/u01/app/oracle');
|
||||
putenv('NLS_LANG=AMERICAN_AMERICA.UTF8');
|
||||
|
||||
}
|
||||
|
||||
|
||||
$this->prefix = '';
|
||||
break;
|
||||
default:
|
||||
throw new \Exception(sprintf(
|
||||
'Backend "%s" is not supported', $type
|
||||
));
|
||||
}
|
||||
$attributes = array(
|
||||
'host' => $this->config->host,
|
||||
'port' => $port,
|
||||
'username' => $this->config->user,
|
||||
'password' => $this->config->pass,
|
||||
'dbname' => $this->config->db,
|
||||
'options' => $options,
|
||||
'driver_options' => $drv_options
|
||||
);
|
||||
if ($this->dbtype === 'oracle') {
|
||||
$attributes['persistent'] = true;
|
||||
}
|
||||
$this->db = \Zend_Db::factory($adapter, $attributes);
|
||||
if ($adapter === 'Oracle') {
|
||||
$this->db->setLobAsString(false);
|
||||
}
|
||||
$this->db->setFetchMode(\Zend_Db::FETCH_OBJ);
|
||||
// $this->db->setFetchMode(\Zend_Db::FETCH_ASSOC);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// *** TODO: EVERYTHING BELOW THIS LINE WILL BE MOVED AWAY *** ///
|
||||
|
||||
|
||||
// UGLY temporary host fetch
|
||||
public function fetchHost($host)
|
||||
{
|
||||
$object = \Icinga\Objects\Service::fromBackend(
|
||||
$this->select()
|
||||
->from('servicelist')
|
||||
->where('so.name1 = ?', $host)
|
||||
->fetchRow()
|
||||
);
|
||||
$object->customvars = $this->fetchCustomvars($host);
|
||||
return $object;
|
||||
}
|
||||
|
||||
// UGLY temporary service fetch
|
||||
public function fetchService($host, $service)
|
||||
{
|
||||
$object = \Icinga\Objects\Service::fromBackend(
|
||||
$this->select()
|
||||
->from('servicelist')
|
||||
->where('so.name1 = ?', $host)
|
||||
->where('so.name2 = ?', $service)
|
||||
->fetchRow()
|
||||
);
|
||||
$object->customvars = $this->fetchCustomvars($host, $service);
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function fetchCustomvars($host, $service = null)
|
||||
{
|
||||
if ($this->dbtype === 'oracle') return (object) array();
|
||||
|
||||
$select = $this->db->select()->from(
|
||||
array('cv' => $this->prefix . 'customvariablestatus'),
|
||||
array(
|
||||
// 'host_name' => 'cvo.name1',
|
||||
// 'service_description' => 'cvo.name2',
|
||||
'name' => 'cv.varname',
|
||||
'value' => 'cv.varvalue',
|
||||
)
|
||||
)->join(
|
||||
array('cvo' => $this->prefix . 'objects'),
|
||||
'cvo.object_id = cv.object_id',
|
||||
array()
|
||||
);
|
||||
$select->where('name1 = ?', $host);
|
||||
if ($service === null) {
|
||||
$select->where('objecttype_id = 1');
|
||||
} else {
|
||||
$select->where('objecttype_id = 1');
|
||||
$select->where('name2 = ?', $service);
|
||||
}
|
||||
$select->where('is_active = 1')->order('cv.varname');
|
||||
return (object) $this->db->fetchPairs($select);
|
||||
}
|
||||
|
||||
// TODO: Move to module!
|
||||
|
||||
public function fetchHardStatesForBpHosts($hosts)
|
||||
{
|
||||
return $this->fetchStatesForBp($hosts, 'last_hard_state');
|
||||
}
|
||||
|
||||
public function fetchSoftStatesForBpHosts($hosts)
|
||||
{
|
||||
return $this->fetchStatesForBp($hosts, 'current_state');
|
||||
}
|
||||
|
||||
public function fetchStatesForBp($hosts, $state_column = 'last_hard_state')
|
||||
{
|
||||
$select_hosts = $this->db->select()->from(
|
||||
array('hs' => $this->prefix . 'hoststatus'),
|
||||
array(
|
||||
'state' => 'hs.' . $state_column,
|
||||
'ack' => 'hs.problem_has_been_acknowledged',
|
||||
'in_downtime' => 'CASE WHEN (d.object_id IS NULL) THEN 0 ELSE 1 END',
|
||||
'combined' => 'hs.current_state << 2 + hs.problem_has_been_acknowledged << 1 + CASE WHEN (d.object_id IS NULL) THEN 0 ELSE 1 END'
|
||||
)
|
||||
)->joinRight(
|
||||
array('o' => $this->prefix . 'objects'),
|
||||
'hs.host_object_id = o.object_id',
|
||||
array(
|
||||
'object_id' => 'o.object_id',
|
||||
'hostname' => 'o.name1',
|
||||
'service' => '(NULL)'
|
||||
)
|
||||
)->joinLeft(
|
||||
array('d' => $this->prefix . 'scheduleddowntime'),
|
||||
'o.object_id = d.object_id'
|
||||
. ' AND d.was_started = 1'
|
||||
. ' AND d.scheduled_end_time > NOW()'
|
||||
. ' AND d.actual_start_time < NOW()',
|
||||
array()
|
||||
)->where('o.name1 IN (?)', $hosts)
|
||||
->where('o.objecttype_id = 1')
|
||||
->where('o.is_active = 1');
|
||||
|
||||
$select_services = $this->db->select()->from(
|
||||
array('ss' => $this->prefix . 'servicestatus'),
|
||||
array(
|
||||
'state' => 'ss.' . $state_column,
|
||||
'ack' => 'ss.problem_has_been_acknowledged',
|
||||
'in_downtime' => 'CASE WHEN (d.object_id IS NULL) THEN 0 ELSE 1 END',
|
||||
'combined' => 'ss.current_state << 2 + ss.problem_has_been_acknowledged << 1 + CASE WHEN (d.object_id IS NULL) THEN 0 ELSE 1 END'
|
||||
)
|
||||
)->joinRight(
|
||||
array('o' => $this->prefix . 'objects'),
|
||||
'ss.service_object_id = o.object_id',
|
||||
array(
|
||||
'object_id' => 'o.object_id',
|
||||
'hostname' => 'o.name1',
|
||||
'service' => 'o.name2'
|
||||
)
|
||||
)->joinLeft(
|
||||
array('d' => $this->prefix . 'scheduleddowntime'),
|
||||
'o.object_id = d.object_id'
|
||||
. ' AND d.was_started = 1'
|
||||
. ' AND d.scheduled_end_time > NOW()'
|
||||
. ' AND d.actual_start_time < NOW()',
|
||||
array()
|
||||
)->where('o.name1 IN (?)', $hosts)
|
||||
->where('o.is_active = 1')
|
||||
->where('o.objecttype_id = 2');
|
||||
|
||||
$union = $this->db->select()->union(
|
||||
array(
|
||||
'(' . $select_hosts . ')', // ZF-4338 :-(
|
||||
'(' . $select_services . ')',
|
||||
),
|
||||
// At least on MySQL UNION ALL seems to be faster than UNION in
|
||||
// most situations, as it doesn't care about duplicates
|
||||
\Zend_Db_Select::SQL_UNION_ALL
|
||||
)->order('hostname')->order('service');
|
||||
|
||||
return $this->db->fetchAll($union);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Backend\Ido;
|
||||
abstract class GroupsummaryQuery extends Query
|
||||
{
|
||||
protected $name_alias;
|
||||
protected $sub_group_column;
|
||||
protected $sub_query;
|
||||
protected $sub_count_query;
|
||||
|
||||
protected $available_columns = array(
|
||||
'ok' => 'SUM(CASE WHEN state = 0 THEN 1 ELSE 0 END)',
|
||||
'critical' => 'SUM(CASE WHEN state = 2 AND downtime = 0 AND ack = 0 THEN 1 ELSE 0 END)',
|
||||
'critical_dt' => 'SUM(CASE WHEN state = 2 AND downtime = 1 THEN 1 ELSE 0 END)',
|
||||
'critical_ack' => 'SUM(CASE WHEN state = 2 AND downtime = 0 AND ack = 1 THEN 1 ELSE 0 END)',
|
||||
'unknown' => 'SUM(CASE WHEN state = 3 AND downtime = 0 AND ack = 0 THEN 1 ELSE 0 END)',
|
||||
'unknown_dt' => 'SUM(CASE WHEN state = 3 AND downtime = 1 THEN 1 ELSE 0 END)',
|
||||
'unknown_ack' => 'SUM(CASE WHEN state = 3 AND downtime = 0 AND ack = 1 THEN 1 ELSE 0 END)',
|
||||
'warning' => 'SUM(CASE WHEN state = 1 AND downtime = 0 AND ack = 0 THEN 1 ELSE 0 END)',
|
||||
'warning_dt' => 'SUM(CASE WHEN state = 1 AND downtime = 1 THEN 1 ELSE 0 END)',
|
||||
'warning_ack' => 'SUM(CASE WHEN state = 1 AND downtime = 0 AND ack = 1 THEN 1 ELSE 0 END)',
|
||||
'last_state_change' => 'UNIX_TIMESTAMP(MAX(last_state_change))',
|
||||
);
|
||||
|
||||
protected $order_columns = array(
|
||||
'state' => array(
|
||||
'ASC' => array(
|
||||
'ok ASC',
|
||||
'warning_dt ASC',
|
||||
'warning_ack ASC',
|
||||
'warning ASC',
|
||||
'unknown_dt ASC',
|
||||
'unknown_ack ASC',
|
||||
'unknown ASC',
|
||||
'critical_dt ASC',
|
||||
'critical_ack ASC',
|
||||
'critical ASC',
|
||||
),
|
||||
'DESC' => array(
|
||||
'critical DESC',
|
||||
'unknown DESC',
|
||||
'warning DESC',
|
||||
'critical_ack DESC',
|
||||
'critical_dt DESC',
|
||||
'unknown_ack DESC',
|
||||
'unknown_dt DESC',
|
||||
'warning_ack DESC',
|
||||
'warning_dt DESC',
|
||||
'ok DESC',
|
||||
),
|
||||
'default' => 'DESC'
|
||||
)
|
||||
);
|
||||
|
||||
abstract protected function addSummaryJoins($query);
|
||||
|
||||
protected function init()
|
||||
{
|
||||
parent::init();
|
||||
if ($this->dbtype === 'oracle') {
|
||||
$this->columns['last_state_change'] = 'localts2unixts(MAX(last_state_change))';
|
||||
}
|
||||
}
|
||||
|
||||
protected function createQuery()
|
||||
{
|
||||
$this->columns[$this->name_alias] = $this->name_alias;
|
||||
$this->order_columns['state']['ASC'][] = $this->name_alias . ' ASC';
|
||||
$this->order_columns['state']['DESC'][] = $this->name_alias . ' DESC';
|
||||
$this->order_columns['name'] = array(
|
||||
'ASC' => array( $this->name_alias . ' ASC'),
|
||||
'DESC' => array( $this->name_alias . ' DESC'),
|
||||
'default' => 'ASC'
|
||||
);
|
||||
$sub_query = $this->createSubQuery();
|
||||
// $sub_query->group($this->sub_group_column);
|
||||
// $sub_query->columns(array($this->name_alias => 'MAX(' . $this->sub_group_column . ')'));
|
||||
$sub_query->columns(array($this->name_alias => $this->sub_group_column ));
|
||||
$this->addSummaryJoins($sub_query);
|
||||
$query = $this->db->select()->from(
|
||||
array('sub' => $sub_query),
|
||||
array()
|
||||
);
|
||||
$query->group($this->name_alias);
|
||||
$this->sub_query = $sub_query;
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
protected function createCountQuery()
|
||||
{
|
||||
$this->sub_count_query = $this->createCountSubQuery();
|
||||
$this->sub_count_query->group($this->sub_group_column);
|
||||
$this->addSummaryJoins($this->sub_count_query);
|
||||
$count = $this->db->select()->from(
|
||||
array('cnt' => $this->sub_count_query),
|
||||
array()
|
||||
);
|
||||
return $count;
|
||||
}
|
||||
|
||||
protected function createSubQuery()
|
||||
{
|
||||
|
||||
$query = $this->db->select()
|
||||
->from(
|
||||
array('so' => $this->prefix . 'objects'),
|
||||
array(
|
||||
// MAX seems to be useless, but is required as of the GROUP below
|
||||
// 'state' => 'MAX(ss.current_state)',
|
||||
'state' => 'ss.current_state',
|
||||
// 'ack' => 'MAX(ss.problem_has_been_acknowledged)',
|
||||
'ack' => 'ss.problem_has_been_acknowledged',
|
||||
// 'downtime' => 'MAX(CASE WHEN (dt.object_id IS NULL) THEN 0 ELSE 1 END)',
|
||||
// 'downtime' => 'MAX(CASE WHEN (scheduled_downtime_depth = 0) THEN 0 ELSE 1 END)',
|
||||
'downtime' => 'CASE WHEN (scheduled_downtime_depth = 0) THEN 0 ELSE 1 END',
|
||||
// 'last_state_change' => 'MAX(ss.last_state_change)',
|
||||
'last_state_change' => 'ss.last_state_change',
|
||||
)
|
||||
)->joinLeft(
|
||||
array('ss' => $this->prefix . 'servicestatus'),
|
||||
"so.$this->object_id = ss.service_object_id",
|
||||
array()
|
||||
)->join(
|
||||
array('s' => $this->prefix . 'services'),
|
||||
's.service_object_id = ss.service_object_id',
|
||||
array()
|
||||
)/*->joinLeft(
|
||||
array('dt' => $this->prefix . 'scheduleddowntime'),
|
||||
"so.$this->object_id = dt.object_id"
|
||||
. ' AND dt.is_in_effect = 1',
|
||||
array()
|
||||
)->joinLeft(
|
||||
array('co' => $this->prefix . 'comments'),
|
||||
"so.$this->object_id = co.object_id",
|
||||
array()
|
||||
)*/
|
||||
->where('so.is_active = 1')
|
||||
->where('so.objecttype_id = 2')
|
||||
// Group is required as there could be multiple comments:
|
||||
// ->group('so.' . $this->object_id)
|
||||
;
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function createCountSubQuery()
|
||||
{
|
||||
return $this->db->select()
|
||||
->from(
|
||||
array('so' => $this->prefix . 'objects'),
|
||||
array('state' => 'MAX(ss.current_state)')
|
||||
)->joinLeft(
|
||||
array('ss' => $this->prefix . 'servicestatus'),
|
||||
"so.$this->object_id = ss.service_object_id",
|
||||
array()
|
||||
)->join(
|
||||
array('s' => $this->prefix . 'services'),
|
||||
's.service_object_id = ss.service_object_id',
|
||||
array()
|
||||
);
|
||||
}
|
||||
|
||||
public function where($column, $value = null)
|
||||
{
|
||||
if ($column === 'problems') {
|
||||
if ($value === 'true') {
|
||||
$this->query->having('(SUM(CASE WHEN state = 2 AND downtime = 0 AND ack = 0 THEN 1 ELSE 0 END) +
|
||||
SUM(CASE WHEN state = 2 AND downtime = 1 THEN 1 ELSE 0 END) +
|
||||
SUM(CASE WHEN state = 2 AND downtime = 0 AND ack = 1 THEN 1 ELSE 0 END) +
|
||||
SUM(CASE WHEN state = 3 AND downtime = 0 AND ack = 0 THEN 1 ELSE 0 END) +
|
||||
SUM(CASE WHEN state = 3 AND downtime = 1 THEN 1 ELSE 0 END) +
|
||||
SUM(CASE WHEN state = 3 AND downtime = 0 AND ack = 1 THEN 1 ELSE 0 END) +
|
||||
SUM(CASE WHEN state = 1 AND downtime = 0 AND ack = 0 THEN 1 ELSE 0 END) +
|
||||
SUM(CASE WHEN state = 1 AND downtime = 1 THEN 1 ELSE 0 END) +
|
||||
SUM(CASE WHEN state = 1 AND downtime = 0 AND ack = 1 THEN 1 ELSE 0 END)) > 0');
|
||||
$this->sub_count_query->where('ss.current_state > 0');
|
||||
$this->sub_query->where('ss.current_state > 0');
|
||||
}
|
||||
} elseif ($column === 'search') {
|
||||
if ($value) {
|
||||
// $this->sub_query->where($this->name_alias . ' LIKE ?', '%' . $value . '%');
|
||||
$this->sub_query->where($this->sub_group_column . ' LIKE ?', '%' . $value . '%');
|
||||
$this->sub_count_query->where($this->sub_group_column . ' LIKE ?', '%' . $value . '%');
|
||||
}
|
||||
} else {
|
||||
parent::where($column, $value);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Backend\Ido;
|
||||
class HostgroupsummaryQuery extends GroupsummaryQuery
|
||||
{
|
||||
protected $name_alias = 'hostgroup_name';
|
||||
protected $sub_group_column = 'hg.alias';
|
||||
|
||||
protected function addSummaryJoins($query)
|
||||
{
|
||||
$this->joinServiceHostgroups($query);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,521 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Backend\Ido;
|
||||
abstract class Query extends \Icinga\Backend\Query
|
||||
{
|
||||
protected $db;
|
||||
protected $prefix;
|
||||
|
||||
protected $query;
|
||||
protected $count_query;
|
||||
protected $count_columns;
|
||||
|
||||
protected $ordered = false;
|
||||
protected $finalized = false;
|
||||
protected $object_id = 'object_id';
|
||||
protected $hostgroup_id = 'hostgroup_id';
|
||||
protected $servicegroup_id = 'servicegroup_id';
|
||||
|
||||
protected $custom_cols = array();
|
||||
|
||||
/**
|
||||
* Available sort combinations
|
||||
*/
|
||||
protected $order_columns = array(
|
||||
'host' => array(
|
||||
'ASC' => array(
|
||||
'host_name ASC',
|
||||
'service_description ASC'
|
||||
),
|
||||
'DESC' => array(
|
||||
'host_name DESC',
|
||||
'service_description ASC'
|
||||
),
|
||||
'default' => 'ASC'
|
||||
),
|
||||
'host_address' => array(
|
||||
'ASC' => array(
|
||||
'host_ipv4 ASC',
|
||||
'service_description ASC'
|
||||
),
|
||||
'DESC' => array(
|
||||
'host_ipv4 ASC',
|
||||
'service_description ASC'
|
||||
),
|
||||
'default' => 'ASC'
|
||||
),
|
||||
'service' => array(
|
||||
'ASC' => array(
|
||||
'service_description ASC'
|
||||
),
|
||||
'DESC' => array(
|
||||
'service_description DESC'
|
||||
),
|
||||
'default' => 'ASC'
|
||||
),
|
||||
'service_state_change' => array(
|
||||
'ASC' => array(
|
||||
'ss.last_state_change ASC'
|
||||
),
|
||||
'DESC' => array(
|
||||
'ss.last_state_change DESC'
|
||||
),
|
||||
'default' => 'DESC'
|
||||
),
|
||||
'service_state' => array(
|
||||
'ASC' => array(
|
||||
'CASE WHEN (ss.current_state = 3) THEN 2 WHEN (ss.current_state = 2) THEN 3 ELSE ss.current_state END DESC', // TODO: distinct severity in a better way
|
||||
'ss.problem_has_been_acknowledged ASC',
|
||||
// 'CASE WHEN (ss.scheduled_downtime_depth = 0) THEN 0 ELSE 1 END ASC',
|
||||
'service_in_downtime ASC', // TODO: Check if all dbs allow sorting by alias
|
||||
'ss.last_state_change DESC',
|
||||
'so.name1 ASC',
|
||||
'so.name2 ASC'
|
||||
),
|
||||
'DESC' => array(
|
||||
'CASE WHEN (ss.current_state = 3) THEN 2 WHEN (ss.current_state = 2) THEN 3 ELSE ss.current_state END ASC',
|
||||
'ss.problem_has_been_acknowledged ASC',
|
||||
// 'CASE WHEN (ss.scheduled_downtime_depth = 0) THEN 0 ELSE 1 END ASC',
|
||||
'service_in_downtime ASC',
|
||||
'ss.last_state_change DESC'
|
||||
),
|
||||
'default' => 'ASC'
|
||||
)
|
||||
);
|
||||
|
||||
abstract protected function createQuery();
|
||||
|
||||
public function dump()
|
||||
{
|
||||
$this->finalize();
|
||||
return "QUERY\n=====\n"
|
||||
. $this->query
|
||||
. "\n\nCOUNT\n=====\n"
|
||||
. $this->count_query
|
||||
. "\n\n";
|
||||
}
|
||||
|
||||
public function getCountQueryObject()
|
||||
{
|
||||
return $this->finalize()->count_query;
|
||||
}
|
||||
|
||||
public function getQueryObject()
|
||||
{
|
||||
return $this->finalize()->query;
|
||||
}
|
||||
|
||||
protected function createCountQuery()
|
||||
{
|
||||
return clone($this->query);
|
||||
}
|
||||
|
||||
protected function init()
|
||||
{
|
||||
$this->db = $this->backend->getAdapter();
|
||||
$this->dbtype = $this->backend->getDbType();
|
||||
if ($this->dbtype === 'oracle') {
|
||||
$this->object_id = $this->hostgroup_id = $this->servicegroup_id = 'id';
|
||||
}
|
||||
$this->prefix = $this->backend->getPrefix();
|
||||
$this->query = $this->createQuery();
|
||||
$this->count_query = $this->createCountQuery();
|
||||
}
|
||||
|
||||
protected function finalize()
|
||||
{
|
||||
if ($this->finalized) return $this;
|
||||
$this->finalized = true;
|
||||
$this->query->columns($this->columns);
|
||||
if ($this->count_columns === null) {
|
||||
$this->count_columns = array('cnt' => 'COUNT(*)');
|
||||
}
|
||||
if (! $this->ordered) {
|
||||
$this->order();
|
||||
}
|
||||
$this->count_query->columns($this->count_columns);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function prepareServiceStatesQuery()
|
||||
{
|
||||
$query = $this->db->select()
|
||||
->from(
|
||||
array('hs' => $this->prefix . 'hoststatus'),
|
||||
array()
|
||||
)->join(
|
||||
array('h' => $this->prefix . 'hosts'),
|
||||
'hs.host_object_id = h.host_object_id',
|
||||
array()
|
||||
)->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()
|
||||
);
|
||||
// $this->joinServiceDowntimes($query);
|
||||
// $query->group('so.object_id');
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function prepareServicesCount()
|
||||
{
|
||||
// TODO: Depends on filter, some cols could be avoided
|
||||
$query = $this->db->select()->from(
|
||||
array('hs' => $this->prefix . 'hoststatus'),
|
||||
array()
|
||||
)->join(
|
||||
array('h' => $this->prefix . 'hosts'),
|
||||
'hs.host_object_id = h.host_object_id',
|
||||
array()
|
||||
)->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",
|
||||
"COUNT(so.$this->object_id)"
|
||||
)->joinLeft(
|
||||
array('ss' => $this->prefix . 'servicestatus'),
|
||||
"so.$this->object_id = ss.service_object_id",
|
||||
array()
|
||||
);
|
||||
// $this->joinServiceDowntimes($query);
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function joinHostgroups($query = null)
|
||||
{
|
||||
if ($query === null) $query = $this->query;
|
||||
|
||||
$query->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($query)
|
||||
{
|
||||
if ($query === null) $query = $this->query;
|
||||
|
||||
$query->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()
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function joinServicegroups($query)
|
||||
{
|
||||
if ($query === null) $query = $this->query;
|
||||
|
||||
$query->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()
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function joinServiceDowntimes($query)
|
||||
{
|
||||
$query->joinLeft(
|
||||
array('dt' => $this->prefix . 'scheduleddowntime'),
|
||||
"so.$this->object_id = dt.object_id"
|
||||
. ' AND dt.is_in_effect = 1',
|
||||
array()
|
||||
);
|
||||
// NDO compat (doesn't work correctly like this):
|
||||
// $now = "'" . date('Y-m-d H:i:s') . "'";
|
||||
// . ' AND dt.was_started = 1'
|
||||
// . ' AND dt.scheduled_end_time > ' . $now
|
||||
// . ' AND dt.actual_start_time < ' . $now,
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function where($column, $value = null)
|
||||
{
|
||||
// Ugly temporary hack:
|
||||
foreach (array($this->query, $this->count_query) as $query) {
|
||||
if ($column === 'search') {
|
||||
if ($this->dbtype === 'mysql') {
|
||||
$query->where($this->db->quoteInto(
|
||||
'so.name2 COLLATE latin1_general_ci LIKE ?'
|
||||
. ' OR so.name1 COLLATE latin1_general_ci LIKE ?',
|
||||
'%' . $value . '%',
|
||||
'%' . $value . '%'
|
||||
));
|
||||
} else {
|
||||
$query->where($this->db->quoteInto(
|
||||
'LOWER(so.name2) LIKE ?'
|
||||
. ' OR LOWER(so.name1) LIKE ?',
|
||||
'%' . strtolower($value) . '%',
|
||||
'%' . strtolower($value) . '%'
|
||||
));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// TODO: Check if this also works based on column:
|
||||
if ($column === 'hostgroups') {
|
||||
$this->appendHostgroupLimit($query, $value);
|
||||
continue;
|
||||
}
|
||||
if (preg_match('~^_([^_]+)_(.+)$~', $column, $m)) {
|
||||
switch($m[1]) {
|
||||
case 'host':
|
||||
$this->appendHostCustomVarLimit($query, $m[2], $value);
|
||||
break;
|
||||
case 'service':
|
||||
$this->appendServiceCustomVarLimit($query, $m[2], $value);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
//$column = preg_replace('~^current_state~', 'ss.current_state', $column);
|
||||
if (array_key_exists($column, $this->available_columns)) {
|
||||
$column = $this->available_columns[$column];
|
||||
}
|
||||
$query->where($this->prepareFilterStringForColumn($column, $value));
|
||||
}
|
||||
|
||||
/*->orWhere('last_state_change > ?', $new)*/
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function order($column = '', $dir = null)
|
||||
{
|
||||
$this->ordered = true;
|
||||
return $this->applyOrder($column, $dir);
|
||||
}
|
||||
|
||||
protected function applyOrder($order = '', $order_dir = null)
|
||||
{
|
||||
if (! array_key_exists($order, $this->order_columns)) {
|
||||
$order = key($this->order_columns);
|
||||
}
|
||||
|
||||
if ($order_dir === null) {
|
||||
$order_dir = $this->order_columns[$order]['default'];
|
||||
}
|
||||
foreach ($this->order_columns[$order][$order_dir] as $col) {
|
||||
$this->query->order($col);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function addServiceComments($query = null)
|
||||
{
|
||||
if ($query === null) {
|
||||
$query = $this->query;
|
||||
}
|
||||
$query->joinLeft(
|
||||
array('co' => $this->prefix . 'comments'),
|
||||
"so.$this->object_id = co.object_id",
|
||||
array()
|
||||
)
|
||||
|
||||
->group('so.object_id')
|
||||
|
||||
;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* $column = col
|
||||
* $value = abc,cde,cd*,!egh,!*hh*
|
||||
* -> (col IN ('abc', 'cde') OR col LIKE 'cd%') AND (col != 'egh' AND col NOT LIKE '%hh%')
|
||||
*/
|
||||
protected function prepareFilterStringForColumn($column, $value)
|
||||
{
|
||||
$filter = '';
|
||||
$filters = array();
|
||||
|
||||
$or = array();
|
||||
$and = array();
|
||||
|
||||
if (strpos($value, ',') !== false) {
|
||||
$value = preg_split('~,~', $value, -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
if (! is_array($value)) {
|
||||
$value = array($value);
|
||||
}
|
||||
|
||||
// Go through all given values
|
||||
foreach ($value as $val) {
|
||||
// Value starting with - means negation
|
||||
if ($val[0] === '-') {
|
||||
$val = substr($val, 1);
|
||||
if (strpos($val, '*') === false) {
|
||||
$and[] = $this->db->quoteInto($column . ' != ?', $val);
|
||||
} else {
|
||||
$and[] = $this->db->quoteInto(
|
||||
$column . ' NOT LIKE ?',
|
||||
str_replace('*', '%', $val)
|
||||
);
|
||||
}
|
||||
// Starting with + enforces AND
|
||||
} elseif ($val[0] === '+') {
|
||||
$val = substr($val, 1);
|
||||
if (strpos($val, '*') === false) {
|
||||
$and[] = $this->db->quoteInto($column . ' = ?', $val);
|
||||
} else {
|
||||
$and[] = $this->db->quoteInto(
|
||||
$column . ' LIKE ?',
|
||||
str_replace('*', '%', $val)
|
||||
);
|
||||
}
|
||||
// All others ar ORs:
|
||||
} else {
|
||||
if (strpos($val, '*') === false) {
|
||||
$or[] = $this->db->quoteInto($column . ' = ?', $val);
|
||||
} else {
|
||||
$or[] = $this->db->quoteInto(
|
||||
$column . ' LIKE ?',
|
||||
str_replace('*', '%', $val)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($or)) { $filters[] = implode(' OR ', $or); }
|
||||
if (! empty($and)) { $filters[] = implode(' AND ', $and); }
|
||||
if (! empty($filters)) {
|
||||
$filter = '(' . implode(') AND (', $filters) . ')';
|
||||
}
|
||||
|
||||
return $filter;
|
||||
}
|
||||
|
||||
protected function addCustomVarColumn($query, $alias, $name, $filter = null)
|
||||
{
|
||||
// TODO: Improve this:
|
||||
if (! preg_match('~^[a-zA-Z0-9_]+$~', $name)) {
|
||||
throw new \Exception(sprintf(
|
||||
'Got invalid custom var: "%s"',
|
||||
$name
|
||||
));
|
||||
}
|
||||
$qobj = spl_object_hash($query);
|
||||
if (! array_key_exists($qobj, $this->custom_cols)) {
|
||||
$this->custom_cols[$qobj] = array();
|
||||
}
|
||||
|
||||
if (array_key_exists($alias, $this->custom_cols[$qobj])) {
|
||||
if ($name !== $this->custom_cols[$qobj][$alias]) {
|
||||
throw new \Exception(sprintf(
|
||||
'Cannot add CV alias "%s" twice with different target',
|
||||
$alias
|
||||
));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
$query->join(
|
||||
// TODO: Allow multiple limits with different aliases
|
||||
array($alias => $this->prefix . 'customvariablestatus'),
|
||||
's.host_object_id = ' . $alias . '.object_id'
|
||||
. ' AND ' . $alias . '.varname = '
|
||||
. $this->db->quote(strtoupper($name))
|
||||
//. ($filter === null ? '' : ' AND ' . $filter),
|
||||
,
|
||||
array()
|
||||
);
|
||||
$this->custom_cols[$qobj][$alias] = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function appendHostCustomVarLimit($query, $key, $value)
|
||||
{
|
||||
$alias = 'hcv_' . strtolower($key);
|
||||
$filter = $this->prepareFilterStringForColumn($alias . '.varvalue', $value);
|
||||
$this->addCustomVarColumn($query, $alias, $key);
|
||||
$query->where($filter);
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function appendHostgroupLimit($query, $hostgroups)
|
||||
{
|
||||
return $query->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()
|
||||
)
|
||||
->where('hg.alias IN (?)', $hostgroups);
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return $this->db->fetchOne(
|
||||
$this->finalize()->count_query
|
||||
);
|
||||
}
|
||||
|
||||
public function fetchAll()
|
||||
{
|
||||
return $this->db->fetchAll($this->finalize()->query);
|
||||
}
|
||||
|
||||
public function fetchRow()
|
||||
{
|
||||
return $this->db->fetchRow($this->finalize()->query);
|
||||
}
|
||||
|
||||
public function fetchOne()
|
||||
{
|
||||
return $this->db->fetchOne($this->finalize()->query);
|
||||
}
|
||||
|
||||
public function fetchPairs()
|
||||
{
|
||||
return $this->db->fetchPairs($this->finalize()->query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a limit count and offset to the query
|
||||
*
|
||||
* @param int $count Number of rows to return
|
||||
* @param int $offset Row offset to start from
|
||||
* @return \Icinga\Backend\Query This Query object
|
||||
*/
|
||||
public function limit($count = null, $offset = null)
|
||||
{
|
||||
$this->query->limit($count, $offset);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$this->finalize();
|
||||
return (string) $this->query;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Backend\Ido;
|
||||
class ServicegroupsummaryQuery extends GroupsummaryQuery
|
||||
{
|
||||
protected $name_alias = 'servicegroup_name';
|
||||
protected $sub_group_column = 'sg.alias';
|
||||
|
||||
protected function addSummaryJoins($query)
|
||||
{
|
||||
$this->joinServicegroups($query);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Backend\Ido;
|
||||
class ServicelistQuery extends Query
|
||||
{
|
||||
protected $available_columns = array(
|
||||
// Host config
|
||||
'host_name' => 'so.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 state
|
||||
'host_state' => 'hs.current_state',
|
||||
'host_output' => 'hs.output',
|
||||
'host_perfdata' => 'hs.perfdata',
|
||||
'host_acknowledged' => 'hs.problem_has_been_acknowledged',
|
||||
'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)',
|
||||
|
||||
// Service config
|
||||
'service_description' => 'so.name2',
|
||||
'service_display_name' => 's.display_name',
|
||||
|
||||
// Service state
|
||||
'current_state' => 'ss.current_state',
|
||||
'service_state' => 'ss.current_state',
|
||||
'service_output' => 'ss.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 > 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 comments
|
||||
//'service_downtimes_with_info' => "IF(dt.object_id IS NULL, NULL, GROUP_CONCAT(CONCAT('[', dt.author_name, '] ', dt.comment_data) ORDER BY dt.entry_time DESC SEPARATOR '|'))",
|
||||
//'service_comments_with_info' => "IF(co.object_id IS NULL, NULL, GROUP_CONCAT(CONCAT('[', co.author_name, '] ', co.comment_data) ORDER BY co.entry_time DESC SEPARATOR '|'))",
|
||||
// SLA Example:
|
||||
// 'sla' => "icinga_availability(so.object_id,"
|
||||
// . " '2012-12-01 00:00:00', '2012-12-31 23:59:59')",
|
||||
);
|
||||
|
||||
protected function init()
|
||||
{
|
||||
parent::init();
|
||||
if ($this->dbtype === 'oracle') {
|
||||
$this->columns['host_last_state_change'] =
|
||||
'localts2unixts(ss.last_state_change)';
|
||||
$this->columns['service_last_state_change'] =
|
||||
'localts2unixts(ss.last_state_change)';
|
||||
}
|
||||
}
|
||||
|
||||
public function where($column, $value = null)
|
||||
{
|
||||
// Ugly temporary hack:
|
||||
if ($column === 'problems') {
|
||||
if ($value === true || $value === 'true') {
|
||||
parent::where('current_state', '-0');
|
||||
} elseif ($value === false || $value === 'false') {
|
||||
parent::where('current_state', '0');
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($column === 'handled') $column = 'service_handled';
|
||||
parent::where($column, $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function createQuery()
|
||||
{
|
||||
$query = $this->prepareServiceStatesQuery();
|
||||
if ($this->dbtype === 'mysql') {
|
||||
// $this->addServiceComments($query);
|
||||
} else {
|
||||
$this->columns['host_ipv4'] = 'h.address';
|
||||
$this->columns['service_downtimes_with_info'] = '(NULL)';
|
||||
$this->columns['service_comments_with_info'] = '(NULL)';
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function createCountQuery()
|
||||
{
|
||||
return $this->prepareServicesCount();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Backend\Ido;
|
||||
class StatehistoryQuery extends Query
|
||||
{
|
||||
protected $available_columns = array(
|
||||
// Host config
|
||||
'host_name' => 'sho.name1',
|
||||
'service_description' => 'sho.name2',
|
||||
'object_type' => "CASE WHEN sho.objecttype_id = 1 THEN 'host' ELSE 'service' END",
|
||||
'timestamp' => 'UNIX_TIMESTAMP(sh.state_time)',
|
||||
'state' => 'sh.state',
|
||||
'last_state' => 'sh.last_state',
|
||||
'last_hard_state' => 'sh.last_hard_state',
|
||||
'attempt' => 'sh.current_check_attempt',
|
||||
'max_attempts' => 'sh.max_check_attempts',
|
||||
'output' => 'sh.output', // no long_output in browse
|
||||
|
||||
);
|
||||
protected $order_columns = array(
|
||||
'timestamp' => array(
|
||||
'ASC' => array(
|
||||
'state_time ASC',
|
||||
),
|
||||
'DESC' => array(
|
||||
'state_time DESC',
|
||||
),
|
||||
'default' => 'DESC'
|
||||
)
|
||||
);
|
||||
|
||||
protected function init()
|
||||
{
|
||||
parent::init();
|
||||
if ($this->dbtype === 'oracle') {
|
||||
$this->columns['timestamp'] =
|
||||
'localts2unixts(sh.state_time)';
|
||||
}
|
||||
}
|
||||
|
||||
public function where($column, $value = null)
|
||||
{
|
||||
if ($column === 'problems') {
|
||||
if ($value === 'true') {
|
||||
foreach (array($this->query, $this->count_query) as $query) {
|
||||
$query->where('sh.state > 0');
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
if ($column === 'host') {
|
||||
foreach (array($this->query, $this->count_query) as $query) {
|
||||
$query->where('sho.name1 = ?', $value);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
if ($column === 'service') {
|
||||
foreach (array($this->query, $this->count_query) as $query) {
|
||||
$query->where('sho.name2 = ?', $value);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
parent::where($column, $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function createQuery()
|
||||
{
|
||||
$query = $this->db->select()->from(
|
||||
array('sh' => $this->prefix . 'statehistory'),
|
||||
array()
|
||||
// )->join(
|
||||
)->joinLeft( // LEFT is bullshit but greatly helps MySQL
|
||||
// Problem -> has to be INNER once permissions are in effect
|
||||
// Therefore this should probably be "flexible" or handled in another
|
||||
// way
|
||||
array('sho' => $this->prefix . 'objects'),
|
||||
'sho.' . $this->object_id . ' = sh.object_id AND sho.is_active = 1',
|
||||
array()
|
||||
);
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Icinga Livestatus Backend
|
||||
*
|
||||
* @package Icinga\Backend
|
||||
*/
|
||||
namespace Icinga\Backend;
|
||||
use Icinga\Protocol\Livestatus\Connection;
|
||||
|
||||
/**
|
||||
* This class provides an easy-to-use interface to the Livestatus socket library
|
||||
*
|
||||
* You should usually not directly use this class but go through Icinga\Backend.
|
||||
*
|
||||
* @copyright Copyright (c) 2013 Icinga-Web Team <info@icinga.org>
|
||||
* @author Icinga-Web Team <info@icinga.org>
|
||||
* @package Icinga\Application
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
|
||||
*/
|
||||
class Livestatus extends AbstractBackend
|
||||
{
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Backend initialization starts here
|
||||
*
|
||||
* return void
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
$this->connection = new Connection($this->config->socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our Livestatus connection
|
||||
*
|
||||
* return \Icinga\Protocol\Livestatus\Connection
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Backend\Livestatus;
|
||||
abstract class Query extends \Icinga\Backend\Query
|
||||
{
|
||||
protected $connection;
|
||||
|
||||
protected $query;
|
||||
|
||||
protected $ordered = false;
|
||||
protected $finalized = false;
|
||||
|
||||
/**
|
||||
* Available sort combinations
|
||||
*/
|
||||
protected $order_columns = array(
|
||||
'host' => array(
|
||||
'ASC' => array(
|
||||
'host_name ASC',
|
||||
'service_description ASC'
|
||||
),
|
||||
'DESC' => array(
|
||||
'host_name DESC',
|
||||
'service_description ASC'
|
||||
),
|
||||
'default' => 'ASC'
|
||||
),
|
||||
'host_address' => array(
|
||||
'ASC' => array(
|
||||
'host_ipv4 ASC',
|
||||
'service_description ASC'
|
||||
),
|
||||
'DESC' => array(
|
||||
'host_ipv4 ASC',
|
||||
'service_description ASC'
|
||||
),
|
||||
'default' => 'ASC'
|
||||
),
|
||||
'service' => array(
|
||||
'ASC' => array(
|
||||
'service_description ASC'
|
||||
),
|
||||
'DESC' => array(
|
||||
'service_description DESC'
|
||||
),
|
||||
'default' => 'ASC'
|
||||
),
|
||||
'service_state_change' => array(
|
||||
'ASC' => array(
|
||||
'ss.last_state_change ASC'
|
||||
),
|
||||
'DESC' => array(
|
||||
'ss.last_state_change DESC'
|
||||
),
|
||||
'default' => 'DESC'
|
||||
),
|
||||
'service_state' => array(
|
||||
'ASC' => array(
|
||||
'IF (ss.current_state = 3, 2, IF(ss.current_state = 2, 3, ss.current_state)) DESC',
|
||||
'ss.problem_has_been_acknowledged ASC',
|
||||
'IF(dt.object_id IS NULL, 0, 1) ASC',
|
||||
'ss.last_state_change DESC'
|
||||
),
|
||||
'DESC' => array(
|
||||
'IF (ss.current_state = 3, 2, IF(ss.current_state = 3, 2, ss.current_state)) DESC',
|
||||
'ss.problem_has_been_acknowledged ASC',
|
||||
'IF(dt.object_id IS NULL, 0, 1) ASC',
|
||||
'ss.last_state_change DESC'
|
||||
),
|
||||
'default' => 'ASC'
|
||||
)
|
||||
);
|
||||
|
||||
abstract protected function createQuery();
|
||||
|
||||
protected function init()
|
||||
{
|
||||
$this->connection = $this->backend->getConnection();
|
||||
$this->query = $this->createQuery();
|
||||
}
|
||||
|
||||
public function where($column, $value = null)
|
||||
{
|
||||
if ($column === 'problems') {
|
||||
if ($value === 'true') {
|
||||
$this->query->where('state > 0');
|
||||
} elseif ($value === 'false') {
|
||||
$this->query->where('state = 0');
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
if ($column === 'handled') {
|
||||
if ($value === 'true') {
|
||||
// TODO: Not yet
|
||||
} elseif ($value === 'false') {
|
||||
// TODO: Not yet
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Ugly temporary hack:
|
||||
$colcheck = preg_replace('~[\s=><].+$~', '', $column);
|
||||
if (array_key_exists($colcheck, $this->available_columns)) {
|
||||
$query->where(preg_replace(
|
||||
'~' . $colcheck . '~',
|
||||
$this->available_columns[$colcheck],
|
||||
$column
|
||||
), $value);
|
||||
} else {
|
||||
$this->query->where($column, $value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function finalize()
|
||||
{
|
||||
return $this;
|
||||
|
||||
if ($this->finalized) return $this;
|
||||
$this->finalized = true;
|
||||
$this->query->columns($this->columns);
|
||||
if ($this->count_columns === null) {
|
||||
$this->count_columns = array('cnt' => 'COUNT(*)');
|
||||
}
|
||||
if (! $this->ordered) {
|
||||
$this->order();
|
||||
}
|
||||
$this->count_query->columns($this->count_columns);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function applyFilters($filters = array())
|
||||
{
|
||||
foreach ($filters as $key => $val) {
|
||||
$this->where($key, $val);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function order($column = '', $dir = null)
|
||||
{
|
||||
$this->ordered = true;
|
||||
return $this->applyOrder($column, $dir);
|
||||
}
|
||||
|
||||
protected function applyOrder($order = '', $order_dir = null)
|
||||
{
|
||||
return $this;
|
||||
|
||||
if (! array_key_exists($order, $this->order_columns)) {
|
||||
$order = key($this->order_columns);
|
||||
}
|
||||
|
||||
if ($order_dir === null) {
|
||||
$order_dir = $this->order_columns[$order]['default'];
|
||||
}
|
||||
foreach ($this->order_columns[$order][$order_dir] as $col) {
|
||||
$this->query->order($col);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return $this->connection->count(
|
||||
$this->finalize()->query
|
||||
);
|
||||
}
|
||||
|
||||
public function fetchAll()
|
||||
{
|
||||
return $this->connection->fetchAll($this->finalize()->query);
|
||||
}
|
||||
|
||||
public function fetchRow()
|
||||
{
|
||||
return $this->db->fetchRow($this->finalize()->query);
|
||||
}
|
||||
|
||||
public function fetchOne()
|
||||
{
|
||||
return $this->db->fetchOne($this->finalize()->query);
|
||||
}
|
||||
|
||||
public function fetchPairs()
|
||||
{
|
||||
return $this->db->fetchPairs($this->finalize()->query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a limit count and offset to the query
|
||||
*
|
||||
* @param int $count Number of rows to return
|
||||
* @param int $offset Row offset to start from
|
||||
* @return \Icinga\Backend\Query This Query object
|
||||
*/
|
||||
public function limit($count = null, $offset = null)
|
||||
{
|
||||
$this->query->limit($count, $offset);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
// For debugging and testing only:
|
||||
public function __toString()
|
||||
{
|
||||
$this->finalize();
|
||||
return (string) $this->query;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Backend\Livestatus;
|
||||
class ServicelistQuery extends Query
|
||||
{
|
||||
protected $available_columns = array(
|
||||
'host_name',
|
||||
'host_display_name',
|
||||
'host_alias',
|
||||
'host_address',
|
||||
'host_ipv4' => 'host_address', // TODO
|
||||
'host_icon_image',
|
||||
|
||||
// Host state
|
||||
'host_state',
|
||||
'host_output' => 'host_plugin_output',
|
||||
'host_perfdata' => 'host_perf_data',
|
||||
'host_acknowledged',
|
||||
'host_does_active_checks' => 'host_active_checks_enabled',
|
||||
'host_accepts_passive_checks' => 'host_accept_passive_checks',
|
||||
'host_last_state_change',
|
||||
|
||||
// Service config
|
||||
'service_description' => 'description',
|
||||
'service_display_name' => 'display_name',
|
||||
|
||||
// Service state
|
||||
'service_state' => 'state',
|
||||
'service_output' => 'plugin_output',
|
||||
'service_perfdata' => 'perf_data',
|
||||
'service_acknowledged' => 'acknowledged',
|
||||
'service_does_active_checks' => 'active_checks_enabled',
|
||||
'service_accepts_passive_checks' => 'accept_passive_checks',
|
||||
'service_last_state_change' => 'last_state_change',
|
||||
|
||||
// Service comments
|
||||
'comments_with_info',
|
||||
'downtimes_with_info',
|
||||
);
|
||||
|
||||
protected function createQuery()
|
||||
{
|
||||
return $this->connection->select()->from('services', $this->available_columns);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
namespace Icinga\Backend;
|
||||
|
||||
/**
|
||||
* Wrapper around an array of monitoring objects that can be enhanced with an optional
|
||||
* object that extends AbstractAccessorStrategy. This will act as a dataview and provide
|
||||
* normalized access to the underlying data (mapping properties, retrieving additional data)
|
||||
*
|
||||
* If not Accessor is set, this class just behaves like a normal Iterator and returns
|
||||
* the underlying objects.
|
||||
*
|
||||
* If the dataset contains arrays instead of objects, they will be cast to objects.
|
||||
*
|
||||
*/
|
||||
use Icinga\Backend\DataView as DataView;
|
||||
|
||||
class MonitoringObjectList implements \Iterator, \Countable
|
||||
{
|
||||
private $dataSet = array();
|
||||
private $position = 0;
|
||||
private $dataView = null;
|
||||
|
||||
function __construct(array &$dataset, DataView\AbstractAccessorStrategy $dataView = null)
|
||||
{
|
||||
$this->dataSet = $dataset;
|
||||
$this->position = 0;
|
||||
$this->dataView = $dataView;
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->dataSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* (PHP 5 >= 5.0.0)<br/>
|
||||
* Return the current element
|
||||
* @link http://php.net/manual/en/iterator.current.php
|
||||
* @return mixed Can return any type.
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
if ($this->dataView)
|
||||
return $this;
|
||||
return $this->dataSet[$this->position];
|
||||
}
|
||||
|
||||
/**
|
||||
* (PHP 5 >= 5.0.0)<br/>
|
||||
* Move forward to next element
|
||||
* @link http://php.net/manual/en/iterator.next.php
|
||||
* @return void Any returned value is ignored.
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this->position++;
|
||||
}
|
||||
|
||||
/**
|
||||
* (PHP 5 >= 5.0.0)<br/>
|
||||
* Return the key of the current element
|
||||
* @link http://php.net/manual/en/iterator.key.php
|
||||
* @return mixed scalar on success, or null on failure.
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* (PHP 5 >= 5.0.0)<br/>
|
||||
* Checks if current position is valid
|
||||
* @link http://php.net/manual/en/iterator.valid.php
|
||||
* @return boolean The return value will be casted to boolean and then evaluated.
|
||||
* Returns true on success or false on failure.
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->position < count($this->dataSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* (PHP 5 >= 5.0.0)<br/>
|
||||
* Rewind the Iterator to the first element
|
||||
* @link http://php.net/manual/en/iterator.rewind.php
|
||||
* @return void Any returned value is ignored.
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->position = 0;
|
||||
}
|
||||
|
||||
public function __isset($name)
|
||||
{
|
||||
return $this->dataView->exists($this->dataSet[$this->position],$name);
|
||||
}
|
||||
|
||||
function __get($name)
|
||||
{
|
||||
return $this->dataView->get($this->dataSet[$this->position],$name);
|
||||
|
||||
|
||||
}
|
||||
|
||||
function __set($name, $value)
|
||||
{
|
||||
throw new \Exception("Setting is currently not available for objects");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,376 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Data;
|
||||
|
||||
use Icinga\Exception;
|
||||
|
||||
abstract class AbstractQuery
|
||||
{
|
||||
/**
|
||||
* Sort ascending
|
||||
*/
|
||||
const SORT_ASC = 1;
|
||||
|
||||
/**
|
||||
* Sort descending
|
||||
*/
|
||||
const SORT_DESC = -1;
|
||||
|
||||
/**
|
||||
* Query data source
|
||||
*
|
||||
* @var DatasourceInterface
|
||||
*/
|
||||
protected $ds;
|
||||
|
||||
/**
|
||||
* The table you are going to query
|
||||
*/
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* The columns you are interested in. All columns if empty
|
||||
*/
|
||||
protected $columns = array();
|
||||
|
||||
/**
|
||||
* A list of filters
|
||||
*/
|
||||
protected $filters = array();
|
||||
|
||||
/**
|
||||
* The columns you're using to sort the query result
|
||||
*/
|
||||
protected $order_columns = array();
|
||||
|
||||
/**
|
||||
* Return not more than that many rows
|
||||
*/
|
||||
protected $limit_count;
|
||||
|
||||
/**
|
||||
* Result starts with this row
|
||||
*/
|
||||
protected $limit_offset;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param DatasourceInterface $ds Your data source
|
||||
*/
|
||||
public function __construct(DatasourceInterface $ds, $columns = null)
|
||||
{
|
||||
$this->ds = $ds;
|
||||
|
||||
if ($columns === null) {
|
||||
$columns = $this->getDefaultColumns();
|
||||
}
|
||||
if ($columns !== null) {
|
||||
$this->columns($columns);
|
||||
}
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
protected function getDefaultColumns()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose a table and the colums you are interested in
|
||||
*
|
||||
* Query will return all available columns if none are given here
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function from($table, $columns = null)
|
||||
{
|
||||
$this->table = $table;
|
||||
if ($columns !== null) {
|
||||
$this->columns($columns);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function columns($columns)
|
||||
{
|
||||
if (is_array($columns)) {
|
||||
$this->columns = $columns;
|
||||
} else {
|
||||
$this->columns = array($columns);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use once or multiple times to filter result set
|
||||
*
|
||||
* Multiple where calls will be combined by a logical AND operation
|
||||
*
|
||||
* @param string $key Column or backend-specific search expression
|
||||
* @param string $val Search value, must be escaped automagically
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function where($key, $val = null)
|
||||
{
|
||||
$this->filters[] = array($key, $val);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort query result by the given column name
|
||||
*
|
||||
* Sort direction can be ascending (self::SORT_ASC, being the default)
|
||||
* or descending (self::SORT_DESC).
|
||||
*
|
||||
* Preferred usage:
|
||||
* <code>
|
||||
* $query->sort('column_name ASC')
|
||||
* </code>
|
||||
*
|
||||
* @param string $col Column, may contain direction separated by space
|
||||
* @param int $dir Sort direction
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function order($col, $dir = null)
|
||||
{
|
||||
if ($dir === null) {
|
||||
if (($pos = strpos($col, ' ')) === false) {
|
||||
$dir = $this->getDefaultSortDir($col);
|
||||
} else {
|
||||
$dir = strtoupper(substr($col, $pos + 1));
|
||||
if ($dir === 'DESC') {
|
||||
$dir = self::SORT_DESC;
|
||||
} else {
|
||||
$dir = self::SORT_ASC;
|
||||
}
|
||||
$col = substr($col, 0, $pos);
|
||||
}
|
||||
} else {
|
||||
if (strtoupper($dir) === 'DESC') {
|
||||
$dir = self::SORT_DESC;
|
||||
} else {
|
||||
$dir = self::SORT_ASC;
|
||||
}
|
||||
}
|
||||
$this->order_columns[] = array($col, $dir);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getDefaultSortDir($col)
|
||||
{
|
||||
return self::SORT_ASC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the result set
|
||||
*
|
||||
* @param int $count Return not more than that many rows
|
||||
* @param int $offset Result starts with this row
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
// Nur wenn keine stats, sonst im RAM!!
|
||||
// Offset gibt es nicht, muss simuliert werden
|
||||
public function limit($count = null, $offset = null)
|
||||
{
|
||||
if (! preg_match('~^\d+~', $count . $offset)) {
|
||||
throw new Exception\ProgrammingError(
|
||||
sprintf(
|
||||
'Got invalid limit: %s, %s',
|
||||
$count,
|
||||
$offset
|
||||
)
|
||||
);
|
||||
}
|
||||
$this->limit_count = (int) $count;
|
||||
$this->limit_offset = (int) $offset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wheter at least one order column has been applied to this Query
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasOrder()
|
||||
{
|
||||
return ! empty($this->order_columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wheter a limit has been applied to this Query
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasLimit()
|
||||
{
|
||||
return $this->limit_count !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wheter a starting offset been applied to this Query
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasOffset()
|
||||
{
|
||||
return $this->limit_offset > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query limit
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getLimit()
|
||||
{
|
||||
return $this->limit_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query starting offset
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getOffset()
|
||||
{
|
||||
return $this->limit_offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the columns that have been asked for with this query
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listColumns()
|
||||
{
|
||||
return $this->columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filters that have been applied to this query
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listFilters()
|
||||
{
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend this function for things that should happen at construction time
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend this function for things that should happen before query execution
|
||||
*/
|
||||
protected function finish()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Total result size regardless of limit and offset
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return $this->ds->count($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch result as an array of objects
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAll()
|
||||
{
|
||||
return $this->ds->fetchAll($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch first result row
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function fetchRow()
|
||||
{
|
||||
return $this->ds->fetchRow($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch first result column
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchColumn()
|
||||
{
|
||||
return $this->ds->fetchColumn($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch first column value from first result row
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function fetchOne()
|
||||
{
|
||||
return $this->ds->fetchOne($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch result as a key/value pair array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchPairs()
|
||||
{
|
||||
return $this->ds->fetchPairs($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pagination adapter for this query
|
||||
*
|
||||
* @return \Zend_Paginator
|
||||
*/
|
||||
public function paginate($limit = null, $page = null)
|
||||
{
|
||||
$this->finish();
|
||||
if ($page === null && $limit === null) {
|
||||
$request = \Zend_Controller_Front::getInstance()->getRequest();
|
||||
|
||||
if ($page === null) {
|
||||
$page = $request->getParam('page', 0);
|
||||
}
|
||||
|
||||
if ($limit === null) {
|
||||
$limit = $request->getParam('limit', 20);
|
||||
}
|
||||
}
|
||||
|
||||
$paginator = new \Zend_Paginator(
|
||||
new \Icinga\Web\Paginator\Adapter\QueryAdapter($this)
|
||||
);
|
||||
|
||||
$paginator->setItemCountPerPage($limit);
|
||||
$paginator->setCurrentPageNumber($page);
|
||||
|
||||
return $paginator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor. Remove $ds, just to be on the safe side
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
unset($this->ds);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Data;
|
||||
|
||||
class ArrayDatasource implements DatasourceInterface
|
||||
{
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Constructor, create a new Datasource for the given Array
|
||||
*
|
||||
* @param array $array The array you're going to use as a data source
|
||||
*/
|
||||
public function __construct(array $array)
|
||||
{
|
||||
$this->data = (array) $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate an ArrayQuery object
|
||||
*
|
||||
* @return ArrayQuery
|
||||
*/
|
||||
public function select()
|
||||
{
|
||||
return new ArrayQuery($this);
|
||||
}
|
||||
|
||||
public function fetchColumn(ArrayQuery $query)
|
||||
{
|
||||
$result = array();
|
||||
foreach ($this->getResult($query) as $row) {
|
||||
$arr = (array) $row;
|
||||
$result[] = array_shift($arr);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function fetchAll(ArrayQuery $query)
|
||||
{
|
||||
$result = $this->getResult($query);
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function count(ArrayQuery $query)
|
||||
{
|
||||
$this->createResult($query);
|
||||
return $query->getCount();
|
||||
}
|
||||
|
||||
protected function createResult(ArrayQuery $query)
|
||||
{
|
||||
if ($query->hasResult()) {
|
||||
return $this;
|
||||
}
|
||||
$result = array();
|
||||
$filters = $query->listFilters();
|
||||
$columns = $query->listColumns();
|
||||
foreach ($this->data as & $row) {
|
||||
|
||||
// Skip rows that do not match applied filters
|
||||
foreach ($filters as $f) {
|
||||
if ($row->{$f[0]} !== $f[1]) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Get only desired columns if asked so
|
||||
if (empty($columns)) {
|
||||
$result[] = $row;
|
||||
} else {
|
||||
$c_row = (object) array();
|
||||
foreach ($columns as $key) {
|
||||
if (isset($row->$key)) {
|
||||
$c_row->$key = $row->$key;
|
||||
} else {
|
||||
$c_row->$key = null;
|
||||
}
|
||||
}
|
||||
$result[] = $c_row;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the result
|
||||
if ($query->hasOrder()) {
|
||||
usort($result, array($query, 'compare'));
|
||||
}
|
||||
|
||||
$query->setResult($result);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getResult(ArrayQuery $query)
|
||||
{
|
||||
if (! $query->hasResult()) {
|
||||
$this->createResult($query);
|
||||
}
|
||||
return $query->getLimitedResult();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Data;
|
||||
|
||||
class ArrayQuery extends AbstractQuery
|
||||
{
|
||||
/**
|
||||
* Remember the last count
|
||||
*/
|
||||
protected $count;
|
||||
|
||||
/**
|
||||
* Remember the last result without applied limits
|
||||
*/
|
||||
protected $result;
|
||||
|
||||
public function getCount()
|
||||
{
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
public function hasResult()
|
||||
{
|
||||
return $this->result !== null;
|
||||
}
|
||||
|
||||
public function getFullResult()
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public function getLimitedResult()
|
||||
{
|
||||
if ($this->hasLimit()) {
|
||||
if ($this->hasOffset()) {
|
||||
$offset = $this->getOffset();
|
||||
} else {
|
||||
$offset = 0;
|
||||
}
|
||||
return array_slice($this->result, $offset, $this->getLimit());
|
||||
} else {
|
||||
return $this->result;
|
||||
}
|
||||
}
|
||||
|
||||
public function setResult($result)
|
||||
{
|
||||
$this->result = $result;
|
||||
$this->count = count($result);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* ArrayDatasource will apply this function to sort the array
|
||||
*
|
||||
* @param mixed $a Left side comparsion value
|
||||
* @param mixed $b Right side comparsion value
|
||||
* @param int $col_num Current position in order_columns array
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function compare(& $a, & $b, $col_num = 0)
|
||||
{
|
||||
if (! array_key_exists($col_num, $this->order_columns)) {
|
||||
return 0;
|
||||
}
|
||||
$col = $this->order_columns[$col_num][0];
|
||||
$dir = $this->order_columns[$col_num][1];
|
||||
|
||||
//$res = strnatcmp(strtolower($a->$col), strtolower($b->$col));
|
||||
$res = strcmp(strtolower($a->$col), strtolower($b->$col));
|
||||
if ($res === 0) {
|
||||
if (array_key_exists(++$col_num, $this->order_columns)) {
|
||||
return $this->compare($a, $b, $col_num);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if ($dir === self::SORT_ASC) {
|
||||
return $res;
|
||||
} else {
|
||||
return $res * -1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
// TODO: create interface instead of abstract class
|
||||
namespace Icinga\Data;
|
||||
|
||||
interface DatasourceInterface
|
||||
{
|
||||
/**
|
||||
* Instantiate a Query object
|
||||
*
|
||||
* @return AbstractQuery
|
||||
*/
|
||||
public function select();
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Data\Db;
|
||||
|
||||
use Icinga\Data\DatasourceInterface;
|
||||
|
||||
class Connection implements DatasourceInterface
|
||||
{
|
||||
protected $db;
|
||||
protected $config;
|
||||
protected $dbtype;
|
||||
|
||||
public function __construct(\Zend_Config $config = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->connect();
|
||||
$this->init();
|
||||
}
|
||||
|
||||
public function select()
|
||||
{
|
||||
return new Query($this);
|
||||
}
|
||||
|
||||
public function getDbType()
|
||||
{
|
||||
return $this->dbtype;
|
||||
}
|
||||
|
||||
public function getDb()
|
||||
{
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
protected function init()
|
||||
{
|
||||
}
|
||||
|
||||
protected function connect()
|
||||
{
|
||||
$this->dbtype = $this->config->get('dbtype', 'mysql');
|
||||
|
||||
$options = array(
|
||||
\Zend_Db::AUTO_QUOTE_IDENTIFIERS => false,
|
||||
\Zend_Db::CASE_FOLDING => \Zend_Db::CASE_LOWER
|
||||
);
|
||||
|
||||
$drv_options = array(
|
||||
\PDO::ATTR_TIMEOUT => 2,
|
||||
// TODO: Check whether LC is useful. Zend_Db does fetchNum for Oci:
|
||||
\PDO::ATTR_CASE => \PDO::CASE_LOWER
|
||||
// TODO: ATTR_ERRMODE => ERRMODE_EXCEPTION vs ERRMODE_SILENT
|
||||
);
|
||||
|
||||
switch ($this->dbtype) {
|
||||
case 'mysql':
|
||||
$adapter = 'Pdo_Mysql';
|
||||
$drv_options[\PDO::MYSQL_ATTR_INIT_COMMAND] =
|
||||
"SET SESSION SQL_MODE='STRICT_ALL_TABLES,NO_ZERO_IN_DATE,"
|
||||
. "NO_ZERO_DATE,NO_ENGINE_SUBSTITUTION';";
|
||||
// Not using ONLY_FULL_GROUP_BY as of performance impact
|
||||
// TODO: NO_ZERO_IN_DATE as been added with 5.1.11. Is it
|
||||
// ignored by other versions?
|
||||
$port = $this->config->get('port', 3306);
|
||||
break;
|
||||
case 'pgsql':
|
||||
$adapter = 'Pdo_Pgsql';
|
||||
$port = $this->config->get('port', 5432);
|
||||
break;
|
||||
case 'oracle':
|
||||
$adapter = 'Pdo_Oci';
|
||||
// $adapter = 'Oracle';
|
||||
$port = $this->config->get('port', 1521);
|
||||
// $drv_options[\PDO::ATTR_STRINGIFY_FETCHES] = true;
|
||||
|
||||
if ($adapter === 'Oracle') {
|
||||
// Unused right now
|
||||
putenv('ORACLE_SID=XE');
|
||||
putenv('ORACLE_HOME=/u01/app/oracle/product/11.2.0/xe');
|
||||
putenv('PATH=$PATH:$ORACLE_HOME/bin');
|
||||
putenv('ORACLE_BASE=/u01/app/oracle');
|
||||
putenv('NLS_LANG=AMERICAN_AMERICA.UTF8');
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new \Exception(sprintf(
|
||||
'Backend "%s" is not supported', $type
|
||||
));
|
||||
}
|
||||
$attributes = array(
|
||||
'host' => $this->config->host,
|
||||
'port' => $port,
|
||||
'username' => $this->config->user,
|
||||
'password' => $this->config->pass,
|
||||
'dbname' => $this->config->db,
|
||||
'options' => $options,
|
||||
'driver_options' => $drv_options
|
||||
);
|
||||
if ($this->dbtype === 'oracle') {
|
||||
$attributes['persistent'] = true;
|
||||
}
|
||||
$this->db = \Zend_Db::factory($adapter, $attributes);
|
||||
if ($adapter === 'Oracle') {
|
||||
$this->db->setLobAsString(false);
|
||||
}
|
||||
|
||||
// TODO: Zend_Db::FETCH_ASSOC for Oracle?
|
||||
$this->db->setFetchMode(\Zend_Db::FETCH_OBJ);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Data\Db;
|
||||
|
||||
use Icinga\Data\AbstractQuery;
|
||||
|
||||
class Query extends AbstractQuery
|
||||
{
|
||||
/**
|
||||
* Zend_Db_Adapter_Abstract
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* Base Query will be prepared here, has tables and cols
|
||||
* shared by full & count query
|
||||
*/
|
||||
protected $baseQuery;
|
||||
|
||||
/**
|
||||
* Select object
|
||||
*/
|
||||
protected $selectQuery;
|
||||
|
||||
/**
|
||||
* Select object used for count query
|
||||
*/
|
||||
protected $countQuery;
|
||||
|
||||
/**
|
||||
* Allow to override COUNT(*)
|
||||
*/
|
||||
protected $countColumns;
|
||||
|
||||
protected function init()
|
||||
{
|
||||
$this->db = $this->ds->getConnection()->getDb();
|
||||
$this->baseQuery = $this->db->select();
|
||||
}
|
||||
|
||||
protected function getSelectQuery()
|
||||
{
|
||||
if ($this->selectQuery === null) {
|
||||
$this->createQueryObjects();
|
||||
}
|
||||
|
||||
if ($this->hasLimit()) {
|
||||
$this->selectQuery->limit($this->getLimit(), $this->getOffset());
|
||||
}
|
||||
return $this->selectQuery;
|
||||
}
|
||||
|
||||
protected function getCountQuery()
|
||||
{
|
||||
if ($this->countQuery === null) {
|
||||
$this->createQueryObjects();
|
||||
}
|
||||
return $this->countQuery;
|
||||
}
|
||||
|
||||
protected function createQueryObjects()
|
||||
{
|
||||
$this->beforeCreatingCountQuery();
|
||||
$this->countQuery = clone($this->baseQuery);
|
||||
if ($this->countColumns === null) {
|
||||
$this->countColumns = array('cnt' => 'COUNT(*)');
|
||||
}
|
||||
$this->countQuery->columns($this->countColumns);
|
||||
|
||||
$this->beforeCreatingSelectQuery();
|
||||
$this->selectQuery = clone($this->baseQuery);
|
||||
$this->selectQuery->columns($this->columns);
|
||||
if ($this->hasOrder()) {
|
||||
foreach ($this->order_columns as $col) {
|
||||
$this->selectQuery->order(
|
||||
$col[0]
|
||||
. ' '
|
||||
. ( $col[1] === self::SORT_DESC ? 'DESC' : 'ASC')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function beforeCreatingCountQuery()
|
||||
{
|
||||
}
|
||||
|
||||
protected function beforeCreatingSelectQuery()
|
||||
{
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return $this->db->fetchOne($this->getCountQuery());
|
||||
}
|
||||
|
||||
public function fetchAll()
|
||||
{
|
||||
return $this->db->fetchAll($this->getSelectQuery());
|
||||
}
|
||||
|
||||
public function fetchRow()
|
||||
{
|
||||
return $this->db->fetchRow($this->getSelectQuery());
|
||||
}
|
||||
|
||||
public function fetchOne()
|
||||
{
|
||||
return $this->db->fetchOne($this->getSelectQuery());
|
||||
}
|
||||
|
||||
public function fetchPairs()
|
||||
{
|
||||
return $this->db->fetchPairs($this->getSelectQuery());
|
||||
}
|
||||
|
||||
public function dump()
|
||||
{
|
||||
return "QUERY\n=====\n"
|
||||
. $this->getSelectQuery()
|
||||
. "\n\nCOUNT\n=====\n"
|
||||
. $this->getCountQuery()
|
||||
. "\n\n";
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->getSelectQuery();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Data Filter
|
||||
*/
|
||||
namespace Icinga\Data;
|
||||
|
||||
use ArrayIterator;
|
||||
|
||||
/**
|
||||
* This class contains an array of filters
|
||||
*
|
||||
* @package Icinga\Data
|
||||
* @author Icinga-Web Team <info@icinga.org>
|
||||
* @copyright 2013 Icinga-Web Team <info@icinga.org>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
|
||||
*/
|
||||
class Filter extends ArrayIterator
|
||||
{
|
||||
|
||||
public function without($keys)
|
||||
{
|
||||
$filter = new Filter();
|
||||
$params = $this->toParams();
|
||||
if (! is_array($keys)) {
|
||||
$keys = array($keys);
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
if (array_key_exists($key, $params)) {
|
||||
unset($params[$key]);
|
||||
}
|
||||
}
|
||||
foreach ($params as $k => $v) {
|
||||
$filter[] = array($k, $v);
|
||||
}
|
||||
return $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filtere as key-value array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toParams()
|
||||
{
|
||||
$params = array();
|
||||
|
||||
foreach ($this as $filter) {
|
||||
$params[$filter[0]] = $filter[1];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Objects;
|
||||
|
||||
class Host extends Object
|
||||
{
|
||||
// Nothing here
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Objects;
|
||||
|
||||
class Object
|
||||
{
|
||||
protected $id;
|
||||
protected $name;
|
||||
protected $props;
|
||||
protected $defaults = array();
|
||||
protected $fromBackend = false;
|
||||
protected $hasBeenChanged = false;
|
||||
|
||||
protected function __construct($props = array())
|
||||
{
|
||||
$this->props = $this->defaults;
|
||||
if (! empty($props)) {
|
||||
$this->setProperties($props);
|
||||
}
|
||||
}
|
||||
|
||||
public function setProperties($props)
|
||||
{
|
||||
foreach ($props as $key => $val) {
|
||||
$this->props[$key] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
protected function set($key, $val)
|
||||
{
|
||||
$this->props[$key] = $val;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __set($key, $val)
|
||||
{
|
||||
$this->set($key, $val);
|
||||
}
|
||||
|
||||
public function __get($key)
|
||||
{
|
||||
if (array_key_exists($key, $this->props)) {
|
||||
return $this->props[$key];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function setLoadedFromBackend($loaded = true)
|
||||
{
|
||||
$this->fromBackend = $loaded;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public static function fromBackend($row)
|
||||
{
|
||||
$class = get_called_class();
|
||||
$object = new $class($row);
|
||||
$object->setLoadedFromBackend();
|
||||
return $object;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Objects;
|
||||
|
||||
class Service extends Object
|
||||
{
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Pdf;
|
||||
|
||||
use TCPDF;
|
||||
|
||||
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_MAIN', dirname(ICINGA_LIBDIR) . '/public');
|
||||
define('K_PATH_FONTS', ICINGA_LIBDIR . '/vendor/tcpdf/fonts/');
|
||||
define('K_PATH_CACHE', ICINGA_LIBDIR . '/vendor/tcpdf/cache/');
|
||||
define('K_PATH_URL_CACHE', ICINGA_LIBDIR . '/vendor/tcpdf/cache/');
|
||||
//define('K_PATH_IMAGES', K_PATH_MAIN . 'images/'); // ???
|
||||
define('K_PATH_IMAGES', dirname(ICINGA_LIBDIR) . '/public'); // ???
|
||||
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_THROW_EXCEPTION_ERROR', true);
|
||||
|
||||
|
||||
require_once 'vendor/tcpdf/tcpdf.php';
|
||||
|
||||
class File extends TCPDF
|
||||
{
|
||||
protected $cell_height_ratio = 1.25;
|
||||
public function __construct(
|
||||
$orientation = 'P',
|
||||
$unit = 'mm',
|
||||
$format = 'A4',
|
||||
$unicode = true,
|
||||
$encoding = 'UTF-8',
|
||||
$diskcache = false,
|
||||
$pdfa = false
|
||||
) {
|
||||
parent::__construct(
|
||||
$orientation,
|
||||
$unit,
|
||||
$format,
|
||||
$unicode,
|
||||
$encoding,
|
||||
$diskcache,
|
||||
$pdfa
|
||||
);
|
||||
|
||||
$this->SetCreator('IcingaWeb');
|
||||
$this->SetAuthor('IcingaWeb Team');
|
||||
$this->SetTitle('IcingaWeb Sample PDF - Title');
|
||||
$this->SetSubject('IcingaWeb Sample PDF - Subject');
|
||||
$this->SetKeywords('IcingaWeb, Monitoring');
|
||||
|
||||
// set default header data
|
||||
// $pdf->SetHeaderData('tcpdf_logo.jpg', 30, 'Header title',
|
||||
// 'Header string', array(0,64,255), array(0,64,128));
|
||||
// $pdf->setFooterData($tc=array(0,64,0), $lc=array(0,64,128));
|
||||
|
||||
$this->setHeaderFont(array('helvetica', '', 10));
|
||||
$this->setFooterFont(array('helvetica', '', 8));
|
||||
$this->SetDefaultMonospacedFont('courier');
|
||||
|
||||
$this->SetMargins(15, 27, 15); // left, top, right
|
||||
$this->SetHeaderMargin(5);
|
||||
$this->SetFooterMargin(10);
|
||||
|
||||
$this->SetAutoPageBreak(true, 25); // margin bottom
|
||||
$this->setImageScale(1.75);
|
||||
|
||||
$lang = array(
|
||||
'a_meta_charset' => 'UTF-8',
|
||||
'a_meta_dir' => 'ltr',
|
||||
'a_meta_language' => 'de',
|
||||
'w_page' => 'Seite',
|
||||
);
|
||||
$this->setLanguageArray($lang);
|
||||
|
||||
|
||||
$this->setFontSubsetting(true);
|
||||
$this->SetFont('dejavusans', '', 16, '', true);
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ class LdapUtils
|
|||
public static function explodeDN($dn, $with_type = true)
|
||||
{
|
||||
$res = ldap_explode_dn($dn, $with_type ? 0 : 1);
|
||||
|
||||
foreach ($res as $k => $v) {
|
||||
$res[$k] = preg_replace(
|
||||
'/\\\([0-9a-f]{2})/ei',
|
||||
|
|
|
@ -0,0 +1,249 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Protocol\Livestatus;
|
||||
class Connection
|
||||
{
|
||||
const TYPE_UNIX = 1;
|
||||
const TYPE_TCP = 2;
|
||||
|
||||
protected $available_tables = array(
|
||||
'hosts', // hosts
|
||||
'services', // services, joined with all data from hosts
|
||||
'hostgroups', // hostgroups
|
||||
'servicegroups', // servicegroups
|
||||
'contactgroups', // contact groups
|
||||
'servicesbygroup', // all services grouped by service groups
|
||||
'servicesbyhostgroup', // all services grouped by host groups
|
||||
'hostsbygroup', // all hosts grouped by host groups
|
||||
'contacts', // contacts
|
||||
'commands', // defined commands
|
||||
'timeperiods', // time period definitions (currently only name
|
||||
// and alias)
|
||||
'downtimes', // all scheduled host and service downtimes,
|
||||
// joined with data from hosts and services.
|
||||
'comments', // all host and service comments
|
||||
'log', // a transparent access to the nagios logfiles
|
||||
// (include archived ones)ones
|
||||
'status', // general performance and status information.
|
||||
// This table contains exactly one dataset.
|
||||
'columns', // a complete list of all tables and columns
|
||||
// available via Livestatus, including
|
||||
// descriptions!
|
||||
'statehist', // 1.2.1i2 sla statistics for hosts and services,
|
||||
// joined with data from hosts, services and log.
|
||||
);
|
||||
|
||||
protected $socket_path;
|
||||
protected $socket_host;
|
||||
protected $socket_port;
|
||||
protected $socket_type;
|
||||
protected $connection;
|
||||
|
||||
public function hasTable($name)
|
||||
{
|
||||
return in_array($name, $this->available_tables);
|
||||
}
|
||||
|
||||
public function __construct($socket = '/var/lib/icinga/rw/live')
|
||||
{
|
||||
$this->assertPhpExtensionLoaded('sockets');
|
||||
if ($socket[0] === '/') {
|
||||
if (! is_writable($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
|
||||
));
|
||||
}
|
||||
// TODO: Better syntax checks
|
||||
$this->socket_host = $m[1];
|
||||
$this->socket_port = (int) $m[2];
|
||||
$this->socket_type = self::TYPE_TCP;
|
||||
}
|
||||
}
|
||||
|
||||
public function select()
|
||||
{
|
||||
$select = new Query($this);
|
||||
return $select;
|
||||
}
|
||||
|
||||
public function count(Query $query)
|
||||
{
|
||||
$count = clone($query);
|
||||
$count->count();
|
||||
\Icinga\Benchmark::measure('Sending Livestatus Count Query');
|
||||
$data = $this->_fetch((string) $count);
|
||||
\Icinga\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');
|
||||
if ($query->hasColumns()) {
|
||||
$headers = $query->getColumnAliases();
|
||||
} else {
|
||||
$headers = array_shift($data);
|
||||
}
|
||||
$result = array();
|
||||
foreach ($data as $row) {
|
||||
$result_row = & $result[];
|
||||
$result_row = (object) array();
|
||||
foreach ($row as $key => $val) {
|
||||
$result_row->{$headers[$key]} = $val;
|
||||
}
|
||||
}
|
||||
if ($query->hasOrder()) {
|
||||
usort($result, array($query, 'compare'));
|
||||
}
|
||||
if ($query->hasLimit()) {
|
||||
$result = array_slice(
|
||||
$result,
|
||||
$query->getOffset(),
|
||||
$query->getLimit()
|
||||
);
|
||||
}
|
||||
\Icinga\Benchmark::measure('Data sorted, limits applied');
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function _fetch($raw_query)
|
||||
{
|
||||
$conn = $this->getConnection();
|
||||
$this->writeToSocket($raw_query);
|
||||
$header = $this->readFromSocket(16);
|
||||
$status = (int) substr($header, 0, 3);
|
||||
$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
|
||||
));
|
||||
}
|
||||
$result = json_decode($body);
|
||||
if ($result === null) {
|
||||
throw new \Exception('Got invalid response body from livestatus');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function readFromSocket($length)
|
||||
{
|
||||
$offset = 0;
|
||||
$buffer = '';
|
||||
|
||||
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))
|
||||
));
|
||||
}
|
||||
$size = strlen($data);
|
||||
$offset += $size;
|
||||
$buffer .= $data;
|
||||
|
||||
if ($size === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($offset !== $length) {
|
||||
throw new \Exception(sprintf(
|
||||
'Got only %d instead of %d bytes from livestatus socket',
|
||||
$offset, $length
|
||||
));
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
protected function writeToSocket($data)
|
||||
{
|
||||
$res = socket_write($this->connection, $data);
|
||||
if ($res === false) {
|
||||
throw new \Exception('Writing to livestatus socket failed');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function assertPhpExtensionLoaded($name)
|
||||
{
|
||||
if (! extension_loaded($name)) {
|
||||
throw new \Exception(sprintf(
|
||||
'The extension "%s" is not loaded',
|
||||
$name
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
protected function getConnection()
|
||||
{
|
||||
if ($this->connection === null) {
|
||||
if ($this->socket_type === self::TYPE_TCP) {
|
||||
$this->establishTcpConnection();
|
||||
} else {
|
||||
$this->establishSocketConnection();
|
||||
}
|
||||
}
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
protected function establishTcpConnection()
|
||||
{
|
||||
// TODO: find a bedder place for this
|
||||
if (! defined('TCP_NODELAY')) {
|
||||
define('TCP_NODELAY', 1);
|
||||
}
|
||||
|
||||
$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))
|
||||
));
|
||||
}
|
||||
socket_set_option($this->connection, SOL_TCP, TCP_NODELAY, 1);
|
||||
}
|
||||
|
||||
protected function establishSocketConnection()
|
||||
{
|
||||
$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
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public function disconnect()
|
||||
{
|
||||
if ($this->connection) {
|
||||
socket_close($this->connection);
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->disconnect();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Protocol\Livestatus;
|
||||
use Icinga\Protocol;
|
||||
|
||||
class Query extends Protocol\AbstractQuery
|
||||
{
|
||||
|
||||
protected $connection;
|
||||
protected $table;
|
||||
protected $filters = array();
|
||||
protected $limit_count;
|
||||
protected $limit_offset;
|
||||
protected $columns;
|
||||
protected $order_columns = array();
|
||||
protected $count = false;
|
||||
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
public function getAdapter()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
public function compare(& $a, & $b, $col_num = 0)
|
||||
{
|
||||
if (! array_key_exists($col_num, $this->order_columns)) {
|
||||
return 0;
|
||||
}
|
||||
$col = $this->order_columns[$col_num][0];
|
||||
$dir = $this->order_columns[$col_num][1];
|
||||
|
||||
//$res = strnatcmp(strtolower($a->$col), strtolower($b->$col));
|
||||
$res = strcmp(strtolower($a->$col), strtolower($b->$col));
|
||||
if ($res === 0) {
|
||||
if (array_key_exists(++$col_num, $this->order_columns)) {
|
||||
return $this->compare($a, $b, $col_num);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if ($dir === self::SORT_ASC) {
|
||||
return $res;
|
||||
} else {
|
||||
return $res * -1;
|
||||
}
|
||||
}
|
||||
|
||||
public function hasOrder()
|
||||
{
|
||||
return ! empty($this->order_columns);
|
||||
}
|
||||
|
||||
public function where($key, $val = null)
|
||||
{
|
||||
$this->filters[$key] = $val;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function order($col)
|
||||
{
|
||||
if (($pos = strpos($col, ' ')) !== false) {
|
||||
$dir = strtoupper(substr($col, $pos + 1));
|
||||
if ($dir === 'DESC') {
|
||||
$dir = self::SORT_DESC;
|
||||
} else {
|
||||
$dir = self::SORT_ASC;
|
||||
}
|
||||
$col = substr($col, 0, $pos);
|
||||
} else {
|
||||
$col = $col;
|
||||
}
|
||||
$this->order_columns[] = array($col, $dir);
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Nur wenn keine stats, sonst im RAM!!
|
||||
// Offset gibt es nicht, muss simuliert werden
|
||||
public function limit($count = null, $offset = null)
|
||||
{
|
||||
if (! preg_match('~^\d+~', $count . $offset)) {
|
||||
throw new Exception(sprintf(
|
||||
'Got invalid limit: %s, %s',
|
||||
$count,
|
||||
$offset
|
||||
));
|
||||
}
|
||||
$this->limit_count = (int) $count;
|
||||
$this->limit_offset = (int) $offset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasLimit()
|
||||
{
|
||||
return $this->limit_count !== null;
|
||||
}
|
||||
|
||||
public function hasOffset()
|
||||
{
|
||||
return $this->limit_offset > 0;
|
||||
}
|
||||
|
||||
public function getLimit()
|
||||
{
|
||||
return $this->limit_count;
|
||||
}
|
||||
|
||||
public function getOffset()
|
||||
{
|
||||
return $this->limit_offset;
|
||||
}
|
||||
|
||||
public function from($table, $columns = null)
|
||||
{
|
||||
if (! $this->connection->hasTable($table)) {
|
||||
throw new Exception(sprintf(
|
||||
'This livestatus connection does not provide "%s"',
|
||||
$table
|
||||
));
|
||||
}
|
||||
$this->table = $table;
|
||||
if (is_array($columns)) {
|
||||
// TODO: check for valid names?
|
||||
$this->columns = $columns;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasColumns()
|
||||
{
|
||||
return $this->columns !== null;
|
||||
}
|
||||
|
||||
public function getColumns()
|
||||
{
|
||||
return $this->columns;
|
||||
}
|
||||
|
||||
public function getColumnAliases()
|
||||
{
|
||||
$aliases = array();
|
||||
foreach ($this->getColumns() as $key => $val) {
|
||||
if (is_int($key)) {
|
||||
$aliases[] = $val;
|
||||
} else {
|
||||
$aliases[] = $key;
|
||||
}
|
||||
}
|
||||
return $aliases;
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
$this->count = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
if ($this->table === null) {
|
||||
throw new Exception('Table is required');
|
||||
}
|
||||
$default_headers = array(
|
||||
'OutputFormat: json',
|
||||
'ResponseHeader: fixed16',
|
||||
'KeepAlive: on'
|
||||
);
|
||||
$parts = array(
|
||||
sprintf('GET %s', $this->table)
|
||||
);
|
||||
if ($this->count === false && $this->columns !== null) {
|
||||
$parts[] = 'Columns: ' . implode(' ', $this->columns);
|
||||
}
|
||||
foreach ($this->filters as $key => $val) {
|
||||
if ($key === 'search') {
|
||||
$parts[] = 'Filter: host_name ~~ ' . $val;
|
||||
$parts[] = 'Filter: description ~~ ' . $val;
|
||||
$parts[] = 'Or: 2';
|
||||
continue;
|
||||
}
|
||||
if ($val === null) {
|
||||
$parts[] = 'Filter: ' . $key;
|
||||
} elseif (strpos($key, '?') === false) {
|
||||
$parts[] = sprintf('Filter: %s = %s', $key, $val);
|
||||
} else {
|
||||
$parts[] = sprintf('Filter: %s', str_replace('?', $val, $key));
|
||||
}
|
||||
}
|
||||
if ($this->count === true) {
|
||||
$parts[] = 'Stats: state >= 0';
|
||||
}
|
||||
if (! $this->count && $this->hasLimit() && ! $this->hasOrder()) {
|
||||
$parts[] = 'Limit: ' . ($this->limit_count + $this->limit_offset);
|
||||
}
|
||||
$lql = implode("\n", $parts)
|
||||
. "\n"
|
||||
. implode("\n", $default_headers)
|
||||
. "\n\n";
|
||||
return $lql;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
unset($this->connection);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Protocol\Nrpe;
|
||||
|
||||
class Connection
|
||||
{
|
||||
protected $host;
|
||||
protected $port;
|
||||
protected $connection;
|
||||
protected $use_ssl = false;
|
||||
protected $lastReturnCode = null;
|
||||
|
||||
public function __construct($host, $port = 5666)
|
||||
{
|
||||
$this->host = $host;
|
||||
$this->port = $port;
|
||||
}
|
||||
|
||||
public function useSsl($use_ssl = true)
|
||||
{
|
||||
$this->use_ssl = $use_ssl;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function sendCommand($command, $args = null)
|
||||
{
|
||||
if (! empty($args)) {
|
||||
$command .= '!' . implode('!', $args);
|
||||
}
|
||||
|
||||
$packet = Packet::createQuery($command);
|
||||
return $this->send($packet);
|
||||
}
|
||||
|
||||
public function getLastReturnCode()
|
||||
{
|
||||
return $this->lastReturnCode;
|
||||
}
|
||||
|
||||
public function send(Packet $packet)
|
||||
{
|
||||
$conn = $this->connection();
|
||||
$bytes = $packet->getBinary();
|
||||
fputs($conn, $bytes, strlen($bytes));
|
||||
// TODO: Check result checksum!
|
||||
$result = fread($conn, 8192);
|
||||
if ($result === false) {
|
||||
throw new \Exception('CHECK_NRPE: Error receiving data from daemon.');
|
||||
} elseif (strlen($result) === 0) {
|
||||
throw new \Exception(
|
||||
'CHECK_NRPE: Received 0 bytes from daemon.'
|
||||
. ' Check the remote server logs for error messages'
|
||||
);
|
||||
}
|
||||
// TODO: CHECK_NRPE: Receive underflow - only %d bytes received (%d expected)
|
||||
$code = unpack('n', substr($result, 8, 2));
|
||||
$this->lastReturnCode = $code[1];
|
||||
$this->disconnect();
|
||||
return rtrim(substr($result, 10, -2));
|
||||
}
|
||||
|
||||
protected function connect()
|
||||
{
|
||||
$ctx = stream_context_create();
|
||||
if ($this->use_ssl) {
|
||||
// TODO: fail if not ok:
|
||||
$res = stream_context_set_option($ctx, 'ssl', 'ciphers', 'ADH');
|
||||
$uri = sprintf('ssl://%s:%d', $this->host, $this->port);
|
||||
} else {
|
||||
$uri = sprintf('tcp://%s:%d', $this->host, $this->port);
|
||||
}
|
||||
$this->connection = stream_socket_client(
|
||||
$uri,
|
||||
$errno,
|
||||
$errstr,
|
||||
60,
|
||||
STREAM_CLIENT_CONNECT,
|
||||
$ctx
|
||||
);
|
||||
if (! $this->connection) {
|
||||
throw new \Exception(sprintf('NRPE Connection failed: ' . $errstr));
|
||||
}
|
||||
}
|
||||
|
||||
protected function connection()
|
||||
{
|
||||
if ($this->connection === null) {
|
||||
$this->connect();
|
||||
}
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
protected function disconnect()
|
||||
{
|
||||
if ($this->connection !== null) {
|
||||
fclose($this->connection);
|
||||
$this->connection = null;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->disconnect();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Protocol\Nrpe;
|
||||
|
||||
class Packet
|
||||
{
|
||||
const QUERY = 0x01;
|
||||
const RESPONSE = 0x02;
|
||||
|
||||
protected $version = 0x02;
|
||||
protected $type;
|
||||
protected $body;
|
||||
protected static $randomBytes;
|
||||
|
||||
public function __construct($type, $body)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->body = $body;
|
||||
$this->regenerateRandomBytes();
|
||||
}
|
||||
|
||||
// TODO: renew "from time to time" to allow long-running daemons
|
||||
protected function regenerateRandomBytes()
|
||||
{
|
||||
self::$randomBytes = '';
|
||||
for ($i = 0; $i < 4096; $i++) {
|
||||
self::$randomBytes .= pack('N', mt_rand());
|
||||
}
|
||||
}
|
||||
|
||||
public static function createQuery($body)
|
||||
{
|
||||
$packet = new Packet(self::QUERY, $body);
|
||||
return $packet;
|
||||
}
|
||||
|
||||
protected function getFillString($length)
|
||||
{
|
||||
$max = strlen(self::$randomBytes) - $length;
|
||||
return substr(self::$randomBytes, rand(0, $max), $length);
|
||||
}
|
||||
|
||||
// TODO: WTF is SR? And 2324?
|
||||
public function getBinary()
|
||||
{
|
||||
$version = pack('n', $this->version);
|
||||
$type = pack('n', $this->type);
|
||||
$dummycrc = "\x00\x00\x00\x00";
|
||||
$result = "\x00\x00";
|
||||
$result = pack('n', 2324);
|
||||
$body = $this->body
|
||||
. "\x00"
|
||||
. $this->getFillString(1023 - strlen($this->body))
|
||||
. 'SR';
|
||||
|
||||
$crc = pack(
|
||||
'N',
|
||||
crc32($version . $type . $dummycrc . $result . $body)
|
||||
);
|
||||
$bytes = $version . $type . $crc . $result . $body;
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Util;
|
||||
|
||||
class Format
|
||||
{
|
||||
const STANDARD_IEC = 0;
|
||||
const STANDARD_SI = 1;
|
||||
protected static $instance;
|
||||
|
||||
protected static $bitPrefix = array(
|
||||
array('bit', 'Kibit', 'Mibit', 'Gibit', 'Tibit', 'Pibit', 'Eibit', 'Zibit', 'Yibit'),
|
||||
array('bit', 'kbit', 'Mbit', 'Gbit', 'Tbit', 'Pbit', 'Ebit', 'Zbit', 'Ybit'),
|
||||
);
|
||||
protected static $bitBase = array(1024, 1000);
|
||||
|
||||
protected static $bytePrefix = array(
|
||||
array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'),
|
||||
array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'),
|
||||
);
|
||||
protected static $byteBase = array(1024, 1000);
|
||||
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new Format;
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public static function bits($value, $standard = self::STANDARD_SI)
|
||||
{
|
||||
return self::formatForUnits(
|
||||
$value,
|
||||
self::$bitPrefix[$standard],
|
||||
self::$bitBase[$standard]
|
||||
);
|
||||
}
|
||||
|
||||
public static function bytes($value, $standard = self::STANDARD_IEC)
|
||||
{
|
||||
return self::formatForUnits(
|
||||
$value,
|
||||
self::$bytePrefix[$standard],
|
||||
self::$byteBase[$standard]
|
||||
);
|
||||
}
|
||||
|
||||
protected static function formatForUnits($value, & $units, $base)
|
||||
{
|
||||
$sign = '';
|
||||
if ($value < 0) {
|
||||
$value = abs($value);
|
||||
$sign = '-';
|
||||
}
|
||||
$pow = floor(log($value, $base));
|
||||
$result = $value / pow($base, $pow);
|
||||
|
||||
// 1034.23 looks better than 1.03, but 2.03 is fine:
|
||||
if ($pow > 0 && $result < 2) {
|
||||
$pow--;
|
||||
$result = $value / pow($base, $pow);
|
||||
}
|
||||
return sprintf(
|
||||
'%s%0.2f %s',
|
||||
$sign,
|
||||
$result,
|
||||
$units[$pow]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Web;
|
||||
|
||||
class Cookie
|
||||
{
|
||||
// TBD
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Web;
|
||||
|
||||
use Zend_Form;
|
||||
use Zend_Controller_Front as Front; // TODO: Get from App
|
||||
use Zend_Controller_Action_HelperBroker as ZfActionHelper;
|
||||
|
||||
class Form extends Zend_Form
|
||||
{
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* @param Zend_Controller_Request_Abstract $request
|
||||
* @param array $options[optional]
|
||||
*/
|
||||
public function __construct($options = null)
|
||||
{
|
||||
/*
|
||||
if (isset($options['prefill'])) {
|
||||
$this->_prefill = $options['prefill'];
|
||||
unset($options['prefill']);
|
||||
}
|
||||
*/
|
||||
$this->request = Front::getInstance()->getRequest();
|
||||
// $this->handleRequest();
|
||||
foreach ($this->elements() as $key => $values) {
|
||||
$this->addElement($values[0], $key, $values[1]); // do it better!
|
||||
}
|
||||
|
||||
// Should be replaced with button check:
|
||||
$this->addElement('hidden', '__submitted');
|
||||
$this->setDefaults(array('__submitted' => 'true'));
|
||||
|
||||
parent::__construct($options);
|
||||
if ($this->getAttrib('action') === null) {
|
||||
$this->setAction($this->request->getRequestUri());
|
||||
}
|
||||
if ($this->getAttrib('method') === null) {
|
||||
$this->setMethod('post');
|
||||
}
|
||||
if ($this->hasBeenSubmitted()) {
|
||||
$this->handleRequest();
|
||||
}
|
||||
}
|
||||
|
||||
public function redirectNow($url)
|
||||
{
|
||||
ZfActionHelper::getStaticHelper('redirector')
|
||||
->gotoUrlAndExit($url);
|
||||
}
|
||||
|
||||
public function handleRequest()
|
||||
{
|
||||
if ($this->isValid($this->request->getPost())) {
|
||||
$this->onSuccess();
|
||||
} else {
|
||||
$this->onFailure();
|
||||
}
|
||||
}
|
||||
|
||||
public function onSuccess()
|
||||
{
|
||||
}
|
||||
|
||||
public function onFailure()
|
||||
{
|
||||
}
|
||||
|
||||
public function hasBeenSubmitted()
|
||||
{
|
||||
return $this->request->getPost('__submitted', 'false') === 'true';
|
||||
}
|
||||
|
||||
public function elements()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Icinga Web Grapher Hook
|
||||
*/
|
||||
namespace Icinga\Web\Hook;
|
||||
|
||||
/**
|
||||
* Icinga Web Grapher Hook base class
|
||||
*
|
||||
* Extend this class if you want to integrate your graphing solution nicely into
|
||||
* Icinga Web
|
||||
*
|
||||
* @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 Grapher
|
||||
{
|
||||
/**
|
||||
* Whether this grapher provides preview images
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $hasPreviews = false;
|
||||
|
||||
/**
|
||||
* Constructor must live without arguments right now
|
||||
*
|
||||
* Therefore the constructor is final, we might change our opinion about
|
||||
* this one far day
|
||||
*/
|
||||
final public function __construct()
|
||||
{
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this grapher provides preview images
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPreviews()
|
||||
{
|
||||
return $this->hasPreviews;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite this function if you want to do some initialization stuff
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a graph for the given host[, service [, plot]] exists
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasGraph($host, $service = null, $plot = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a preview image for the given host[, service [, plot]] exists
|
||||
*
|
||||
* WARNING: We are not sure yet whether this will remain as is
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPreviewImage($host, $service = null, $plot = null)
|
||||
{
|
||||
throw new Exception('This backend has no preview images');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL pointing to the grapher
|
||||
*
|
||||
* WARNING: We are not sure yet whether this will remain as is
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getGraphUrl($host, $service = null, $plot = null)
|
||||
{
|
||||
throw new Exception('This backend has no images');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Web\Hook;
|
||||
use Icinga\Application\Logger as Logger;
|
||||
|
||||
abstract class Toptray {
|
||||
const ALIGN_LEFT = "pull-left";
|
||||
const ALIGN_NONE = "";
|
||||
const ALIGN_RIGHT = "pull-right";
|
||||
protected $align = self::ALIGN_NONE;
|
||||
|
||||
public function setAlignment($align)
|
||||
{
|
||||
$this->align = $align;
|
||||
}
|
||||
|
||||
final public function getWidgetDOM()
|
||||
{
|
||||
try {
|
||||
return '<ul class="nav '.$this->align.'" >'.$this->buildDOM().'</ul>';
|
||||
} catch (\Exception $e) {
|
||||
Logger::error("Could not create tray widget : %s",$e->getMessage());
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
abstract protected function buildDOM();
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Module action controller
|
||||
*/
|
||||
namespace Icinga\Web;
|
||||
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Icinga;
|
||||
|
||||
/**
|
||||
* Base class for all module action controllers
|
||||
*
|
||||
* All Icinga Web module controllers should extend this class
|
||||
*
|
||||
* @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 ModuleActionController extends ActionController
|
||||
{
|
||||
protected $module;
|
||||
protected $module_dir;
|
||||
|
||||
/**
|
||||
* Gives you this modules base directory
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getModuleDir()
|
||||
{
|
||||
if ($this->module_dir === null) {
|
||||
$this->module_dir = $this->getModule()->getBaseDir();
|
||||
}
|
||||
return $this->module_dir;
|
||||
}
|
||||
|
||||
public function getModule()
|
||||
{
|
||||
if ($this->module === null) {
|
||||
$this->module = Icinga::app()->getModule(
|
||||
$this->module_name
|
||||
);
|
||||
}
|
||||
return $this->module;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the given string with the modules translation catalog
|
||||
*
|
||||
* @param string $string The string that should be translated
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function translate($string)
|
||||
{
|
||||
return mt($this->module_name, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is where the module configuration is going to be loaded
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function loadConfig()
|
||||
{
|
||||
$this->config = Config::getInstance()->{$this->module_name};
|
||||
}
|
||||
|
||||
/**
|
||||
* Once dispatched we are going to place each modules output in a div
|
||||
* container having the icinga-module and the icinga-$module-name classes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function postDispatch()
|
||||
{
|
||||
parent::postDispatch();
|
||||
$this->_helper->layout()->moduleStart =
|
||||
'<div class="icinga-module module-'
|
||||
. $this->module_name
|
||||
. '">'
|
||||
. "\n"
|
||||
;
|
||||
$this->_helper->layout()->moduleEnd = "</div>\n";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Web;
|
||||
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Application\Platform;
|
||||
use Icinga\Application\Logger as Log;
|
||||
use Zend_Session_Namespace as SessionNamespace;
|
||||
|
||||
class Notification
|
||||
{
|
||||
protected static $instance;
|
||||
protected $is_cli = false;
|
||||
|
||||
public static function info($msg)
|
||||
{
|
||||
self::getInstance()->addMessage($msg, 'info');
|
||||
}
|
||||
|
||||
public static function success($msg)
|
||||
{
|
||||
self::getInstance()->addMessage($msg, 'success');
|
||||
}
|
||||
|
||||
public static function warning($msg)
|
||||
{
|
||||
self::getInstance()->addMessage($msg, 'warning');
|
||||
}
|
||||
|
||||
public static function error($msg)
|
||||
{
|
||||
self::getInstance()->addMessage($msg, 'error');
|
||||
}
|
||||
|
||||
protected function addMessage($message, $type = 'info')
|
||||
{
|
||||
if (! in_array(
|
||||
$type,
|
||||
array(
|
||||
'info',
|
||||
'error',
|
||||
'warning',
|
||||
'success'
|
||||
)
|
||||
)) {
|
||||
throw new ProgrammingError(
|
||||
sprintf(
|
||||
'"%s" is not a valid notification type',
|
||||
$type
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->is_cli) {
|
||||
$msg = sprintf('[%s] %s', $type, $message);
|
||||
switch ($type) {
|
||||
case 'info':
|
||||
case 'success':
|
||||
Log::info($msg);
|
||||
break;
|
||||
case 'warning':
|
||||
Log::warn($msg);
|
||||
break;
|
||||
case 'error':
|
||||
Log::error($msg);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$mo = (object) array(
|
||||
'type' => $type,
|
||||
'message' => $message,
|
||||
);
|
||||
|
||||
// Get, change, set - just to be on the safe side:
|
||||
$msgs = $this->session->messages;
|
||||
$msgs[] = $mo;
|
||||
$this->session->messages = $msgs;
|
||||
}
|
||||
|
||||
public function hasMessages()
|
||||
{
|
||||
return ! empty($this->session->messages);
|
||||
}
|
||||
|
||||
public function getMessages()
|
||||
{
|
||||
$msgs = $this->session->messages;
|
||||
$this->session->messages = array();
|
||||
return $msgs;
|
||||
}
|
||||
|
||||
final private function __construct()
|
||||
{
|
||||
$this->session = new SessionNamespace('IcingaNotification');
|
||||
if (! is_array($this->session->messages)) {
|
||||
$this->session->messages = array();
|
||||
}
|
||||
|
||||
if (Platform::isCli()) {
|
||||
$this->is_cli = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new Notification();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Web\Paginator\Adapter;
|
||||
/**
|
||||
* @see Zend_Paginator_Adapter_Interface
|
||||
*/
|
||||
|
||||
class QueryAdapter implements \Zend_Paginator_Adapter_Interface
|
||||
{
|
||||
/**
|
||||
* Array
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $query = null;
|
||||
|
||||
/**
|
||||
* Item count
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $count = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $query Query to paginate
|
||||
*/
|
||||
// TODO: Re-add abstract Query type as soon as a more generic one
|
||||
// is available. Should fit Protocol-Queries too.
|
||||
// public function __construct(\Icinga\Backend\Query $query)
|
||||
public function __construct($query)
|
||||
{
|
||||
$this->query = $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of items for a page.
|
||||
*
|
||||
* @param integer $offset Page offset
|
||||
* @param integer $itemCountPerPage Number of items per page
|
||||
* @return array
|
||||
*/
|
||||
public function getItems($offset, $itemCountPerPage)
|
||||
{
|
||||
return $this->query->limit($itemCountPerPage, $offset)->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of items in the query result.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
if ($this->count === null) {
|
||||
$this->count = $this->query->count();
|
||||
}
|
||||
return $this->count;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @see Zend_Paginator_ScrollingStyle_Interface
|
||||
*/
|
||||
class Icinga_Web_Paginator_ScrollingStyle_SlidingWithBorder implements Zend_Paginator_ScrollingStyle_Interface
|
||||
{
|
||||
/**
|
||||
* Returns an array of "local" pages given a page number and range.
|
||||
*
|
||||
* @param Zend_Paginator $paginator
|
||||
* @param integer $pageRange (Optional) Page range
|
||||
* @return array
|
||||
*/
|
||||
public function getPages(Zend_Paginator $paginator, $pageRange = null)
|
||||
{
|
||||
if ($pageRange === null) {
|
||||
$pageRange = $paginator->getPageRange();
|
||||
}
|
||||
|
||||
$pageNumber = $paginator->getCurrentPageNumber();
|
||||
$pageCount = count($paginator);
|
||||
$range = array();
|
||||
|
||||
|
||||
|
||||
if ($pageCount < 15) {
|
||||
for ($i = 1; $i < 15; $i++) {
|
||||
if ($i > $pageCount) break;
|
||||
$range[$i] = $i;
|
||||
}
|
||||
} else {
|
||||
foreach (array(1, 2) as $i) {
|
||||
$range[$i] = $i;
|
||||
}
|
||||
if ($pageNumber > 8) {
|
||||
$range[] = '...';
|
||||
$start = 5;
|
||||
if ($pageCount - $pageNumber < 8) {
|
||||
$start = 9 - ($pageCount - $pageNumber);
|
||||
}
|
||||
for ($i = $pageNumber - $start; $i < $pageNumber + (10 - $start); $i++) {
|
||||
if ($i > $pageCount) break;
|
||||
$range[$i] = $i;
|
||||
}
|
||||
} else {
|
||||
for ($i = 3; $i <= 10; $i++) {
|
||||
$range[$i] = $i;
|
||||
}
|
||||
}
|
||||
if ($pageNumber < ($pageCount - 7)) {
|
||||
$range[] = '...';
|
||||
foreach (array($pageCount - 1, $pageCount) as $i) {
|
||||
$range[$i] = $i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (empty($range)) $range[] = 1;
|
||||
return $range;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Session handling
|
||||
*/
|
||||
namespace Icinga\Web;
|
||||
|
||||
use Icinga\Authentication\Auth\User;
|
||||
use Zend_Session;
|
||||
use Zend_Session_Namespace;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
||||
/**
|
||||
* Session handling happens here
|
||||
*
|
||||
* This is mainly a facade for Zend_Session_Namespace but provides some farther
|
||||
* functionality for authentication
|
||||
*
|
||||
* @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 Session
|
||||
{
|
||||
/**
|
||||
* Session is a Singleton stored in $instance
|
||||
*
|
||||
* @var Session
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
protected $defaultOptions = array(
|
||||
'use_trans_sid' => false,
|
||||
'use_cookies' => true,
|
||||
'use_only_cooies' => true,
|
||||
'cookie_httponly' => true,
|
||||
'use_only_cookies' => true,
|
||||
'hash_function' => true,
|
||||
'hash_bits_per_character' => 5,
|
||||
);
|
||||
|
||||
/**
|
||||
* The ZF session namespace
|
||||
*
|
||||
* @var \Zend_Session_Namespace
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
protected $started = false;
|
||||
protected $closed = true;
|
||||
|
||||
/**
|
||||
* Constructor is protected to enforce singleton usage
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
Zend_Session::start();
|
||||
$this->session = new Zend_Session_Namespace('Icinga');
|
||||
}
|
||||
/*
|
||||
// Not yet
|
||||
public function start()
|
||||
{
|
||||
if ($this->started) {
|
||||
return $this;
|
||||
}
|
||||
if ($this->closed) {
|
||||
ini_set('session.cache_limiter', null);
|
||||
ini_set('session.use_only_cookies', false);
|
||||
ini_set('session.use_cookies', false);
|
||||
ini_set('session.use_trans_sid', false);
|
||||
}
|
||||
$this->applyOptions();
|
||||
session_start();
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function applyOptions()
|
||||
{
|
||||
foreach ($this->defaultOptions as $key => $val) {
|
||||
ini_set('session.' . $key => $val);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
*/
|
||||
public static function setOptions($options)
|
||||
{
|
||||
return Zend_Session::setOptions($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Once authenticated we store the given user(name) to our session
|
||||
*
|
||||
* @param Auth\User $user The user object
|
||||
* // TODO: Useless
|
||||
* @return self
|
||||
*/
|
||||
public function setAuthenticatedUser(User $user)
|
||||
{
|
||||
$this->session->userInfo = (string) $user;
|
||||
$this->session->realname = (string) $user; // TODO: getRealName()
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user object for the authenticated user
|
||||
*
|
||||
* // TODO: This has not been done yet. Useless?
|
||||
*
|
||||
* @return User $user The user object
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
throw new ProgrammingError('Not implemented yet');
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this session has an authenticated user
|
||||
*
|
||||
* // TODO: remove
|
||||
* @return bool
|
||||
*/
|
||||
public function isAuthenticated()
|
||||
{
|
||||
return isset($this->session->username);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget everything we know about the authenticated user
|
||||
*
|
||||
* // TODO: Remove
|
||||
* @return self
|
||||
*/
|
||||
public function discardAuthentication()
|
||||
{
|
||||
unset($this->session->username);
|
||||
unset($this->session->realname);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Singleton instance
|
||||
*
|
||||
* TODO: This doesn't work so yet, it gives you a Zend_Session_Namespace
|
||||
* instance. Facade has to be completed before we can fix this.
|
||||
*
|
||||
* @return Icinga\Web\Session -> not yet
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new Session();
|
||||
}
|
||||
return self::$instance->session;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Web widget class
|
||||
*/
|
||||
namespace Icinga\Web;
|
||||
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
||||
/**
|
||||
* Web widgets make things easier for you!
|
||||
*
|
||||
* This class provides nothing but a static factory method for widget creation.
|
||||
* Usually it will not be used directly as there are widget()-helpers available
|
||||
* in your action controllers and view scripts.
|
||||
*
|
||||
* Usage example:
|
||||
* <code>
|
||||
* $tabs = Widget::create('tabs');
|
||||
* </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 Widget
|
||||
{
|
||||
|
||||
/**
|
||||
* Create a new widget
|
||||
*
|
||||
* @param string $name Widget name
|
||||
* @param array $options Widget constructor options
|
||||
*
|
||||
* @return Icinga\Web\Widget\AbstractWidget
|
||||
*/
|
||||
public static function create($name, $options = array())
|
||||
{
|
||||
$class = 'Icinga\\Web\\Widget\\' . ucfirst($name);
|
||||
|
||||
if (! class_exists($class)) {
|
||||
throw new ProgrammingError(
|
||||
sprintf(
|
||||
'There is no such widget: %s',
|
||||
$name
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$widget = new $class($options);
|
||||
return $widget;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Web Widget abstract class
|
||||
*/
|
||||
namespace Icinga\Web\Widget;
|
||||
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Zend_Controller_Action_HelperBroker as ZfActionHelper;
|
||||
|
||||
/**
|
||||
* Web widgets MUST extend this class
|
||||
*
|
||||
* AbstractWidget implements getters and setters for widget options stored in
|
||||
* the protected options array. If you want to allow options for your own
|
||||
* widget, you have to set a default value (may be null) for each single option
|
||||
* in this array.
|
||||
*
|
||||
* Please have a look at the available widgets in this folder to get a better
|
||||
* idea on what they should look like.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
abstract class AbstractWidget
|
||||
{
|
||||
/**
|
||||
* If you are going to access the current view with the view() function,
|
||||
* it's instance is stored here for performance reasons.
|
||||
*
|
||||
* @var Zend_View_Abstract
|
||||
*/
|
||||
protected static $view;
|
||||
|
||||
/**
|
||||
* Fill $properties with default values for all your valid widget properties
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $properties = array();
|
||||
|
||||
/**
|
||||
* You MUST extend this function. This is where all your HTML voodoo happens
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function renderAsHtml();
|
||||
|
||||
/**
|
||||
* You are not allowed to override the constructor, but you can put
|
||||
* initialization stuff in your init() function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* We are not allowing you to override the constructor unless someone
|
||||
* presents a very good reason for doing so
|
||||
*
|
||||
* @param array $properties An optional properties array
|
||||
*/
|
||||
final public function __construct($properties = array())
|
||||
{
|
||||
foreach ($properties as $key => $val) {
|
||||
$this->$key = $val;
|
||||
}
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for widget properties
|
||||
*
|
||||
* @param string $key The option you're interested in
|
||||
*
|
||||
* @throws ProgrammingError for unknown property name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
if (array_key_exists($key, $this->properties)) {
|
||||
return $this->properties[$key];
|
||||
}
|
||||
|
||||
throw new ProgrammingError(
|
||||
sprintf(
|
||||
'Trying to get invalid "%s" property for %s',
|
||||
$key,
|
||||
get_class($this)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for widget properties
|
||||
*
|
||||
* @param string $key The option you want to set
|
||||
* @param string $val The new value going to be assigned to this option
|
||||
*
|
||||
* @throws ProgrammingError for unknown property name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __set($key, $val)
|
||||
{
|
||||
if (array_key_exists($key, $this->properties)) {
|
||||
$this->properties[$key] = $val;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new ProgrammingError(
|
||||
sprintf(
|
||||
'Trying to set invalid "%s" property in %s. Allowed are: %s',
|
||||
$key,
|
||||
get_class($this),
|
||||
empty($this->properties)
|
||||
? 'none'
|
||||
: implode(', ', array_keys($this->properties))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the current view
|
||||
*
|
||||
* Will instantiate a new one if none exists
|
||||
* // TODO: App->getView
|
||||
*
|
||||
* @return Zend_View_Abstract
|
||||
*/
|
||||
protected function view()
|
||||
{
|
||||
if (self::$view === null) {
|
||||
|
||||
$renderer = ZfActionHelper::getStaticHelper(
|
||||
'viewRenderer'
|
||||
);
|
||||
|
||||
if (null === $renderer->view) {
|
||||
$renderer->initView();
|
||||
}
|
||||
|
||||
self::$view = $renderer->view;
|
||||
}
|
||||
|
||||
return self::$view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast this widget to a string. Will call your renderAsHtml() function
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->renderAsHtml();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Form
|
||||
*/
|
||||
namespace Icinga\Web\Widget;
|
||||
|
||||
/**
|
||||
* A form loader...
|
||||
*
|
||||
* @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 Form extends AbstractWidget
|
||||
{
|
||||
protected $form;
|
||||
protected $properties = array(
|
||||
'name' => null
|
||||
);
|
||||
|
||||
public function __call($func, $args)
|
||||
{
|
||||
return call_user_func_array(array($this->form, $func), $args);
|
||||
}
|
||||
|
||||
protected function init()
|
||||
{
|
||||
// Load form by name given in props?
|
||||
$class = 'Icinga\\Web\\Form\\' . ucfirst($this->name) . 'Form';
|
||||
$file = ICINGA_APPDIR
|
||||
. '/forms/authentication/'
|
||||
. ucfirst($this->name)
|
||||
. 'Form.php';
|
||||
require_once($file);
|
||||
$this->form = new $class;
|
||||
}
|
||||
|
||||
public function renderAsHtml()
|
||||
{
|
||||
return (string) $this->form;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Single tab
|
||||
*/
|
||||
namespace Icinga\Web\Widget;
|
||||
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
||||
/**
|
||||
* A single tab, usually used through the tabs widget
|
||||
*
|
||||
* Will generate an <li> list item, with an optional link and icon
|
||||
*
|
||||
* @property string $name Tab identifier
|
||||
* @property string $title Tab title
|
||||
* @property string $icon Icon URL, preferrably relative to the Icinga
|
||||
* base URL
|
||||
* @property string $url Action URL, preferrably relative to the Icinga
|
||||
* base URL
|
||||
* @property string $urlParams Action URL Parameters
|
||||
*
|
||||
* @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 Tab extends AbstractWidget
|
||||
{
|
||||
/**
|
||||
* Whether this tab is currently active
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $active = false;
|
||||
|
||||
/**
|
||||
* Default values for widget properties
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $properties = array(
|
||||
'name' => null,
|
||||
'title' => '',
|
||||
'url' => null,
|
||||
'urlParams' => array(),
|
||||
'icon' => null,
|
||||
);
|
||||
|
||||
/**
|
||||
* Health check at initialization time
|
||||
*
|
||||
* @throws Icinga\Exception\ProgrammingError if tab name is missing
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
if ($this->name === null) {
|
||||
throw new ProgrammingError(
|
||||
'Cannot create a nameless tab'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this tab active (default) or inactive
|
||||
*
|
||||
* This is usually done through the tabs container widget, therefore it
|
||||
* is not a good idea to directly call this function
|
||||
*
|
||||
* @param bool $active Whether the tab should be active
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setActive($active = true)
|
||||
{
|
||||
$this->active = (bool) $active;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this tab is currently active
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isActive()
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is where the list item HTML is created
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function renderAsHtml()
|
||||
{
|
||||
$view = $this->view();
|
||||
$class = $this->isActive() ? ' class="active"' : '';
|
||||
$caption = $this->title;
|
||||
if ($this->icon !== null) {
|
||||
$caption = $view->img($this->icon, array(
|
||||
'width' => 16,
|
||||
'height' => 16
|
||||
)) . ' ' . $caption;
|
||||
}
|
||||
if ($this->url !== null) {
|
||||
$tab = $view->qlink(
|
||||
$caption,
|
||||
$this->url,
|
||||
$this->urlParams,
|
||||
array('quote' => false)
|
||||
);
|
||||
} else {
|
||||
$tab = $caption;
|
||||
}
|
||||
return "<li $class>$tab</li>\n";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Navigation tabs
|
||||
*/
|
||||
namespace Icinga\Web\Widget;
|
||||
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
||||
/**
|
||||
* Navigation tab widget
|
||||
*
|
||||
* Useful if you want to create navigation tabs
|
||||
*
|
||||
* @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 Tabs extends AbstractWidget
|
||||
{
|
||||
/**
|
||||
* This is where single tabs added to this container will be stored
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $tabs = array();
|
||||
|
||||
/**
|
||||
* The name of the currently activated tab
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $active;
|
||||
|
||||
/**
|
||||
* Class name(s) going to be assigned to the <ul> element
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tab_class = 'nav-tabs';
|
||||
|
||||
/**
|
||||
* Activate the tab with the given name
|
||||
*
|
||||
* If another tab is currently active it will be deactivated
|
||||
*
|
||||
* @param string $name Name of the tab going to be activated
|
||||
*
|
||||
* @throws ProgrammingError if given tab name doesn't exist
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function activate($name)
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
if ($this->active !== null) {
|
||||
$this->tabs[$this->active]->setActive(false);
|
||||
}
|
||||
$this->get($name)->setActive();
|
||||
$this->active = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
throw new ProgrammingError(
|
||||
sprintf(
|
||||
"Cannot activate a tab that doesn't exist: %s. Available: %s",
|
||||
$name,
|
||||
empty($this->tabs)
|
||||
? 'none'
|
||||
: implode(', ', array_keys($this->tabs))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function getActiveName()
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the CSS class name(s) for the <ul> element
|
||||
*
|
||||
* @param string $name CSS class name(s)
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setClass($name)
|
||||
{
|
||||
$this->tab_class = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given tab name exists
|
||||
*
|
||||
* @param string $name Tab name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return array_key_exists($name, $this->tabs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given tab name exists
|
||||
*
|
||||
* @param string $name The tab you're interested in
|
||||
*
|
||||
* @throws ProgrammingError if given tab name doesn't exist
|
||||
*
|
||||
* @return Tab
|
||||
*/
|
||||
public function get($name)
|
||||
{
|
||||
if (! $this->has($name)) {
|
||||
throw new ProgrammingError(
|
||||
sprintf(
|
||||
'There is no such tab: %s',
|
||||
$name
|
||||
)
|
||||
);
|
||||
}
|
||||
return $this->tabs[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new tab
|
||||
*
|
||||
* A unique tab name is required, the Tab itself can either be an array
|
||||
* with tab properties or an instance of an existing Tab
|
||||
*
|
||||
* @param string $name The new tab name
|
||||
* @param array|Tab The tab itself of it's properties
|
||||
*
|
||||
* @throws ProgrammingError if tab name already exists
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function add($name, $tab)
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
throw new ProgrammingError(
|
||||
sprintf(
|
||||
'Cannot add a tab named "%s" twice"',
|
||||
$name
|
||||
)
|
||||
);
|
||||
}
|
||||
return $this->set($name, $tab);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a tab
|
||||
*
|
||||
* A unique tab name is required, will be replaced in case it already
|
||||
* exists. The tab can either be an array with tab properties or an instance
|
||||
* of an existing Tab
|
||||
*
|
||||
* @param string $name The new tab name
|
||||
* @param array|Tab The tab itself of it's properties
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function set($name, $tab)
|
||||
{
|
||||
if ($tab instanceof Tab) {
|
||||
$this->tabs[$name] = $tab;
|
||||
} else {
|
||||
$this->tabs[$name] = new Tab($tab + array('name' => $name));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is where the tabs are going to be rendered
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function renderAsHtml()
|
||||
{
|
||||
$view = $this->view();
|
||||
|
||||
if (empty($this->tabs)) {
|
||||
return '';
|
||||
}
|
||||
$html = '<ul class="nav ' . $this->tab_class . '">' . "\n";
|
||||
|
||||
foreach ($this->tabs as $tab) {
|
||||
$html .= $tab;
|
||||
}
|
||||
$html .= "</ul>\n";
|
||||
return $html;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue