257 lines
6.9 KiB
PHP
257 lines
6.9 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace Icinga\Protocol\Statusdat;
|
||
|
use Icinga\Protocol;
|
||
|
|
||
|
class Query extends Protocol\AbstractQuery
|
||
|
{
|
||
|
public static $VALID_TARGETS = array(
|
||
|
"hosts" => array("host"),
|
||
|
"services" => array("service"),
|
||
|
"downtimes" => array("hostdowntime", "servicedowntime"),
|
||
|
"hostdowntimes" => array("hostdowntime"),
|
||
|
"servicedowntimes" => array("servicedowntime"),
|
||
|
"hostgroups" => array("hostgroup"),
|
||
|
"servicegroups" => array("servicegroup"),
|
||
|
"comments" => array("servicecomment", "hostcomment"),
|
||
|
"hostcomments" => array("hostcomment"),
|
||
|
"servicecomments" => array("servicecomment")
|
||
|
);
|
||
|
|
||
|
private $reader = null;
|
||
|
private $source = "";
|
||
|
private $columns = array();
|
||
|
private $limit = null;
|
||
|
private $offset = 0;
|
||
|
private $order_columns = array();
|
||
|
private $groupColumns = array();
|
||
|
private $groupByFn = null;
|
||
|
private $filter = array();
|
||
|
|
||
|
// Magic indexes
|
||
|
const FN_SCOPE = 0;
|
||
|
const FN_NAME = 1;
|
||
|
|
||
|
public function hasOrder()
|
||
|
{
|
||
|
return !empty($this->order_columns);
|
||
|
}
|
||
|
|
||
|
public function hasColumns()
|
||
|
{
|
||
|
return !empty($this->columns);
|
||
|
}
|
||
|
|
||
|
public function getColumns()
|
||
|
{
|
||
|
return $this->columns;
|
||
|
|
||
|
}
|
||
|
|
||
|
public function hasLimit()
|
||
|
{
|
||
|
return $this->limit !== false;
|
||
|
}
|
||
|
|
||
|
public function hasOffset()
|
||
|
{
|
||
|
return $this->offset !== false;
|
||
|
}
|
||
|
|
||
|
public function getLimit()
|
||
|
{
|
||
|
return $this->limit;
|
||
|
}
|
||
|
|
||
|
public function getOffset()
|
||
|
{
|
||
|
return $this->offset;
|
||
|
}
|
||
|
|
||
|
public function __construct(IReader $reader)
|
||
|
{
|
||
|
$this->reader = $reader;
|
||
|
}
|
||
|
|
||
|
public function where($key, $val = null)
|
||
|
{
|
||
|
$this->filter[] = array($key, $val);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
public function order($columns, $dir = null)
|
||
|
{
|
||
|
if($dir && strtolower($dir) == "desc")
|
||
|
$dir = self::SORT_DESC;
|
||
|
else
|
||
|
$dir = self::SORT_ASC;
|
||
|
if(!is_array($columns))
|
||
|
$columns = array($columns);
|
||
|
foreach($columns as $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;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
} else {
|
||
|
throw new Exception("Got invalid limit $count, $offset");
|
||
|
}
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
public function from($table, $columns = null)
|
||
|
{
|
||
|
if (isset(self::$VALID_TARGETS[$table]))
|
||
|
$this->source = $table;
|
||
|
else
|
||
|
throw new \Exception("Unknown from target for status.dat :". $table);
|
||
|
$this->columns = $columns;
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @throws Exception
|
||
|
*/
|
||
|
private function getFilteredIndices($classType = "\Icinga\Protocol\Statusdat\Query\Group")
|
||
|
{
|
||
|
$baseGroup = null;
|
||
|
if (!empty($this->filter)) {
|
||
|
$baseGroup = new $classType();
|
||
|
|
||
|
foreach ($this->filter as $values) {
|
||
|
$baseGroup->addItem(new $classType($values[0], $values[1]));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$state = $this->reader->getObjects();
|
||
|
$result = array();
|
||
|
foreach (self::$VALID_TARGETS[$this->source] as $target) {
|
||
|
$indexes = & array_keys($state[$target]);
|
||
|
if ($baseGroup) {
|
||
|
$indexes = & $baseGroup->filter($state[$target]);
|
||
|
}
|
||
|
if (!isset($result[$target])) {
|
||
|
$result[$target] = $indexes;
|
||
|
} else {
|
||
|
array_merge($result[$target], $indexes);
|
||
|
}
|
||
|
}
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
private function orderIndices(array &$indices) {
|
||
|
if(!empty($this->order_columns)) {
|
||
|
foreach($indices as $type=>&$subindices) {
|
||
|
$this->currentType = $type; // we're singlethreaded, so let's do it a bit dirty
|
||
|
usort($subindices,array($this,"orderResult"));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private function orderResult($a,$b) {
|
||
|
$o1 = &$this->reader->getObjectByName($this->currentType,$a);
|
||
|
$o2 = &$this->reader->getObjectByName($this->currentType,$b);
|
||
|
$result = 0;
|
||
|
foreach($this->order_columns as $col) {
|
||
|
|
||
|
$result += $col[1]*strnatcasecmp($o1->{$col[0]},$o2->{$col[0]});
|
||
|
}
|
||
|
if($result > 0)
|
||
|
return 1;
|
||
|
if($result < 0)
|
||
|
return -1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
private function limitIndices(array &$indices)
|
||
|
{
|
||
|
foreach($indices as $type=>$subindices){
|
||
|
$indices[$type] = array_slice($subindices,$this->offset,$this->limit);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function groupByFunction($fn,$scope=null)
|
||
|
{
|
||
|
$this->groupByFn = array($scope ? $scope : $this,$fn);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
public function groupByColumns($columns)
|
||
|
{
|
||
|
if(!is_array($columns))
|
||
|
$columns = array($columns);
|
||
|
$this->groupColumns = $columns;
|
||
|
$this->groupByFn = array($this,"columnGroupFn");
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
private function columnGroupFn(array &$indices)
|
||
|
{
|
||
|
$cols = $this->groupColumns;
|
||
|
$result = array();
|
||
|
foreach($indices as $type=>$subindices) {
|
||
|
foreach($subindices as $objectIndex) {
|
||
|
$r = &$this->reader->getObjectByName($type,$objectIndex);
|
||
|
$hash = "";
|
||
|
$cols = array();
|
||
|
foreach($this->groupColumns as $col) {
|
||
|
$hash = md5($hash.$r->$col);
|
||
|
$cols[$col] = $r->$col;
|
||
|
}
|
||
|
if(!isset($result[$hash]))
|
||
|
$result[$hash] = (object) array(
|
||
|
"columns" => (object) $cols,
|
||
|
"count" => 0
|
||
|
);
|
||
|
$result[$hash]->count++;
|
||
|
}
|
||
|
}
|
||
|
return array_values($result);
|
||
|
}
|
||
|
|
||
|
public function getResult()
|
||
|
{
|
||
|
|
||
|
$indices = &$this->getFilteredIndices();
|
||
|
$this->orderIndices($indices);
|
||
|
if($this->groupByFn) {
|
||
|
$scope = $this->groupByFn[self::FN_SCOPE];
|
||
|
$fn = $this->groupByFn[self::FN_NAME];
|
||
|
|
||
|
return $scope->$fn($indices);
|
||
|
}
|
||
|
|
||
|
$this->limitIndices($indices);
|
||
|
|
||
|
$result = array();
|
||
|
$state = &$this->reader->getObjects();
|
||
|
foreach ($indices as $type=>$subindices) {
|
||
|
|
||
|
foreach($subindices as $index) {
|
||
|
$result[] = &$state[$type][$index];
|
||
|
}
|
||
|
}
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
}
|