From f3bbf09baeb8cc363d5bf7365e2a5ad01560d4e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannis=20Mo=C3=9Fhammer?= Date: Sun, 20 Oct 2013 15:15:30 +0200 Subject: [PATCH] Status.dat query fixes and documentation after rebase refs #3801 --- library/Icinga/Protocol/Statusdat/IReader.php | 5 - .../Protocol/Statusdat/ObjectContainer.php | 3 +- library/Icinga/Protocol/Statusdat/Parser.php | 102 +++--- .../Protocol/Statusdat/PrintableObject.php | 44 +++ library/Icinga/Protocol/Statusdat/Query.php | 293 +++++++++--------- .../Protocol/Statusdat/Query/Expression.php | 9 +- library/Icinga/Protocol/Statusdat/Reader.php | 127 ++++---- .../Statusdat/RuntimeStateContainer.php | 28 +- .../Statusdat/TreeToStatusdatQueryParser.php | 14 +- .../controllers/CommandController.php | 16 +- .../controllers/MultiController.php | 4 +- .../views/scripts/list/downtimes.phtml | 8 +- .../Backend/Statusdat/Query/DowntimeQuery.php | 41 ++- .../Statusdat/Query/HostgroupQuery.php | 4 +- .../Statusdat/Query/ServicegroupQuery.php | 52 ++++ .../Backend/Statusdat/Query/StatusQuery.php | 38 ++- .../Statusdat/Query/StatusdatQuery.php | 26 +- public/js/icinga/components/mainDetailGrid.js | 2 - .../Icinga/Protocol/Statusdat/QueryTest.php | 7 +- .../Icinga/Protocol/Statusdat/ReaderMock.php | 6 + .../Statusdat/StatusdatTestLoader.php | 1 + 21 files changed, 528 insertions(+), 302 deletions(-) create mode 100644 library/Icinga/Protocol/Statusdat/PrintableObject.php create mode 100755 modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupQuery.php diff --git a/library/Icinga/Protocol/Statusdat/IReader.php b/library/Icinga/Protocol/Statusdat/IReader.php index 4dae2e929..607923c04 100755 --- a/library/Icinga/Protocol/Statusdat/IReader.php +++ b/library/Icinga/Protocol/Statusdat/IReader.php @@ -35,11 +35,6 @@ interface IReader */ public function getState(); - /** - * @return mixed - */ - public function getObjects(); - /** * @param $type * @param $name diff --git a/library/Icinga/Protocol/Statusdat/ObjectContainer.php b/library/Icinga/Protocol/Statusdat/ObjectContainer.php index d4669a394..96f91572c 100644 --- a/library/Icinga/Protocol/Statusdat/ObjectContainer.php +++ b/library/Icinga/Protocol/Statusdat/ObjectContainer.php @@ -48,7 +48,7 @@ class ObjectContainer extends \stdClass * @param \stdClass $obj * @param IReader $reader */ - public function __construct(\stdClass &$obj, IReader &$reader) + public function __construct(&$obj, IReader &$reader) { $this->ref = & $obj; $this->reader = & $reader; @@ -68,6 +68,7 @@ class ObjectContainer extends \stdClass $result = $result->$elem; } } + return $result; } } diff --git a/library/Icinga/Protocol/Statusdat/Parser.php b/library/Icinga/Protocol/Statusdat/Parser.php index 1c4e06fa6..541384c62 100755 --- a/library/Icinga/Protocol/Statusdat/Parser.php +++ b/library/Icinga/Protocol/Statusdat/Parser.php @@ -34,45 +34,59 @@ use Icinga\Exception\ProgrammingError; use Icinga\Protocol\Statusdat\Exception\ParsingException as ParsingException; /** - * Class Parser - * @package Icinga\Protocol\Statusdat + * Status.dat and object.cache parser implementation */ class Parser { /** + * An array of objects that couldn't be resolved yet due to missing dependencies + * * @var array */ private $deferred = array(); /** - * @var null|resource + * The resource pointing to the currently read file + * + * @var resource */ - private $filehandle = null; + private $filehandle; /** - * @var null + * String representation of the currently parsed object type + * + * @var string */ - private $currentObjectType = null; + private $currentObjectType; /** - * @var null + * The current state type (host, service) + * + * @var string */ - private $currentStateType = null; + private $currentStateType; /** - * @var null + * The internal representation of the icinga statue + * + * @var array */ - private $icingaState = null; + private $icingaState; /** + * The current line being read + * * @var int */ private $lineCtr = 0; /** - * @param null $filehandle - * @param null $baseState - * @throws \Icinga\Exception\ConfigurationError + * Create a new parser using the given file + * + * @param resource $filehandle The file handle to usefor parsing + * @param array $baseState The state using for the base + * + * @throws ConfigurationError When the file can't be used */ public function __construct($filehandle = null, $baseState = null) { @@ -85,7 +99,7 @@ class Parser } /** - * + * Parse the given file handle as an objects file and read object information */ public function parseObjectsFile() { @@ -110,8 +124,7 @@ class Parser } /** - * @param null $filehandle - * @throws ProgrammingError + * Parse the given file handle as an status.dat file and read runtime information */ public function parseRuntimeState($filehandle = null) { @@ -126,26 +139,25 @@ class Parser } $this->overwrites = array(); while (!feof($filehandle)) { - $line = trim(fgets($filehandle)); - $this->lineCtr++; if ($line === "" || $line[0] === "#") { continue; } - $this->currentStateType = trim(substr($line, 0, -1)); $this->readCurrentState(); } } /** - * @throws Exception\ParsingException + * Read the next object from the object.cache file handle + * + * @throws ParsingException */ private function readCurrentObject() { $filehandle = $this->filehandle; - $monitoringObject = new \stdClass(); + $monitoringObject = new PrintableObject(); while (!feof($filehandle)) { $line = explode("\t", trim(fgets($filehandle)), 2); $this->lineCtr++; @@ -167,6 +179,8 @@ class Parser } /** + * Read the next state from the status.dat file handler + * * @throws Exception\ParsingException */ private function readCurrentState() @@ -201,9 +215,9 @@ class Parser $type = substr($this->currentStateType, strlen($objectType)); if ($type == "status") { + // directly set the status to the status field of the given object $base[$name]->status = & $statusdatObject; } else { - if (!isset($base[$name]->$type) || !in_array($base[$name]->$type, $this->overwrites)) { $base[$name]->$type = array(); $this->overwrites[] = & $base[$name]->$type; @@ -225,7 +239,9 @@ class Parser } /** - * @return null|string + * Get the corresponding object type name for the given state + * + * @return string */ private function getObjectTypeForState() { @@ -249,8 +265,10 @@ class Parser } /** - * @param bool $returnString - * @return string + * Skip the current object definition + * + * @param bool $returnString If true, the object string will be returned + * @return string The skipped object if $returnString is true */ protected function skipObject($returnString = false) { @@ -268,7 +286,9 @@ class Parser } /** - * @param $object + * Register the given object in the icinga state + * + * @param object $object The monitoring object to register */ protected function registerObject(&$object) { @@ -281,7 +301,11 @@ class Parser } /** - * @param $object + * Register the given object as a property in related objects + * + * This registers for example hosts underneath their hostgroup and vice cersa + * + * @param object $object The object to register as a property */ protected function registerObjectAsProperty(&$object) { @@ -344,8 +368,10 @@ class Parser } /** - * @param $object - * @param $objType + * Defer registration of the given object + * + * @param object $object The object to defer + * @param String $objType The name of the object type */ protected function deferRegistration($object, $objType) { @@ -353,7 +379,7 @@ class Parser } /** - * + * Process deferred objects */ protected function processDeferred() { @@ -364,8 +390,10 @@ class Parser } /** - * @param $object - * @return array + * Return the resolved members directive of an object + * + * @param object $object The object to get the members from + * @return array An array of member names */ protected function getMembers(&$object) { @@ -388,8 +416,10 @@ class Parser } /** - * @param $object - * @return bool|string + * Return the unique name of the given object + * + * @param object $object The object to retrieve the name from + * @return string The name of the object or null if no name can be retrieved */ protected function getObjectIdentifier(&$object) { @@ -409,11 +439,13 @@ class Parser } elseif (isset($object->host_name)) { return $object->host_name; } - return false; + return null; } /** + * Return the internal state of the parser + * * @return null */ public function getRuntimeState() diff --git a/library/Icinga/Protocol/Statusdat/PrintableObject.php b/library/Icinga/Protocol/Statusdat/PrintableObject.php new file mode 100644 index 000000000..456a79010 --- /dev/null +++ b/library/Icinga/Protocol/Statusdat/PrintableObject.php @@ -0,0 +1,44 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Protocol\Statusdat; + +class PrintableObject +{ + public function __toString() + { + if (isset($this->contact_name)) { + return $this->contact_name; + } else if (isset($this->service_description)) { + return $this->service_description; + } else if (isset($this->host_name)) { + return $this->host_name; + } + return ''; + } +} \ No newline at end of file diff --git a/library/Icinga/Protocol/Statusdat/Query.php b/library/Icinga/Protocol/Statusdat/Query.php index 975e1a303..4f844a896 100755 --- a/library/Icinga/Protocol/Statusdat/Query.php +++ b/library/Icinga/Protocol/Statusdat/Query.php @@ -36,87 +36,77 @@ use Icinga\Protocol\Statusdat\View\MonitoringObjectList; use Icinga\Protocol\Statusdat\Query\IQueryPart; /** - * Class Query - * @package Icinga\Protocol\Statusdat + * Base implementation for Statusdat queries. + * */ class Query extends BaseQuery { /** + * An array denoting valid targets by mapping the query target to + * the 'define' directives found in the status.dat/objects.cache files + * * @var array */ public static $VALID_TARGETS = array( - "hosts" => array("host"), - "services" => array("service"), - "downtimes" => array("downtime"), - "groups" => array("hostgroup", "servicegroup"), - "hostgroups" => array("hostgroup"), - "servicegroups" => array("servicegroup"), - "comments" => array("comment"), - "contacts" => array("contact"), - "contactgroups" => array("contactgroup") + 'hosts' => array('host'), + 'services' => array('service'), + 'downtimes' => array('downtime'), + 'groups' => array('hostgroup', 'servicegroup'), + 'hostgroups' => array('hostgroup'), + 'servicegroups' => array('servicegroup'), + 'comments' => array('comment'), + 'contacts' => array('contact'), + 'contactgroups' => array('contactgroup') ); + /** + * The current StatusDat query that will be applied upon calling fetchAll + * + * @var IQueryPart + */ private $queryFilter = null; /** + * The current query source being used + * * @var string */ - private $source = ""; - - /** - * @var null - */ - private $limit = null; - - /** - * @var int - */ - private $offset = 0; + private $source = ''; /** + * An array containing all columns used for sorting + * * @var array */ protected $orderColumns = array(); /** + * An array containig all columns used for (simple) grouping + * * @var array */ private $groupColumns = array(); /** - * @var null + * An optional function callback to use for more specific grouping + * + * @var array */ private $groupByFn = null; /** - * @var array - */ - private $filter = null; - - /** - * @var array - */ - private $attributes = array(); - - /** - * + * The scope index for the callback function */ const FN_SCOPE = 0; /** - * + * The name index for the callback function */ const FN_NAME = 1; /** - * @return bool - */ - public function hasOrder() - { - return !empty($this->orderColumns); - } - - /** + * Return true if columns are set for this query + * * @return bool */ public function hasColumns() @@ -125,59 +115,31 @@ class Query extends BaseQuery return !empty($columns); } + /** + * Set the status.dat specific IQueryPart filter to use + * + * @param IQueryPart $filter + */ public function setQueryFilter($filter) { $this->queryFilter = $filter; } /** - * @return bool - */ - public function hasLimit() - { - return $this->limit !== false; - } - - /** - * @return bool - */ - public function hasOffset() - { - return $this->offset !== false; - } - - /** - * @return null - */ - public function getLimit() - { - return $this->limit; - } - - /** - * @return int|null - */ - public function getOffset() - { - return $this->offset; - } - - /** - * @param $columns - * @param null $dir - * @return $this + * Order the query result by the given columns + * + * @param String|array $columns An array of columns to order by + * @param String $dir The direction (asc or desc) in string form + * + * @return $this Fluent interface */ public function order($columns, $dir = null, $isFunction = false) { - if ($dir && strtolower($dir) == "desc") { + if ($dir && strtolower($dir) == 'desc') { $dir = self::SORT_DESC; } else { $dir = self::SORT_ASC; } - if ($isFunction) { - $this->orderColumns[] = array($columns, $dir); - return $this; - } if (!is_array($columns)) { $columns = array($columns); } @@ -200,34 +162,34 @@ class Query extends BaseQuery return $this; } + /** + * Order the query result using the callback to retrieve values for items + * + * @param array $columns A scope, function array to use for retrieving the values when ordering + * @param String $dir The direction (asc or desc) in string form + * + * @return $this Fluent interface + */ public function orderByFn(array $callBack, $dir = null) { - $this->order($callBack, $dir, true); - } - - /** - * @param null $count - * @param int $offset - * @return $this - * @throws Exception - */ - public function limit($count = null, $offset = 0) - { - if ((is_null($count) || is_integer($count)) && (is_null($offset) || is_integer($offset))) { - $this->offset = $offset; - $this->limit = $count; + if ($dir && strtolower($dir) == 'desc') { + $dir = self::SORT_DESC; } else { - throw new Exception("Got invalid limit $count, $offset"); + $dir = self::SORT_ASC; } - return $this; + $this->orderColumns[] = array($callBack, $dir); } + /** - * @param $table - * @param null $columns - * @return $this - * @throws \Exception + * Set the query target + * + * @param String $table The table/target to select the query from + * @param array $columns An array of attributes to use (required for fetchPairs()) + * + * @return $this Fluent interface + * @throws \Exception If the target is unknonw */ public function from($table, array $attributes = null) { @@ -237,28 +199,29 @@ class Query extends BaseQuery if (isset(self::$VALID_TARGETS[$table])) { $this->source = $table; } else { - throw new \Exception("Unknown from target for status.dat :" . $table); + throw new \Exception('Unknown from target for status.dat :' . $table); } return $this; } /** + * Return an index of all objects matching the filter of this query * - * @throws Exception + * This index will be used for ordering, grouping and limiting */ - private function getFilteredIndices($classType = "\Icinga\Protocol\Statusdat\Query\Group") + private function getFilteredIndices($classType = '\Icinga\Protocol\Statusdat\Query\Group') { $baseGroup = $this->queryFilter; - - - $state = $this->ds->getObjects(); + $state = $this->ds->getState(); $result = array(); $source = self::$VALID_TARGETS[$this->source]; foreach ($source as $target) { + if (! isset($state[$target])) { continue; } + $indexes = array_keys($state[$target]); if ($baseGroup) { $baseGroup->setQuery($this); @@ -274,27 +237,38 @@ class Query extends BaseQuery } /** - * @param array $indices + * Order the given result set + * + * @param array $indices The result set of the query that should be ordered */ private function orderIndices(array &$indices) { if (!empty($this->orderColumns)) { foreach ($indices as $type => &$subindices) { $this->currentType = $type; - usort($subindices, array($this, "orderResult")); + usort($subindices, array($this, 'orderResult')); } } } + /** + * Start a query + * + * This is just a dummy function to allow a more convenient syntax + * + * @return self Fluent interface + */ public function select() { return $this; } /** - * @param $a - * @param $b - * @return int + * Order implementation called by usort + * + * @param String $a The left object index + * @param Strinv $b The right object index + * @return int 0, 1 or -1, see usort for detail */ private function orderResult($a, $b) { @@ -304,30 +278,37 @@ class Query extends BaseQuery foreach ($this->orderColumns as &$col) { if (is_array($col[0])) { - $result += $col[1] * strnatcasecmp($col[0][0]->$col[0][1]($o1), $col[0][0]->$col[0][1]($o2)); + // sort by function + $result += $col[1] * strnatcasecmp( + $col[0][0]->$col[0][1]($o1), + $col[0][0]->$col[0][1]($o2) + ); } else { - if (is_string($o1->{$col[0]}) && is_string($o2->{$col[0]}) ) { - $result += $col[1] * strnatcasecmp($o1->{$col[0]}, $o2->{$col[0]}); - } + $result += $col[1] * strnatcasecmp($o1->{$col[0]}, $o2->{$col[0]}); } } return $result; } /** - * @param array $indices + * Limit the given resultset + * + * @param array $indices The filtered, ordered indices */ private function limitIndices(array &$indices) { foreach ($indices as $type => $subindices) { - $indices[$type] = array_slice($subindices, $this->offset, $this->limit); + $indices[$type] = array_slice($subindices, $this->getOffset(), $this->getLimit()); } } /** - * @param $fn - * @param null $scope - * @return $this + * Register the given function for grouping the result + * + * @param String $fn The function to use for grouping + * @param Object $scope An optional scope to use instead of $this + * + * @return self Fluent interface */ public function groupByFunction($fn, $scope = null) { @@ -336,8 +317,11 @@ class Query extends BaseQuery } /** - * @param $columns - * @return $this + * Group by the given column + * + * @param array|string $columns The columns to use for grouping + * @return self Fluent interface + * @see Query::columnGroupFn() The implementation used for grouping */ public function groupByColumns($columns) { @@ -345,13 +329,15 @@ class Query extends BaseQuery $columns = array($columns); } $this->groupColumns = $columns; - $this->groupByFn = array($this, "columnGroupFn"); + $this->groupByFn = array($this, 'columnGroupFn'); return $this; } /** - * @param array $indices - * @return array + * The internal handler function used by the group function + * + * @param array $indices The indices to group + * @return array The grouped result set */ private function columnGroupFn(array &$indices) { @@ -360,7 +346,7 @@ class Query extends BaseQuery foreach ($indices as $type => $subindices) { foreach ($subindices as $objectIndex) { $r = $this->ds->getObjectByName($type, $objectIndex); - $hash = ""; + $hash = ''; $cols = array(); foreach ($this->groupColumns as $col) { $hash = md5($hash . $r->$col); @@ -368,8 +354,8 @@ class Query extends BaseQuery } if (!isset($result[$hash])) { $result[$hash] = (object)array( - "columns" => (object)$cols, - "count" => 0 + 'columns' => (object)$cols, + 'count' => 0 ); } $result[$hash]->count++; @@ -379,11 +365,12 @@ class Query extends BaseQuery } /** - * @return array + * Query Filter, Order, Group, Limit and return the result set + * + * @return array The resultset matching this query */ public function getResult() { - $indices = $this->getFilteredIndices(); $this->orderIndices($indices); if ($this->groupByFn) { @@ -396,7 +383,8 @@ class Query extends BaseQuery $this->limitIndices($indices); $result = array(); - $state = $this->ds->getObjects(); + $state = $this->ds->getState(); + foreach ($indices as $type => $subindices) { foreach ($subindices as $index) { $result[] = & $state[$type][$index]; @@ -413,14 +401,16 @@ class Query extends BaseQuery { $parser = new TreeToStatusdatQueryParser(); if ($this->getFilter()) { - $query = $parser->treeToQuery($this->getFilter()); + $query = $parser->treeToQuery($this->getFilter(), $this); $this->setQueryFilter($query); } } /** - * @return mixed + * Fetch the first result row + * + * @return MonitoringObjectList The monitoring object matching this query */ public function fetchRow() { @@ -429,7 +419,10 @@ class Query extends BaseQuery } /** - * @return mixed|void + * Fetch the result as an associative array using the first column as the key and the second as the value + * + * @return array An associative array with the result + * @throws \Exception If no attributes are defined */ public function fetchPairs() { @@ -452,15 +445,9 @@ class Query extends BaseQuery } /** - * @return mixed - */ - public function fetchOne() - { - return next($this->fetchAll()); - } - - /** - * @return MList|mixed|null + * Fetch all results + * + * @return MonitoringObjectList An MonitoringObjectList wrapping the given resultset */ public function fetchAll() { @@ -473,7 +460,19 @@ class Query extends BaseQuery } /** - * @return int|mixed + * Fetch one result + * + * @return mixed + */ + public function fetchOne() + { + return next($this->fetchAll()); + } + + /** + * Count the number of results + * + * @return int */ public function count() { diff --git a/library/Icinga/Protocol/Statusdat/Query/Expression.php b/library/Icinga/Protocol/Statusdat/Query/Expression.php index eb21d703d..1087eabca 100755 --- a/library/Icinga/Protocol/Statusdat/Query/Expression.php +++ b/library/Icinga/Protocol/Statusdat/Query/Expression.php @@ -161,6 +161,7 @@ class Expression implements IQueryPart $this->fields = explode(".", trim($tokenized[0])); $this->field = $this->fields[count($this->fields) - 1]; + $this->getOperatorType(trim($tokenized[1])); $tokenized[2] = trim($tokenized[2]); @@ -260,6 +261,7 @@ class Expression implements IQueryPart } } foreach ($values as $val) { + if (!is_string($val) && !is_numeric($val) && is_object($val)) { if (isset($val->service_description)) { $val = $val->service_description; @@ -273,7 +275,6 @@ class Expression implements IQueryPart return true; } } - return false; } @@ -291,6 +292,7 @@ class Expression implements IQueryPart $res = $this->query->get($res, $field); continue; } + if (!isset($res->$field)) { $res = array(); break; @@ -304,6 +306,10 @@ class Expression implements IQueryPart // array that contains the values/objects we're searching $swap = array(); foreach ($res as $sub) { + if ($this->query) { + $swap[] = $this->query->get($sub, $field); + continue; + } if (!isset($sub->$field)) { continue; } @@ -318,6 +324,7 @@ class Expression implements IQueryPart if (!is_array($res)) { return array($res); } + return $res; } diff --git a/library/Icinga/Protocol/Statusdat/Reader.php b/library/Icinga/Protocol/Statusdat/Reader.php index c4e0a289e..f31a71027 100755 --- a/library/Icinga/Protocol/Statusdat/Reader.php +++ b/library/Icinga/Protocol/Statusdat/Reader.php @@ -29,6 +29,7 @@ namespace Icinga\Protocol\Statusdat; use Icinga\Data\DatasourceInterface; +use Icinga\Exception\ConfigurationError; use Icinga\Exception; use Icinga\Benchmark; use Icinga\Protocol\Statusdat\View\MonitoringObjectList; @@ -40,54 +41,70 @@ use Icinga\Protocol\Statusdat\View\MonitoringObjectList; class Reader implements IReader, DatasourceInterface { /** - * + * The default lifetime of the cache in milliseconds */ - const DEFAULT_CACHE_LIFETIME = 300; + const DEFAULT_CACHE_LIFETIME = 30; /** + * The folder for the statusdat cache * */ const STATUSDAT_DEFAULT_CACHE_PATH = "/cache"; /** - * @var null + * The last state from the cache + * + * @var array */ - private $lastState = null; + private $lastState; /** + * True when this reader has already acquired the current runtime state (i.e. Status.dat) + * * @var bool */ private $hasRuntimeState = false; /** - * @var null + * The representation of the object.cache file + * + * @var array */ - private $objectCache = null; + private $objectCache ; /** - * @var null + * The representation of the status.dat file + * @var array */ - private $statusCache = null; + private $statusCache; /** + * True when the icinga state differs from the cache + * * @var bool */ private $newState = false; /** - * @var null + * The Parser object to use for parsing + * + * @var Parser */ - private $parser = null; + private $parser; /** + * Whether to disable the cache + * * @var bool */ - private $noCache = false; + private $noCache; /** - * @param $config - * @param null $parser - * @param bool $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) { @@ -121,7 +138,9 @@ class Reader implements IReader, DatasourceInterface } /** - * @throws Exception\ConfigurationError + * Initialize the internal caches if enabled + * + * @throws ConfigurationError */ private function initializeCaches() { @@ -130,7 +149,7 @@ class Reader implements IReader, DatasourceInterface $cachePath = $this->config->get('cache_path', $defaultCachePath); $maxCacheLifetime = intval($this->config->get('cache_path', self::DEFAULT_CACHE_LIFETIME)); if (!is_writeable($cachePath)) { - throw new Exception\ConfigurationError( + throw new ConfigurationError( "Cache path $cachePath is not writable, check your configuration" ); } @@ -145,9 +164,12 @@ class Reader implements IReader, DatasourceInterface } /** - * @param $file - * @param $backend - * @param $lifetime + * 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) @@ -161,7 +183,9 @@ class Reader implements IReader, DatasourceInterface } /** - * @return bool + * Read the current cache state + * + * @return bool True if the state is the same as the icinga state */ private function fromCache() { @@ -173,12 +197,13 @@ class Reader implements IReader, DatasourceInterface $this->newState = true; return false; } - return true; } /** - * @return bool + * Read the object.cache file from the Zend_Cache backend + * + * @return bool True if the file could be loaded from cache */ private function readObjectsCache() { @@ -191,16 +216,16 @@ class Reader implements IReader, DatasourceInterface } /** - * @return bool + * 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; } @@ -210,6 +235,7 @@ class Reader implements IReader, DatasourceInterface } /** + * Take the status.dat and objects.cache and connect all services to hosts * */ private function createHostServiceConnections() @@ -232,12 +258,14 @@ class Reader implements IReader, DatasourceInterface } /** - * @throws Exception\ConfigurationError + * Parse the object.cache file and update the current state + * + * @throws ConfigurationError If the object.cache couldn't be read */ private function parseObjectsCacheFile() { if (!is_readable($this->config->object_file)) { - throw new Exception\ConfigurationError( + throw new ConfigurationError( 'Can\'t read object-file "' . $this->config->object_file . '", check your configuration' ); } @@ -249,12 +277,14 @@ class Reader implements IReader, DatasourceInterface } /** - * @throws Exception\ConfigurationError + * 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 Exception\ConfigurationError( + throw new ConfigurationError( "Can't read status-file {$this->config->status_file}, check your configuration" ); } @@ -269,7 +299,9 @@ class Reader implements IReader, DatasourceInterface } /** - * @return Query + * Create a new Query + * + * @return Query The query to operate on */ public function select() { @@ -277,34 +309,23 @@ class Reader implements IReader, DatasourceInterface } /** - * @param Query $query - * @return MonitoringObjectList - */ - public function fetchAll(Query $query) - { - return $query->getResult(); - } - - /** - * @return mixed|null + * Return the internal state of the status.dat + * + * @return mixed The internal status.dat representation */ public function getState() { return $this->lastState; } - /** - * @return mixed|null - */ - public function getObjects() - { - return $this->lastState; - } /** - * @param $type - * @param $name - * @return ObjectContainer|mixed|null + * 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) { @@ -315,8 +336,10 @@ class Reader implements IReader, DatasourceInterface } /** - * @param $type - * @return array|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) { diff --git a/library/Icinga/Protocol/Statusdat/RuntimeStateContainer.php b/library/Icinga/Protocol/Statusdat/RuntimeStateContainer.php index 178cf1f55..50b86cc13 100755 --- a/library/Icinga/Protocol/Statusdat/RuntimeStateContainer.php +++ b/library/Icinga/Protocol/Statusdat/RuntimeStateContainer.php @@ -29,17 +29,25 @@ namespace Icinga\Protocol\Statusdat; /** - * Class RuntimeStateContainer - * @package Icinga\Protocol\Statusdat + * Container class containing the runtime state of an object + * + * This class contains the state of the object as a string and parses it + * on the fly as soon as values should be retrieved. This reduces memory usage, + * as most runtime information is never received and only lives for a very short time. + * */ class RuntimeStateContainer extends \stdClass { /** + * The state string + * * @var string */ public $runtimeState = ""; /** + * Create a new runtime state container from the givven string + * * @param string $str */ public function __construct($str = "") @@ -48,8 +56,10 @@ class RuntimeStateContainer extends \stdClass } /** - * @param $attr - * @return bool + * Return true if the argument exists + * + * @param String $attr The argument to retrieve + * @return bool True if it exists, otherwise false */ public function __isset($attr) { @@ -62,9 +72,13 @@ class RuntimeStateContainer extends \stdClass } /** - * @param $attr - * @return mixed - * @throws \InvalidArgumentException + * Return the given attribute + * + * If the container string is not yet parsed, this will happen here + * + * @param String $attr The attribute to retrieve + * @return mixed The value of the attribute + * @throws \InvalidArgumentException When the attribute does not exist */ public function __get($attr) { diff --git a/library/Icinga/Protocol/Statusdat/TreeToStatusdatQueryParser.php b/library/Icinga/Protocol/Statusdat/TreeToStatusdatQueryParser.php index 86c1add72..f1a2dcf17 100644 --- a/library/Icinga/Protocol/Statusdat/TreeToStatusdatQueryParser.php +++ b/library/Icinga/Protocol/Statusdat/TreeToStatusdatQueryParser.php @@ -30,6 +30,7 @@ namespace Icinga\Protocol\Statusdat; +use Icinga\Filter\Filterable; use Icinga\Filter\Query\Node; use Icinga\Filter\Query\Tree; use Icinga\Protocol\Statusdat\Query\Expression; @@ -38,33 +39,34 @@ use Icinga\Protocol\Statusdat\Query\Group; class TreeToStatusdatQueryParser { - private function nodeToQuery(Node $node) + private function nodeToQuery(Node $node, Filterable $source) { if ($node->type === Node::TYPE_OPERATOR) { $op = $node->operator; $value = $node->right; + $node->left = $source->getMappedField($node->left); if (stripos($node->right, '*') !== false) { $op = 'LIKE'; $value = str_replace('*', '%', $value); } - return new Expression($node->left . ' ' . $op . ' ?', $value); } else { $group = new Group(); $group->setType(($node->type === Node::TYPE_OR) ? Group::TYPE_OR : Group::TYPE_AND); - $group->addItem($this->nodeToQuery($node->left)); - $group->addItem($this->nodeToQuery($node->right)); + $group->addItem($this->nodeToQuery($node->left, $source)); + $group->addItem($this->nodeToQuery($node->right, $source)); return $group; } } - public function treeToQuery(Tree $tree) + public function treeToQuery(Tree $tree, Filterable $source) { + $tree = $tree->getCopyForFilterable($source); if ($tree->root !== null) { $tree->root = $tree->normalizeTree($tree->root); - return $this->nodeToQuery($tree->root); + return $this->nodeToQuery($tree->root, $source); } return null; } diff --git a/modules/monitoring/application/controllers/CommandController.php b/modules/monitoring/application/controllers/CommandController.php index e0f6279cc..e364e2c7e 100644 --- a/modules/monitoring/application/controllers/CommandController.php +++ b/modules/monitoring/application/controllers/CommandController.php @@ -189,15 +189,20 @@ class Monitoring_CommandController extends ActionController $query = Backend::createBackend($this->_getParam('backend'))->select()->from("status", $fields); $data = $query->fetchAll(); + $out = array(); foreach ($data as $o) { - $test = (array)$o; - if ($test['host_name'] === $hostname) { + if ($o->host_name === $hostname) { if (!$servicename) { - $out[] = (object) $o; - } elseif ($servicename && strtolower($test['service_description']) === strtolower($servicename)) { - $out[] = (object) $o; + $out[] = (object) array( + "host_name" => $o->host_name + ); + } elseif ($servicename && strtolower($o->service_description) === strtolower($servicename)) { + $out[] = (object) array( + "host_name" => $o->host_name, + "service_description" => $o->service_description + ); } } } @@ -208,6 +213,7 @@ class Monitoring_CommandController extends ActionController "CommandController: SQL Query '%s' failed (message %s) ", $query ? (string) $query->getQuery()->dump() : '--', $e->getMessage() ); + return array(); } } diff --git a/modules/monitoring/application/controllers/MultiController.php b/modules/monitoring/application/controllers/MultiController.php index 0d892ea67..abc51d6fa 100644 --- a/modules/monitoring/application/controllers/MultiController.php +++ b/modules/monitoring/application/controllers/MultiController.php @@ -125,7 +125,7 @@ class Monitoring_MultiController extends ActionController * * @return array */ - private function getUniqueValues(array $values, $key) + private function getUniqueValues($values, $key) { $unique = array(); foreach ($values as $value) @@ -144,7 +144,7 @@ class Monitoring_MultiController extends ActionController * * @param $object array The hosts or services */ - private function getProblems(array $objects) + private function getProblems($objects) { $problems = 0; foreach ($objects as $object) { diff --git a/modules/monitoring/application/views/scripts/list/downtimes.phtml b/modules/monitoring/application/views/scripts/list/downtimes.phtml index 5c5128903..22298d6b5 100644 --- a/modules/monitoring/application/views/scripts/list/downtimes.phtml +++ b/modules/monitoring/application/views/scripts/list/downtimes.phtml @@ -23,14 +23,14 @@ $commandHelper = $this->getHelper('CommandForm'); service)): ?> service ?> on host ?> host ?> + 'host' => $downtime->host_name + )); ?>">host ?>
downtime_author ?>: downtime_comment ?> diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/DowntimeQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/DowntimeQuery.php index 247ccdd91..b0e15be26 100644 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/DowntimeQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/DowntimeQuery.php @@ -35,31 +35,50 @@ class DowntimeQuery extends StatusdatQuery * @var array */ public static $mappedParameters = array( - - 'downtime_type' => 'downtime_type', - 'downtime_author_name' => 'author', - 'downtime_comment_data' => 'comment', - 'downtime_entry_time' => 'entry_time', - 'downtime_is_fixed' => 'is_fixed', + 'downtime_author' => 'author', + 'downtime_comment' => 'comment', 'downtime_duration' => 'duration', - 'downtime_scheduled_start_time' => 'start_time', - 'downtime_scheduled_end_time' => 'end_time', + 'downtime_end' => 'end_time', 'downtime_was_started' => 'was_started', - 'downtime_actual_start_time' => 'start_time', - 'downtime_actual_start_time_usec' => 'end_time', + 'downtime_is_fixed' => 'fixed', 'downtime_is_in_effect' => 'is_in_effect', 'downtime_trigger_time' => 'trigger_time', 'downtime_triggered_by_id' => 'triggered_by_id', 'downtime_internal_downtime_id' => 'internal_downtime_id', + 'host' => 'host_name', 'host_name' => 'host_name', 'service_host_name' => 'host_name', 'service_description' => 'service_description', ); public static $handlerParameters = array( - 'object_type' => 'getObjectType' + 'object_type' => 'getObjectType', + 'downtime_start' => 'getDowntimeStart', + 'downtime_is_flexible' => 'getFlexibleFlag' ); + public static $fieldTypes = array( + 'downtime_end' => self::TIMESTAMP, + 'downtime_trigger_time' => self::TIMESTAMP, + 'downtime_start' => self::TIMESTAMP + ); + + + public function getDowntimeStart(&$obj) + { + if ($obj->trigger_time != '0') { + return $obj->trigger_time; + } else { + return $obj->start_time; + } + } + + + public function getFlexibleFlag(&$obj) + { + return $obj->fixed ? 0 : 1; + } + public function getObjectType(&$obj) { return isset($obj->service_description) ? 'service ': 'host'; diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostgroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostgroupQuery.php index c304a74bb..8bb3f1af7 100755 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostgroupQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/HostgroupQuery.php @@ -38,8 +38,8 @@ class HostgroupQuery extends StatusdatQuery 'hostgroups' => 'hostgroup_name', 'hostgroup_name' => 'hostgroup_name', 'hostgroup_alias' => 'alias', - 'host' => 'hosts.host_name', - 'host_name' => 'hosts.host_name' + 'host' => 'host.host_name', + 'host_name' => 'host.host_name' ); public function selectBase() diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupQuery.php new file mode 100755 index 000000000..b08550f6a --- /dev/null +++ b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/ServicegroupQuery.php @@ -0,0 +1,52 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Module\Monitoring\Backend\Statusdat\Query; + +/** + * Class HostgroupsummaryQuery + * @package Icinga\Backend\Statusdat + */ +class ServicegroupQuery extends StatusdatQuery +{ + public static $mappedParameters = array( + 'servicegroups' => 'servicegroup_name', + 'servicegroup_name' => 'servicegroup_name', + 'servicegroup_alias' => 'alias', + 'host' => 'service.host_name', + 'host_name' => 'service.host_name', + 'service' => 'service.service_description', + 'service_description'=> 'service.service_description' + + ); + + public function selectBase() + { + $this->select()->from("servicegroups", array()); + } +} \ No newline at end of file diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusQuery.php index 7f3f16b63..e1141c7fa 100644 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusQuery.php @@ -20,7 +20,6 @@ class StatusQuery extends StatusdatQuery */ public static $mappedParameters = array( 'host' => 'host.host_name', - 'host_name' => 'host.host_name', 'host_display_name' => 'host.host_name', 'host_alias' => 'host.alias', @@ -140,7 +139,6 @@ class StatusQuery extends StatusdatQuery 'host_unhandled_service_count' => 'getNrOfUnhandledServices', 'host_last_comment' => 'getLastComment', 'service_last_comment' => 'getLastComment', - 'host_state' => 'getStateForHost', 'host_hard_state' => 'getHardStateForHost', 'host_handled' => 'isHandledForHost', @@ -148,7 +146,6 @@ class StatusQuery extends StatusdatQuery 'host_in_downtime' => 'isInDowntimeForHost', 'host_problem' => 'isProblemForHost', 'host_attempt' => 'getAttemptStringForHost', - 'service_state' => 'getState', 'service_hard_state' => 'getHardState', 'service_handled' => 'isHandled', @@ -158,6 +155,25 @@ class StatusQuery extends StatusdatQuery 'service_attempt' => 'getAttemptString' ); + public static $fieldTypes = array( + 'host_last_state_change' => self::TIMESTAMP, + 'host_last_hard_state_change' => self::TIMESTAMP, + 'host_last_check' => self::TIMESTAMP, + 'host_next_check' => self::TIMESTAMP, + 'host_last_time_up' => self::TIMESTAMP, + 'host_last_time_down' => self::TIMESTAMP, + 'host_last_time_unreachable' => self::TIMESTAMP, + 'host_status_update_time' => self::TIMESTAMP, + 'service_last_state_change' => self::TIMESTAMP, + 'service_last_hard_state_change' => self::TIMESTAMP, + 'service_last_check' => self::TIMESTAMP, + 'service_next_check' => self::TIMESTAMP, + 'service_last_time_ok' => self::TIMESTAMP, + 'service_last_time_warning' => self::TIMESTAMP, + 'service_last_time_critical' => self::TIMESTAMP, + 'service_last_time_unknown' => self::TIMESTAMP, + 'service_status_update_time' => self::TIMESTAMP + ); public function selectBase() { @@ -187,12 +203,15 @@ class StatusQuery extends StatusdatQuery public function getState(&$obj) { - return ($obj->status->has_been_checked == 1) ? $obj->status->current_state : 99; + if (!$obj->status->has_been_checked) { + return 99; + } + return $obj->status->current_state; } public function getHardState(&$obj) { - if (!$obj->status->has_been_checked == 1) { + if (!$obj->status->has_been_checked) { return 99; } else { if ($obj->status->state_type == 1) { @@ -209,13 +228,15 @@ class StatusQuery extends StatusdatQuery $status = $host->status; $severity = 0; - if (!$status->has_been_checked == 1) { + if (!$status->has_been_checked) { $severity += 16; - } else if ($status->current_state == 1) { + } elseif($status->current_state == 0) { + return $severity; + } elseif ($status->current_state == 1) { $severity += 32; } elseif ($status->current_state == 2) { $severity += 64; - } else if ($status->current_state > 0) { + } else { $severity += 256; } @@ -232,6 +253,7 @@ class StatusQuery extends StatusdatQuery public function isHandled(&$host) { + return $host->status->current_state == 0 || $host->status->problem_has_been_acknowledged || $host->status->scheduled_downtime_depth; diff --git a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusdatQuery.php b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusdatQuery.php index f84761812..4e6ba295c 100644 --- a/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusdatQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Statusdat/Query/StatusdatQuery.php @@ -46,6 +46,8 @@ use Icinga\Filter\Filterable; */ abstract class StatusdatQuery extends Query implements Filterable, AccessorStrategy { + const TIMESTAMP = 'timestamp'; + /** * An array containing the mappi * @@ -67,6 +69,7 @@ abstract class StatusdatQuery extends Query implements Filterable, AccessorStrat */ public static $handlerParameters = array(); + public static $fieldTypes = array(); /** * @var null @@ -93,6 +96,7 @@ abstract class StatusdatQuery extends Query implements Filterable, AccessorStrat { if ($column) { + $column = strval($column); if (isset(static::$mappedParameters[$column])) { parent::order(static::$mappedParameters[$column], strtolower($dir)); } elseif (isset(static::$handlerParameters[$column])) { @@ -123,19 +127,17 @@ abstract class StatusdatQuery extends Query implements Filterable, AccessorStrat */ public function get(&$item, $field) { - + $result = null; if (isset($item->$field)) { - return $item->$field; - } - if (isset(static::$mappedParameters[$field])) { - return $this->getMappedParameter($item, $field); + $result = $item->$field; + } elseif (isset(static::$mappedParameters[$field])) { + $result = $this->getMappedParameter($item, $field); + } elseif (isset(static::$handlerParameters[$field])) { + $hdl = static::$handlerParameters[$field]; + $result = $this->$hdl($item); } - if (isset(static::$handlerParameters[$field])) { - $hdl = static::$handlerParameters[$field]; - return $this->$hdl($item); - } - return null; + return $result; } private function applyPropertyFunction($function, $value) @@ -231,6 +233,10 @@ abstract class StatusdatQuery extends Query implements Filterable, AccessorStrat return true; } + public function isTimestamp($field) + { + return isset(static::$fieldTypes[$field]) && static::$fieldTypes[$field] === self::TIMESTAMP; + } } diff --git a/public/js/icinga/components/mainDetailGrid.js b/public/js/icinga/components/mainDetailGrid.js index 0884bc0aa..b2e53a5dc 100644 --- a/public/js/icinga/components/mainDetailGrid.js +++ b/public/js/icinga/components/mainDetailGrid.js @@ -309,8 +309,6 @@ function(Container, $, logger, URI, tpl, urlMgr, Selectable, TableMultiSelection contentNode = determineContentTable(); this.initRowSelection(); this.registerControls(); - this.registerHistoryChanges(); - }; this.construct(gridDomNode); diff --git a/test/php/library/Icinga/Protocol/Statusdat/QueryTest.php b/test/php/library/Icinga/Protocol/Statusdat/QueryTest.php index b8dadfb98..fa283ea2c 100755 --- a/test/php/library/Icinga/Protocol/Statusdat/QueryTest.php +++ b/test/php/library/Icinga/Protocol/Statusdat/QueryTest.php @@ -21,20 +21,19 @@ class QueryTest extends \PHPUnit_Framework_TestCase { $readerMock = $this->getServiceTestReader(); $query = new Statusdat\Query($readerMock); - - $result = $query->from("services")->getResult(); $objects = $readerMock->getObjects(); - $this->assertCount(count($objects["service"]), $result); + $result = $query->select()->from("services")->getResult(); + $this->assertCount(count($objects["service"]), $result); } public function testSimpleHostSelect() { $readerMock = $this->getServiceTestReader(); $query = new Statusdat\Query($readerMock); + $objects = $readerMock->getObjects(); $result = $query->from("hosts")->getResult(); - $objects = $readerMock->getObjects(); $this->assertCount(count($objects["host"]), $result); } diff --git a/test/php/library/Icinga/Protocol/Statusdat/ReaderMock.php b/test/php/library/Icinga/Protocol/Statusdat/ReaderMock.php index 998cdaed6..32c842fa9 100755 --- a/test/php/library/Icinga/Protocol/Statusdat/ReaderMock.php +++ b/test/php/library/Icinga/Protocol/Statusdat/ReaderMock.php @@ -17,6 +17,12 @@ class ReaderMock implements IReader, DatasourceInterface public function getState() { + return $this->objects; + } + + public function getInternalState() + { + return array( "objects" => $this->objects, "indices" => $this->indices diff --git a/test/php/library/Icinga/Protocol/Statusdat/StatusdatTestLoader.php b/test/php/library/Icinga/Protocol/Statusdat/StatusdatTestLoader.php index 24b670c6f..9244640d9 100644 --- a/test/php/library/Icinga/Protocol/Statusdat/StatusdatTestLoader.php +++ b/test/php/library/Icinga/Protocol/Statusdat/StatusdatTestLoader.php @@ -20,6 +20,7 @@ class StatusdatTestLoader extends LibraryLoader require_once($libPath . '/Data/DatasourceInterface.php'); $statusdat = realpath($libPath . '/Protocol/Statusdat/'); require_once($statusdat . '/View/AccessorStrategy.php'); + require_once($statusdat . '/PrintableObject.php'); require_once($statusdat . '/View/MonitoringObjectList.php'); require_once($statusdat . '/ObjectContainer.php'); require_once($statusdat . '/IReader.php');