icingaweb2-module-businessp.../library/Businessprocess/BpNode.php
Ravi Kumar Kempapura Srinivasa e19612bd4a Extend abstract isEmpty() method to check for empty nodes in child nodes
We need to check recurively whether the BP Nodes contatin only child BpNodes which are empty and set the entire tree to state NODE_EMPTY state in this case.
2020-05-11 11:45:17 +02:00

587 lines
15 KiB
PHP

<?php
namespace Icinga\Module\Businessprocess;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotFoundError;
use Icinga\Module\Businessprocess\Exception\NestingError;
class BpNode extends Node
{
const OP_AND = '&';
const OP_OR = '|';
const OP_NOT = '!';
protected $operator = '&';
protected $url;
protected $info_command;
protected $display = 0;
/** @var Node[] */
protected $children;
/** @var array */
protected $childNames = array();
protected $counters;
protected $missing = null;
protected $empty = null;
protected $missingChildren;
protected static $emptyStateSummary = array(
'OK' => 0,
'WARNING' => 0,
'CRITICAL' => 0,
'UNKNOWN' => 0,
'PENDING' => 0,
'UP' => 0,
'DOWN' => 0,
'UNREACHABLE' => 0,
'MISSING' => 0,
);
protected static $sortStateInversionMap = array(
4 => 0,
3 => 0,
2 => 2,
1 => 1,
0 => 4
);
protected $className = 'process';
public function __construct($object)
{
$this->name = $object->name;
$this->operator = $object->operator;
$this->childNames = $object->child_names;
}
public function getStateSummary()
{
if ($this->counters === null) {
$this->getState();
$this->counters = self::$emptyStateSummary;
foreach ($this->getChildren() as $child) {
if ($child instanceof BpNode) {
$counters = $child->getStateSummary();
foreach ($counters as $k => $v) {
$this->counters[$k] += $v;
}
} elseif ($child->isMissing()) {
$this->counters['MISSING']++;
} else {
$state = $child->getStateName();
$this->counters[$state]++;
}
}
}
return $this->counters;
}
public function hasProblems()
{
if ($this->isProblem()) {
return true;
}
$okStates = array('OK', 'UP', 'PENDING', 'MISSING');
foreach ($this->getStateSummary() as $state => $cnt) {
if ($cnt !== 0 && ! in_array($state, $okStates)) {
return true;
}
}
return false;
}
/**
* @param Node $node
* @return $this
* @throws ConfigurationError
*/
public function addChild(Node $node)
{
if ($this->children === null) {
$this->getChildren();
}
$name = $node->getName();
if (array_key_exists($name, $this->children)) {
throw new ConfigurationError(
'Node "%s" has been defined more than once',
$name
);
}
$this->children[$name] = $node;
$this->childNames[] = $name;
$this->reorderChildren();
$node->addParent($this);
return $this;
}
public function getProblematicChildren()
{
$problems = array();
foreach ($this->getChildren() as $child) {
if ($child->isProblem()
|| ($child instanceof BpNode && $child->hasProblems())
) {
$problems[] = $child;
}
}
return $problems;
}
public function hasChild($name)
{
return in_array($name, $this->getChildNames());
}
public function removeChild($name)
{
if (($key = array_search($name, $this->getChildNames())) !== false) {
unset($this->childNames[$key]);
if (! empty($this->children)) {
unset($this->children[$name]);
}
}
return $this;
}
public function getProblemTree()
{
$tree = array();
foreach ($this->getProblematicChildren() as $child) {
$name = $child->getName();
$tree[$name] = array(
'node' => $child,
'children' => array()
);
if ($child instanceof BpNode) {
$tree[$name]['children'] = $child->getProblemTree();
}
}
return $tree;
}
/**
* Get the problem nodes as tree reduced to the nodes which have the same state as the business process
*
* @param bool $rootCause Reduce nodes to the nodes which are responsible for the state of the business process
*
* @return array
*/
public function getProblemTreeBlame($rootCause = false)
{
$tree = [];
$nodeState = $this->getState();
if ($nodeState !== 0) {
foreach ($this->getChildren() as $child) {
$childState = $rootCause ? $child->getSortingState() : $child->getState();
if (($rootCause ? $this->getSortingState() : $nodeState) === $childState) {
$name = $child->getName();
$tree[$name] = [
'children' => [],
'node' => $child
];
if ($child instanceof BpNode) {
$tree[$name]['children'] = $child->getProblemTreeBlame($rootCause);
}
}
}
}
return $tree;
}
public function isMissing()
{
if ($this->missing === null) {
$exists = false;
$bp = $this->getBpConfig();
$bp->beginLoopDetection($this->name);
foreach ($this->getChildren() as $child) {
if (! $child->isMissing()) {
$exists = true;
}
}
$bp->endLoopDetection($this->name);
$this->missing = ! $exists && ! empty($this->getChildren());
}
return $this->missing;
}
public function isEmpty()
{
$bp = $this->getBpConfig();
$empty = true;
if ($this->countChildren()) {
$bp->beginLoopDetection($this->name);
foreach ($this->getChildren() as $child) {
if ($child instanceof MonitoredNode) {
$empty = false;
break;
} elseif (!$child->isEmpty()) {
$empty = false;
}
}
$bp->endLoopDetection($this->name);
}
$this->empty = $empty;
return $this->empty;
}
public function getMissingChildren()
{
if ($this->missingChildren === null) {
$missing = array();
foreach ($this->getChildren() as $child) {
if ($child->isMissing()) {
$missing[$child->getName()] = $child;
}
foreach ($child->getMissingChildren() as $m) {
$missing[$m->getName()] = $m;
}
}
$this->missingChildren = $missing;
}
return $this->missingChildren;
}
public function getOperator()
{
return $this->operator;
}
public function setOperator($operator)
{
$this->assertValidOperator($operator);
$this->operator = $operator;
return $this;
}
protected function assertValidOperator($operator)
{
switch ($operator) {
case self::OP_AND:
case self::OP_OR:
case self::OP_NOT:
return;
default:
if (is_numeric($operator)) {
return;
}
}
throw new ConfigurationError(
'Got invalid operator: %s',
$operator
);
}
public function setInfoUrl($url)
{
$this->url = $url;
return $this;
}
public function hasInfoUrl()
{
return ! empty($this->url);
}
public function getInfoUrl()
{
return $this->url;
}
public function setInfoCommand($cmd)
{
$this->info_command = $cmd;
}
public function hasInfoCommand()
{
return $this->info_command !== null;
}
public function getInfoCommand()
{
return $this->info_command;
}
public function getAlias()
{
return $this->alias ? preg_replace('~_~', ' ', $this->alias) : $this->name;
}
/**
* @return int
*/
public function getState()
{
if ($this->state === null) {
try {
$this->reCalculateState();
} catch (NestingError $e) {
$this->getBpConfig()->addError(
$this->getBpConfig()->translate('Nesting error detected: %s'),
$e->getMessage()
);
// Failing nodes are unknown
$this->state = 3;
}
}
return $this->state;
}
public function getHtmlId()
{
return 'businessprocess-' . preg_replace('/[\r\n\t\s]/', '_', $this->getName());
}
protected function invertSortingState($state)
{
return self::$sortStateInversionMap[$state >> self::SHIFT_FLAGS] << self::SHIFT_FLAGS;
}
/**
* @return $this
*/
public function reCalculateState()
{
$bp = $this->getBpConfig();
$sort_states = array();
$lastStateChange = 0;
if (!$this->hasChildren()) {
// TODO: delegate this to operators, should mostly fail
$this->setState(self::NODE_EMPTY);
return $this;
}
foreach ($this->getChildren() as $child) {
$bp->beginLoopDetection($this->name);
if ($child instanceof MonitoredNode && $child->isMissing()) {
if ($child instanceof HostNode) {
$child->setState(self::ICINGA_UNREACHABLE);
} else {
$child->setState(self::ICINGA_UNKNOWN);
}
$child->setMissing();
}
$sort_states[] = $child->getSortingState();
$lastStateChange = max($lastStateChange, $child->getLastStateChange());
$bp->endLoopDetection($this->name);
}
$this->setLastStateChange($lastStateChange);
switch ($this->getOperator()) {
case self::OP_AND:
$sort_state = max($sort_states);
break;
case self::OP_NOT:
$sort_state = $this->invertSortingState(max($sort_states));
break;
case self::OP_OR:
$sort_state = min($sort_states);
break;
default:
// MIN:
$sort_state = 3 << self::SHIFT_FLAGS;
if (count($sort_states) >= $this->operator) {
$actualGood = 0;
foreach ($sort_states as $s) {
if (($s >> self::SHIFT_FLAGS) === self::ICINGA_OK) {
$actualGood++;
}
}
if ($actualGood >= $this->operator) {
// condition is fulfilled
$sort_state = self::ICINGA_OK;
} else {
// worst state if not fulfilled
$sort_state = max($sort_states);
}
}
}
if ($sort_state & self::FLAG_DOWNTIME) {
$this->setDowntime(true);
}
if ($sort_state & self::FLAG_ACK) {
$this->setAck(true);
}
$this->state = $this->sortStateTostate($sort_state);
return $this;
}
public function checkForLoops()
{
$bp = $this->getBpConfig();
foreach ($this->getChildren() as $child) {
$bp->beginLoopDetection($this->name);
if ($child instanceof BpNode) {
$child->checkForLoops();
}
$bp->endLoopDetection($this->name);
}
return $this;
}
public function setDisplay($display)
{
$this->display = (int) $display;
return $this;
}
public function getDisplay()
{
return $this->display;
}
public function setChildNames($names)
{
$this->childNames = $names;
$this->children = null;
$this->reorderChildren();
return $this;
}
public function hasChildren($filter = null)
{
$childNames = $this->getChildNames();
return !empty($childNames);
}
public function getChildNames()
{
return $this->childNames;
}
public function getChildren($filter = null)
{
if ($this->children === null) {
$this->children = [];
$this->reorderChildren();
foreach ($this->getChildNames() as $name) {
$this->children[$name] = $this->getBpConfig()->getNode($name);
$this->children[$name]->addParent($this);
}
}
return $this->children;
}
/**
* Reorder this node's children, in case manual order is not applied
*/
protected function reorderChildren()
{
if ($this->getBpConfig()->getMetadata()->isManuallyOrdered()) {
return;
}
$childNames = $this->getChildNames();
natcasesort($childNames);
$this->childNames = array_values($childNames);
if (! empty($this->children)) {
$children = [];
foreach ($this->childNames as $name) {
$children[$name] = $this->children[$name];
}
$this->children = $children;
}
}
/**
* return BpNode[]
*/
public function getChildBpNodes()
{
$children = array();
foreach ($this->getChildren() as $name => $child) {
if ($child instanceof BpNode) {
$children[$name] = $child;
}
}
return $children;
}
/**
* @param $childName
* @return Node
* @throws NotFoundError
*/
public function getChildByName($childName)
{
foreach ($this->getChildren() as $name => $child) {
if ($name === $childName) {
return $child;
}
}
throw new NotFoundError('Trying to get missing child %s', $childName);
}
protected function assertNumericOperator()
{
if (! is_numeric($this->getOperator())) {
throw new ConfigurationError('Got invalid operator: %s', $this->operator);
}
}
public function operatorHtml()
{
switch ($this->getOperator()) {
case self::OP_AND:
return 'AND';
break;
case self::OP_OR:
return 'OR';
break;
case self::OP_NOT:
return 'NOT';
break;
default:
// MIN
$this->assertNumericOperator();
return 'min:' . $this->operator;
}
}
public function getIcon()
{
$this->icon = $this->hasParents() ? 'cubes' : 'sitemap';
return parent::getIcon();
}
}