icingaweb2/library/Icinga/Protocol/Statusdat/Reader.php

350 lines
10 KiB
PHP
Raw Normal View History

<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
2013-10-23 15:10:33 +02:00
* This file is part of Icinga Web 2.
*
2013-10-23 15:10:33 +02:00
* Icinga Web 2 - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
2013-10-23 15:10:33 +02:00
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Protocol\Statusdat;
use Icinga\Data\DatasourceInterface;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception;
use Icinga\Benchmark;
use Icinga\Protocol\Statusdat\View\MonitoringObjectList;
/**
* Class Reader
* @package Icinga\Protocol\Statusdat
*/
class Reader implements IReader, DatasourceInterface
{
/**
* The default lifetime of the cache in milliseconds
*/
const DEFAULT_CACHE_LIFETIME = 30;
/**
* The folder for the statusdat cache
*
*/
const STATUSDAT_DEFAULT_CACHE_PATH = "/cache";
/**
* The last state from the cache
*
* @var array
*/
private $lastState;
/**
* True when this reader has already acquired the current runtime state (i.e. Status.dat)
*
* @var bool
*/
private $hasRuntimeState = false;
/**
* The representation of the object.cache file
*
* @var array
*/
private $objectCache ;
/**
* The representation of the status.dat file
* @var array
*/
private $statusCache;
/**
* True when the icinga state differs from the cache
*
* @var bool
*/
private $newState = false;
/**
* The Parser object to use for parsing
*
* @var Parser
*/
private $parser;
/**
* Whether to disable the cache
*
* @var bool
*/
private $noCache;
/**
* Create a new Reader from the given configuraion
*
* @param Zend_Config $config The configuration to read the status.dat information from
* @param Parser $parser The parser to use (for testing)
* @param bool $noCache Whether to disable the cache
*/
public function __construct($config = \Zend_Config, $parser = null, $noCache = false)
{
$this->noCache = $noCache;
if (isset($config->no_cache)) {
$this->noCache = $config->no_cache;
}
$this->config = $config;
$this->parser = $parser;
2013-10-19 20:09:17 +02:00
if (!$this->noCache) {
$this->cache = $this->initializeCaches($config);
if ($this->fromCache()) {
$this->createHostServiceConnections();
return;
}
}
2013-10-19 20:09:17 +02:00
if (!$this->lastState) {
$this->parseObjectsCacheFile();
}
if (!$this->hasRuntimeState) {
}
$this->parseStatusDatFile();
if (!$noCache && $this->newState) {
2013-10-19 20:09:17 +02:00
$this->statusCache->save($this->parser->getRuntimeState(), 'object' . md5($this->config->object_file));
}
$this->createHostServiceConnections();
}
/**
* Initialize the internal caches if enabled
*
* @throws ConfigurationError
*/
private function initializeCaches()
{
$defaultCachePath = "/tmp" . self::STATUSDAT_DEFAULT_CACHE_PATH;
$cachePath = $this->config->get('cache_path', $defaultCachePath);
$maxCacheLifetime = intval($this->config->get('cache_path', self::DEFAULT_CACHE_LIFETIME));
if (!is_writeable($cachePath)) {
throw new ConfigurationError(
"Cache path $cachePath is not writable, check your configuration"
);
}
$backendOptions = array(
'cache_dir' => $cachePath
);
2013-10-19 20:09:17 +02:00
// the object cache might exist for months and is still valid
$this->objectCache = $this->initCache($this->config->object_file, $backendOptions, null);
$this->statusCache = $this->initCache($this->config->status_file, $backendOptions, $maxCacheLifetime);
}
/**
* Init the Cache backend in Zend
*
* @param String $file The file to use as the cache master file
* @param Zend_Config $backend The backend configuration to use
* @param integer $lifetime The lifetime of the cache
*
* @return \Zend_Cache_Core|\Zend_Cache_Frontend
*/
private function initCache($file, $backend, $lifetime)
{
$frontendOptions = array(
'lifetime' => $lifetime,
'automatic_serialization' => true,
'master_files' => array($file)
);
return \Zend_Cache::factory('Core', 'File', $frontendOptions, $backend);
}
/**
* Read the current cache state
*
* @return bool True if the state is the same as the icinga state
*/
private function fromCache()
{
if (!$this->readObjectsCache()) {
$this->newState = true;
return false;
}
if (!$this->readStatusCache()) {
$this->newState = true;
return false;
}
return true;
}
/**
* Read the object.cache file from the Zend_Cache backend
*
* @return bool True if the file could be loaded from cache
*/
private function readObjectsCache()
{
2013-10-19 20:09:17 +02:00
$this->lastState = $this->objectCache->load('object' . md5($this->config->object_file));
if ($this->lastState == false) {
return false;
}
return true;
}
/**
* Read the status.dat file from the Zend_Cache backend
*
* @return bool True if the file could be loaded from cache
*/
private function readStatusCache()
{
if (!isset($this->stateCache)) {
return true;
}
$statusInfo = $this->stateCache->load('state' . md5($this->config->status_file));
if ($statusInfo == false) {
return false;
}
$this->hasRuntimeState = true;
return true;
}
/**
* Take the status.dat and objects.cache and connect all services to hosts
*
*/
private function createHostServiceConnections()
{
if (!isset($this->lastState["service"])) {
return;
}
2013-10-19 20:09:17 +02:00
foreach ($this->lastState["host"] as &$host) {
$host->host = $host;
}
foreach ($this->lastState["service"] as &$service) {
2013-10-19 20:09:17 +02:00
$service->service = &$service; // allow easier querying
$host = &$this->lastState["host"][$service->host_name];
if (!isset($host->services)) {
$host->services = array();
}
$host->services[$service->service_description] = & $service;
$service->host = & $host;
}
}
/**
* Parse the object.cache file and update the current state
*
* @throws ConfigurationError If the object.cache couldn't be read
*/
private function parseObjectsCacheFile()
{
2013-10-19 20:09:17 +02:00
if (!is_readable($this->config->object_file)) {
throw new ConfigurationError(
2013-10-19 20:09:17 +02:00
'Can\'t read object-file "' . $this->config->object_file . '", check your configuration'
);
}
if (!$this->parser) {
2013-10-19 20:09:17 +02:00
$this->parser = new Parser(fopen($this->config->object_file, "r"));
}
$this->parser->parseObjectsFile();
2013-07-23 12:18:27 +02:00
$this->lastState = $this->parser->getRuntimeState();
}
/**
* Parse the status.dat file and update the current state
*
* @throws ConfigurationError If the status.dat couldn't be read
*/
private function parseStatusDatFile()
{
if (!is_readable($this->config->status_file)) {
throw new ConfigurationError(
"Can't read status-file {$this->config->status_file}, check your configuration"
);
}
if (!$this->parser) {
$this->parser = new Parser(fopen($this->config->status_file, "r"), $this->lastState);
}
$this->parser->parseRuntimeState(fopen($this->config->status_file, "r"));
2013-07-23 12:18:27 +02:00
$this->lastState = $this->parser->getRuntimeState();
if (!$this->noCache) {
2013-10-19 20:09:17 +02:00
$this->statusCache->save(array("true" => true), "state" . md5($this->config->object_file));
}
}
/**
* Create a new Query
*
* @return Query The query to operate on
*/
public function select()
{
return new Query($this);
}
/**
* Return the internal state of the status.dat
*
* @return mixed The internal status.dat representation
*/
public function getState()
{
return $this->lastState;
}
/**
* Return the object with the given name and type
*
* @param String $type The type of the object to return (service, host, servicegroup...)
* @param String $name The name of the object
*
* @return ObjectContainer An object container wrapping the result or null if the object doesn't exist
*/
public function getObjectByName($type, $name)
{
if (isset($this->lastState[$type]) && isset($this->lastState[$type][$name])) {
return new ObjectContainer($this->lastState[$type][$name], $this);
}
return null;
}
/**
* Get an array containing all names of monitoring objects with the given type
*
* @param String $type The type of object to get the names for
* @return array An array of names or null if the type does not exist
*/
public function getObjectNames($type)
{
return isset($this->lastState[$type]) ? array_keys($this->lastState[$type]) : null;
}
}