424 lines
12 KiB
PHP
Executable File
424 lines
12 KiB
PHP
Executable File
<?php
|
|
// {{{ICINGA_LICENSE_HEADER}}}
|
|
/**
|
|
* This file is part of Icinga 2 Web.
|
|
*
|
|
* Icinga 2 Web - 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.
|
|
*
|
|
* @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\Application\Logger;
|
|
use Icinga\Exception\ConfigurationError;
|
|
use Icinga\Exception\ProgrammingError;
|
|
use Icinga\Protocol\Statusdat\Exception\ParsingException as ParsingException;
|
|
|
|
/**
|
|
* Class Parser
|
|
* @package Icinga\Protocol\Statusdat
|
|
*/
|
|
class Parser
|
|
{
|
|
/**
|
|
* @var array
|
|
*/
|
|
private $deferred = array();
|
|
|
|
/**
|
|
* @var null|resource
|
|
*/
|
|
private $filehandle = null;
|
|
|
|
/**
|
|
* @var null
|
|
*/
|
|
private $currentObjectType = null;
|
|
|
|
/**
|
|
* @var null
|
|
*/
|
|
private $currentStateType = null;
|
|
|
|
/**
|
|
* @var null
|
|
*/
|
|
private $icingaState = null;
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
private $lineCtr = 0;
|
|
|
|
/**
|
|
* @param null $filehandle
|
|
* @param null $baseState
|
|
* @throws \Icinga\Exception\ConfigurationError
|
|
*/
|
|
public function __construct($filehandle = null, $baseState = null)
|
|
{
|
|
if (!is_resource($filehandle)) {
|
|
throw new ConfigurationError("Statusdat parser can't find $filehandle");
|
|
}
|
|
|
|
$this->filehandle = $filehandle;
|
|
$this->icingaState = $baseState;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public function parseObjectsFile()
|
|
{
|
|
$DEFINE = strlen("define ");
|
|
$filehandle = $this->filehandle;
|
|
$this->icingaState = array();
|
|
while (!feof($filehandle)) {
|
|
|
|
$line = trim(fgets($filehandle));
|
|
|
|
$this->lineCtr++;
|
|
if ($line === "" || $line[0] === "#") {
|
|
continue;
|
|
}
|
|
$this->currentObjectType = trim(substr($line, $DEFINE, -1));
|
|
if (!isset($this->icingaState[$this->currentObjectType])) {
|
|
$this->icingaState[$this->currentObjectType] = array();
|
|
}
|
|
$this->readCurrentObject();
|
|
}
|
|
$this->processDeferred();
|
|
}
|
|
|
|
/**
|
|
* @param null $filehandle
|
|
* @throws ProgrammingError
|
|
*/
|
|
public function parseRuntimeState($filehandle = null)
|
|
{
|
|
if ($filehandle != null) {
|
|
$this->filehandle = $filehandle;
|
|
} else {
|
|
$filehandle = $this->filehandle;
|
|
}
|
|
|
|
if (!$this->icingaState) {
|
|
throw new ProgrammingError("Tried to read runtime state without existing objects data");
|
|
}
|
|
$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
|
|
*/
|
|
private function readCurrentObject()
|
|
{
|
|
$filehandle = $this->filehandle;
|
|
$monitoringObject = new \stdClass();
|
|
while (!feof($filehandle)) {
|
|
$line = explode("\t", trim(fgets($filehandle)), 2);
|
|
$this->lineCtr++;
|
|
if (!$line) {
|
|
continue;
|
|
}
|
|
|
|
// End of object
|
|
if ($line[0] === "}") {
|
|
$this->registerObject($monitoringObject);
|
|
return;
|
|
}
|
|
if (!isset($line[1])) {
|
|
$line[1] = "";
|
|
}
|
|
$monitoringObject->{$line[0]} = trim($line[1]);
|
|
}
|
|
throw new ParsingException("Unexpected EOF in objects.cache, line " . $this->lineCtr);
|
|
}
|
|
|
|
/**
|
|
* @throws Exception\ParsingException
|
|
*/
|
|
private function readCurrentState()
|
|
{
|
|
$filehandle = $this->filehandle;
|
|
$statusdatObject = new RuntimeStateContainer();
|
|
|
|
$objectType = $this->getObjectTypeForState();
|
|
|
|
if ($objectType != "host" && $objectType != "service") {
|
|
$this->skipObject(); // ignore unknown objects
|
|
return;
|
|
}
|
|
if (!isset($this->icingaState[$this->currentObjectType])) {
|
|
throw new ParsingException("No $this->currentObjectType objects registered in objects.cache");
|
|
}
|
|
$base = & $this->icingaState[$this->currentObjectType];
|
|
$state = $this->skipObject(true);
|
|
$statusdatObject->runtimeState = & $state;
|
|
$name = $this->getObjectIdentifier($statusdatObject);
|
|
|
|
if (!isset($base[$name])) {
|
|
throw new ParsingException(
|
|
"Unknown object $name " . $this->currentObjectType . " - "
|
|
. print_r(
|
|
$statusdatObject,
|
|
true
|
|
)
|
|
. "\n" . print_r($base, true)
|
|
);
|
|
}
|
|
$type = substr($this->currentStateType, strlen($objectType));
|
|
|
|
if ($type == "status") {
|
|
$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;
|
|
}
|
|
array_push($base[$name]->$type, $statusdatObject);
|
|
$this->currentObjectType = $type;
|
|
if (!isset($this->icingaState[$type])) {
|
|
$this->icingaState[$type] = array();
|
|
}
|
|
$this->icingaState[$type][] = &$statusdatObject;
|
|
$id = $this->getObjectIdentifier($statusdatObject);
|
|
if ($id !== false && isset($this->icingaState[$objectType][$id])) {
|
|
$statusdatObject->$objectType = $this->icingaState[$objectType][$id];
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/**
|
|
* @return null|string
|
|
*/
|
|
private function getObjectTypeForState()
|
|
{
|
|
$pos = strpos($this->currentStateType, "service");
|
|
|
|
if ($pos === false) {
|
|
$pos = strpos($this->currentStateType, "host");
|
|
} else {
|
|
$this->currentObjectType = "service";
|
|
return "service";
|
|
}
|
|
|
|
if ($pos === false) {
|
|
return $this->currentStateType;
|
|
} else {
|
|
$this->currentObjectType = "host";
|
|
return "host";
|
|
}
|
|
|
|
return $this->currentObjectType;
|
|
}
|
|
|
|
/**
|
|
* @param bool $returnString
|
|
* @return string
|
|
*/
|
|
protected function skipObject($returnString = false)
|
|
{
|
|
if (!$returnString) {
|
|
while (trim(fgets($this->filehandle)) !== "}") {
|
|
}
|
|
return null;
|
|
} else {
|
|
$str = "";
|
|
while (($val = trim(fgets($this->filehandle))) !== "}") {
|
|
$str .= $val . "\n";
|
|
}
|
|
return $str;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $object
|
|
*/
|
|
protected function registerObject(&$object)
|
|
{
|
|
|
|
$name = $this->getObjectIdentifier($object);
|
|
if ($name !== false) {
|
|
$this->icingaState[$this->currentObjectType][$name] = &$object;
|
|
}
|
|
$this->registerObjectAsProperty($object);
|
|
}
|
|
|
|
/**
|
|
* @param $object
|
|
*/
|
|
protected function registerObjectAsProperty(&$object)
|
|
{
|
|
if ($this->currentObjectType == 'service'
|
|
|| $this->currentObjectType == 'host'
|
|
|| $this->currentObjectType == 'contact') {
|
|
return null;
|
|
}
|
|
$isService = strpos($this->currentObjectType, "service") !== false;
|
|
$isHost = strpos($this->currentObjectType, "host") !== false;
|
|
$isContact = strpos($this->currentObjectType, "contact") !== false;
|
|
$name = $this->getObjectIdentifier($object);
|
|
|
|
if ($isService === false && $isHost === false && $isContact === false) { // this would be error in the parser implementation
|
|
return null;
|
|
}
|
|
$property = $this->currentObjectType;
|
|
if ($isService) {
|
|
$this->currentObjectType = "service";
|
|
$property = substr($property, strlen("service"));
|
|
} elseif ($isHost) {
|
|
$this->currentObjectType = "host";
|
|
$property = substr($property, strlen("host"));
|
|
} elseif ($isContact) {
|
|
$this->currentObjectType = "contact";
|
|
$property = substr($property, strlen("contact"));
|
|
}
|
|
|
|
if (!isset($this->icingaState[$this->currentObjectType])) {
|
|
return $this->deferRegistration($object, $this->currentObjectType . $property);
|
|
}
|
|
|
|
// @TODO: Clean up, this differates between 1:n and 1:1 references
|
|
if (strpos($property, "group") !== false) {
|
|
$sourceIdentifier = $this->getMembers($object);
|
|
foreach ($sourceIdentifier as $id) {
|
|
$source = $this->icingaState[$this->currentObjectType][$id];
|
|
if (!isset($source->$property)) {
|
|
$source->$property = array();
|
|
}
|
|
$type = $this->currentObjectType;
|
|
if (!isset($object->$type)) {
|
|
$object->$type = array();
|
|
}
|
|
// add the member to the group object
|
|
array_push($object->$type, $source);
|
|
// add the group to the member object
|
|
array_push($source->$property, $name);
|
|
}
|
|
} else {
|
|
$source = $this->icingaState[$this->currentObjectType][$this->getObjectIdentifier($object)];
|
|
if (!isset($source->$property)) {
|
|
$source->$property = array();
|
|
}
|
|
|
|
array_push($source->$property, $object);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param $object
|
|
* @param $objType
|
|
*/
|
|
protected function deferRegistration($object, $objType)
|
|
{
|
|
$this->deferred[] = array($object, $objType);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
protected function processDeferred()
|
|
{
|
|
foreach ($this->deferred as $obj) {
|
|
$this->currentObjectType = $obj[1];
|
|
$this->registerObjectAsProperty($obj[0]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param $object
|
|
* @return array
|
|
*/
|
|
protected function getMembers(&$object)
|
|
{
|
|
if (!isset($object->members)) {
|
|
return array();
|
|
}
|
|
|
|
$members = explode(",", $object->members);
|
|
|
|
if ($this->currentObjectType == "service") {
|
|
$res = array();
|
|
for ($i = 0; $i < count($members); $i += 2) {
|
|
$res[] = $members[$i] . ";" . $members[$i + 1];
|
|
}
|
|
return $res;
|
|
} else {
|
|
return $members;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* @param $object
|
|
* @return bool|string
|
|
*/
|
|
protected function getObjectIdentifier(&$object)
|
|
{
|
|
if ($this->currentObjectType == 'contact') {
|
|
return $object->contact_name;
|
|
}
|
|
|
|
if ($this->currentObjectType == "service") {
|
|
return $object->host_name . ";" . $object->service_description;
|
|
}
|
|
$name = $this->currentObjectType . "_name";
|
|
if (isset($object->{$name})) {
|
|
return $object->{$name};
|
|
}
|
|
if (isset($object->service_description)) {
|
|
return $object->host_name . ";" . $object->service_description;
|
|
} elseif (isset($object->host_name)) {
|
|
return $object->host_name;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
/**
|
|
* @return null
|
|
*/
|
|
public function getRuntimeState()
|
|
{
|
|
return $this->icingaState;
|
|
}
|
|
}
|