Apply state overrides on demand instead of directly

Internally non-process children are only instantiated once.
This means when applying state overrides directly they're
used everywhere and do not differ between the containing
process. State overrides are now applied explicitly and
on demand, decoupling them from children.
This commit is contained in:
Johannes Meyer 2020-06-30 16:07:47 +02:00
parent a8149a1983
commit 49ebbc4cdb
12 changed files with 133 additions and 106 deletions

View file

@ -167,21 +167,22 @@ class ProcessCommand extends Command
} }
} }
protected function renderProblemTree($tree, $useColors = false, $depth = 0) protected function renderProblemTree($tree, $useColors = false, $depth = 0, BpNode $parent = null)
{ {
$output = ''; $output = '';
foreach ($tree as $name => $subtree) { foreach ($tree as $name => $subtree) {
/** @var Node $node */ /** @var Node $node */
$node = $subtree['node']; $node = $subtree['node'];
$state = $parent !== null ? $parent->getChildState($node) : $node->getState();
if ($node instanceof HostNode) { if ($node instanceof HostNode) {
$colors = $this->hostColors[$node->getState()]; $colors = $this->hostColors[$state];
} else { } else {
$colors = $this->serviceColors[$node->getState()]; $colors = $this->serviceColors[$state];
} }
$state = sprintf('[%s]', $node->getStateName()); $state = sprintf('[%s]', $node->getStateName($state));
if ($useColors) { if ($useColors) {
$state = $this->screen->colorize($state, $colors[0], $colors[1]); $state = $this->screen->colorize($state, $colors[0], $colors[1]);
} }
@ -193,7 +194,10 @@ class ProcessCommand extends Command
$state, $state,
$node->getAlias() $node->getAlias()
); );
$output .= $this->renderProblemTree($subtree['children'], $useColors, $depth + 1);
if ($node instanceof BpNode) {
$output .= $this->renderProblemTree($subtree['children'], $useColors, $depth + 1, $node);
}
} }
return $output; return $output;

View file

@ -502,16 +502,17 @@ class AddNodeForm extends QuickForm
$changes = ProcessChanges::construct($this->bp, $this->session); $changes = ProcessChanges::construct($this->bp, $this->session);
switch ($this->getValue('node_type')) { switch ($this->getValue('node_type')) {
case 'service': case 'service':
$properties = $this->getValues(); $stateOverrides = $this->getValue('stateOverrides');
unset($properties['children']); if (! empty($stateOverrides)) {
$services = []; $services = [];
foreach ($this->getValue('children') as $service) { foreach ($this->getValue('children') as $service) {
$services[$service] = $properties; $services[$service] = $stateOverrides;
} }
$changes->addChildrenToNode($services, $this->parent); $changes->modifyNode($this->parent, [
break; 'stateOverrides' => array_merge($this->parent->getStateOverrides(), $services)
]);
}
case 'host': case 'host':
case 'process': case 'process':
if ($this->hasParentNode()) { if ($this->hasParentNode()) {

View file

@ -156,7 +156,6 @@ class EditNodeForm extends QuickForm
if ($this->hasParentNode()) { if ($this->hasParentNode()) {
$this->addElement('hidden', 'node_type', [ $this->addElement('hidden', 'node_type', [
'disabled' => true, 'disabled' => true,
'ignore' => true,
'decorators' => ['ViewHelper'], 'decorators' => ['ViewHelper'],
'value' => $monitoredNodeType 'value' => $monitoredNodeType
]); ]);
@ -192,7 +191,7 @@ class EditNodeForm extends QuickForm
if ($this->getSentValue('hosts') === null) { if ($this->getSentValue('hosts') === null) {
$this->addServicesElement($this->host); $this->addServicesElement($this->host);
$this->addServiceOverrideCheckbox(); $this->addServiceOverrideCheckbox();
if (! empty($this->node->getStateOverrides()) || $this->getSentValue('service_override') === '1') { if ($this->getElement('service_override')->isChecked() || $this->getSentValue('service_override') === '1') {
$this->addServiceOverrideElement(); $this->addServiceOverrideElement();
} }
} elseif ($host = $this->getSentValue('hosts')) { } elseif ($host = $this->getSentValue('hosts')) {
@ -236,7 +235,7 @@ class EditNodeForm extends QuickForm
$this->addElement('checkbox', 'service_override', [ $this->addElement('checkbox', 'service_override', [
'ignore' => true, 'ignore' => true,
'class' => 'autosubmit', 'class' => 'autosubmit',
'value' => ! empty($this->node->getStateOverrides()), 'value' => ! empty($this->parent->getStateOverrides($this->node->getName())),
'label' => $this->translate('Override Service State'), 'label' => $this->translate('Override Service State'),
'description' => $this->translate('Enable service state overrides') 'description' => $this->translate('Enable service state overrides')
]); ]);
@ -247,7 +246,7 @@ class EditNodeForm extends QuickForm
$this->addElement('stateOverrides', 'stateOverrides', [ $this->addElement('stateOverrides', 'stateOverrides', [
'required' => true, 'required' => true,
'states' => $this->enumServiceStateList(), 'states' => $this->enumServiceStateList(),
'value' => $this->node->getStateOverrides(), 'value' => $this->parent->getStateOverrides($this->node->getName()),
'label' => $this->translate('State Overrides') 'label' => $this->translate('State Overrides')
]); ]);
} }
@ -449,11 +448,18 @@ class EditNodeForm extends QuickForm
switch ($this->getValue('node_type')) { switch ($this->getValue('node_type')) {
case 'service': case 'service':
$properties = $this->getValues(); $stateOverrides = $this->getValue('stateOverrides') ?: [];
unset($properties['children']); if (! empty($stateOverrides)) {
$services = [$this->getValue('children') => $properties]; $stateOverrides = array_merge(
$changes->addChildrenToNode($services, $this->parent); $this->parent->getStateOverrides(),
break; [$this->getValue('children') => $stateOverrides]
);
} else {
$stateOverrides = $this->parent->getStateOverrides();
unset($stateOverrides[$this->getValue('children')]);
}
$changes->modifyNode($this->parent, ['stateOverrides' => $stateOverrides]);
case 'host': case 'host':
case 'process': case 'process':
$changes->addChildrenToNode($this->getValue('children'), $this->parent); $changes->addChildrenToNode($this->getValue('children'), $this->parent);

View file

@ -26,6 +26,7 @@ class BpNode extends Node
protected $missing = null; protected $missing = null;
protected $empty = null; protected $empty = null;
protected $missingChildren; protected $missingChildren;
protected $stateOverrides = [];
protected static $emptyStateSummary = array( protected static $emptyStateSummary = array(
'OK' => 0, 'OK' => 0,
@ -71,7 +72,7 @@ class BpNode extends Node
} elseif ($child->isMissing()) { } elseif ($child->isMissing()) {
$this->counters['MISSING']++; $this->counters['MISSING']++;
} else { } else {
$state = $child->getStateName(); $state = $child->getStateName($this->getChildState($child));
$this->counters[$state]++; $this->counters[$state]++;
} }
} }
@ -127,9 +128,13 @@ class BpNode extends Node
$problems = array(); $problems = array();
foreach ($this->getChildren() as $child) { foreach ($this->getChildren() as $child) {
if ($child->isProblem() if (isset($this->stateOverrides[$child->getName()])) {
|| ($child instanceof BpNode && $child->hasProblems()) $problem = $this->getChildState($child) > 0;
) { } else {
$problem = $child->isProblem() || ($child instanceof BpNode && $child->hasProblems());
}
if ($problem) {
$problems[] = $child; $problems[] = $child;
} }
} }
@ -187,7 +192,8 @@ class BpNode extends Node
if ($nodeState !== 0) { if ($nodeState !== 0) {
foreach ($this->getChildren() as $child) { foreach ($this->getChildren() as $child) {
$childState = $rootCause ? $child->getSortingState() : $child->getState(); $childState = $this->getChildState($child);
$childState = $rootCause ? $child->getSortingState($childState) : $childState;
if (($rootCause ? $this->getSortingState() : $nodeState) === $childState) { if (($rootCause ? $this->getSortingState() : $nodeState) === $childState) {
$name = $child->getName(); $name = $child->getName();
$tree[$name] = [ $tree[$name] = [
@ -327,6 +333,31 @@ class BpNode extends Node
return $this->info_command; return $this->info_command;
} }
public function setStateOverrides(array $overrides, $name = null)
{
if ($name === null) {
$this->stateOverrides = $overrides;
} else {
$this->stateOverrides[$name] = $overrides;
}
return $this;
}
public function getStateOverrides($name = null)
{
$overrides = null;
if ($name !== null) {
if (isset($this->stateOverrides[$name])) {
$overrides = $this->stateOverrides[$name];
}
} else {
$overrides = $this->stateOverrides;
}
return $overrides;
}
public function getAlias() public function getAlias()
{ {
return $this->alias ? preg_replace('~_~', ' ', $this->alias) : $this->name; return $this->alias ? preg_replace('~_~', ' ', $this->alias) : $this->name;
@ -354,6 +385,27 @@ class BpNode extends Node
return $this->state; return $this->state;
} }
/**
* Get the given child's state, possibly adjusted by override rules
*
* @param Node|string $child
* @return int
*/
public function getChildState($child)
{
if (! $child instanceof Node) {
$child = $this->getChildByName($child);
}
$childName = $child->getName();
$childState = $child->getState();
if (! isset($this->stateOverrides[$childName][$childState])) {
return $childState;
}
return $this->stateOverrides[$childName][$childState];
}
public function getHtmlId() public function getHtmlId()
{ {
return 'businessprocess-' . preg_replace('/[\r\n\t\s]/', '_', $this->getName()); return 'businessprocess-' . preg_replace('/[\r\n\t\s]/', '_', $this->getName());
@ -391,7 +443,7 @@ class BpNode extends Node
$child->setMissing(); $child->setMissing();
} }
$sort_states[] = $child->getSortingState(); $sort_states[] = $child->getSortingState($this->getChildState($child));
$lastStateChange = max($lastStateChange, $child->getLastStateChange()); $lastStateChange = max($lastStateChange, $child->getLastStateChange());
$bp->endLoopDetection($this->name); $bp->endLoopDetection($this->name);
} }

View file

@ -31,27 +31,15 @@ class NodeAddChildrenAction extends NodeAction
{ {
$node = $config->getBpNode($this->getNodeName()); $node = $config->getBpNode($this->getNodeName());
foreach ($this->children as $name => $properties) { foreach ($this->children as $name) {
if (is_int($name)) {
// TODO: This may be removed once host nodes also have properties
$name = $properties;
}
if (! $config->hasNode($name) || $config->getNode($name)->getBpConfig()->getName() !== $config->getName()) { if (! $config->hasNode($name) || $config->getNode($name)->getBpConfig()->getName() !== $config->getName()) {
if (strpos($name, ';') !== false) { if (strpos($name, ';') !== false) {
list($host, $service) = preg_split('/;/', $name, 2); list($host, $service) = preg_split('/;/', $name, 2);
if ($service === 'Hoststatus') { if ($service === 'Hoststatus') {
$child = $config->createHost($host); $config->createHost($host);
} else { } else {
$child = $config->createService($host, $service); $config->createService($host, $service);
}
if (is_array($properties) && !empty($properties)) {
foreach ($properties as $key => $value) {
$func = 'set' . ucfirst($key);
$child->$func($value);
}
} }
} elseif ($name[0] === '@' && strpos($name, ':') !== false) { } elseif ($name[0] === '@' && strpos($name, ':') !== false) {
list($configName, $nodeName) = preg_split('~:\s*~', substr($name, 1), 2); list($configName, $nodeName) = preg_split('~:\s*~', substr($name, 1), 2);

View file

@ -46,8 +46,6 @@ abstract class Node
self::NODE_EMPTY => 0 self::NODE_EMPTY => 0
); );
protected $stateOverrides = [];
/** @var string Alias of the node */ /** @var string Alias of the node */
protected $alias; protected $alias;
@ -199,18 +197,6 @@ abstract class Node
return $this; return $this;
} }
public function setStateOverrides(array $overrides)
{
$this->stateOverrides = $overrides;
return $this;
}
public function getStateOverrides()
{
return $this->stateOverrides;
}
/** /**
* Forget my state * Forget my state
* *
@ -260,30 +246,14 @@ abstract class Node
); );
} }
if (isset($this->stateOverrides[$this->state])) {
return $this->stateOverrides[$this->state];
} else {
return $this->state;
}
}
public function getRealState()
{
if ($this->state === null) {
throw new ProgrammingError(
sprintf(
'Node %s is unable to retrieve it\'s state',
$this->name
)
);
}
return $this->state; return $this->state;
} }
public function getSortingState() public function getSortingState($state = null)
{ {
if ($state === null) {
$state = $this->getState(); $state = $this->getState();
}
if (self::$ackIsOk && $this->isAcknowledged()) { if (self::$ackIsOk && $this->isAcknowledged()) {
$state = self::ICINGA_OK; $state = self::ICINGA_OK;

View file

@ -180,9 +180,9 @@ abstract class Renderer extends HtmlDocument
if ($node->isEmpty() && ! $node instanceof MonitoredNode) { if ($node->isEmpty() && ! $node instanceof MonitoredNode) {
$classes = array('empty'); $classes = array('empty');
} else { } else {
$classes = array( $classes = [strtolower($node->getStateName(
strtolower($node->getStateName()) $this->parent !== null ? $this->parent->getChildState($node) : null
); ))];
} }
if ($node->hasMissingChildren()) { if ($node->hasMissingChildren()) {
$classes[] = 'missing-children'; $classes[] = 'missing-children';

View file

@ -113,12 +113,14 @@ class NodeTile extends BaseHtmlElement
)); ));
} }
if ($node instanceof ServiceNode && $node->getRealState() !== $node->getState()) { if ($this->renderer->rendersSubNode()
$this->add((new StateBall(strtolower($node->getStateName($node->getRealState()))))->addAttributes([ && $this->renderer->getParentNode()->getChildState($node) !== $node->getState()
) {
$this->add((new StateBall(strtolower($node->getStateName())))->addAttributes([
'class' => 'overridden-state', 'class' => 'overridden-state',
'title' => sprintf( 'title' => sprintf(
'%s', '%s',
$node->getStateName($node->getRealState()) $node->getStateName()
) )
])); ]));
} }

View file

@ -79,7 +79,7 @@ class TreeRenderer extends Renderer
if ($node instanceof BpNode) { if ($node instanceof BpNode) {
$html[] = $this->renderNode($bp, $node); $html[] = $this->renderNode($bp, $node);
} else { } else {
$html[] = $this->renderChild($bp, $node); $html[] = $this->renderChild($bp, $this->parent, $node);
} }
} }
@ -106,9 +106,10 @@ class TreeRenderer extends Renderer
/** /**
* @param Node $node * @param Node $node
* @param array $path * @param array $path
* @param BpNode $parent
* @return BaseHtmlElement[] * @return BaseHtmlElement[]
*/ */
public function getNodeIcons(Node $node, array $path = null) public function getNodeIcons(Node $node, array $path = null, BpNode $parent = null)
{ {
$icons = []; $icons = [];
if (empty($path) && $node instanceof BpNode) { if (empty($path) && $node instanceof BpNode) {
@ -116,7 +117,7 @@ class TreeRenderer extends Renderer
} else { } else {
$icons[] = $node->getIcon(); $icons[] = $node->getIcon();
} }
$state = strtolower($node->getStateName()); $state = strtolower($node->getStateName($parent !== null ? $parent->getChildState($node) : null));
if ($node->isHandled()) { if ($node->isHandled()) {
$state = $state . '-handled'; $state = $state . '-handled';
} }
@ -136,20 +137,20 @@ class TreeRenderer extends Renderer
return $icons; return $icons;
} }
public function getOverriddenState(Node $node) public function getOverriddenState($fakeState, Node $node)
{ {
$overriddenState = Html::tag('div', ['class' => 'overridden-state']); $overriddenState = Html::tag('div', ['class' => 'overridden-state']);
$overriddenState->add((new StateBall(strtolower($node->getStateName($node->getRealState()))))->addAttributes([
'title' => sprintf(
'%s',
$node->getStateName($node->getRealState())
)
]));
$overriddenState->add(Html::tag('i', ['class' => 'icon icon-right-small']));
$overriddenState->add((new StateBall(strtolower($node->getStateName())))->addAttributes([ $overriddenState->add((new StateBall(strtolower($node->getStateName())))->addAttributes([
'title' => sprintf( 'title' => sprintf(
'%s', '%s',
$node->getStateName() $node->getStateName()
)
]));
$overriddenState->add(Html::tag('i', ['class' => 'icon icon-right-small']));
$overriddenState->add((new StateBall(strtolower($node->getStateName($fakeState))))->addAttributes([
'title' => sprintf(
'%s',
$node->getStateName($fakeState)
), ),
'class' => 'last' 'class' => 'last'
])); ]));
@ -243,14 +244,14 @@ class TreeRenderer extends Renderer
if ($child instanceof BpNode) { if ($child instanceof BpNode) {
$ul->add($this->renderNode($bp, $child, $path)); $ul->add($this->renderNode($bp, $child, $path));
} else { } else {
$ul->add($this->renderChild($bp, $child, $path)); $ul->add($this->renderChild($bp, $node, $child, $path));
} }
} }
return $li; return $li;
} }
protected function renderChild($bp, Node $node, $path = null) protected function renderChild($bp, BpNode $parent, Node $node, $path = null)
{ {
$li = Html::tag('li', [ $li = Html::tag('li', [
'class' => 'movable', 'class' => 'movable',
@ -258,14 +259,14 @@ class TreeRenderer extends Renderer
'data-node-name' => $node->getName() 'data-node-name' => $node->getName()
]); ]);
$li->add($this->getNodeIcons($node, $path)); $li->add($this->getNodeIcons($node, $path, $parent));
$link = $node->getLink(); $link = $node->getLink();
$link->getAttributes()->set('data-base-target', '_next'); $link->getAttributes()->set('data-base-target', '_next');
$li->add($link); $li->add($link);
if ($node->getRealState() !== $node->getState()) { if (($overriddenState = $parent->getChildState($node)) !== $node->getState()) {
$li->add($this->getOverriddenState($node)); $li->add($this->getOverriddenState($overriddenState, $node));
} }
if (! $this->isLocked() && $node->getBpConfig()->getName() === $this->getBusinessProcess()->getName()) { if (! $this->isLocked() && $node->getBpConfig()->getName() === $this->getBusinessProcess()->getName()) {

View file

@ -247,7 +247,7 @@ class LegacyConfigParser
$stateOverrides[(int) $from] = (int) $to; $stateOverrides[(int) $from] = (int) $to;
} }
$node->getChildByName($childName)->setStateOverrides($stateOverrides); $node->setStateOverrides($stateOverrides, $childName);
} }
} }

View file

@ -218,14 +218,14 @@ class LegacyConfigRenderer
public static function renderStateOverrides(BpNode $node) public static function renderStateOverrides(BpNode $node)
{ {
$stateOverrides = ''; $stateOverrides = '';
foreach ($node->getChildren() as $child) { foreach ($node->getStateOverrides() as $childName => $overrideRules) {
$overrides = []; $overrides = [];
foreach ($child->getStateOverrides() as $from => $to) { foreach ($overrideRules as $from => $to) {
$overrides[] = sprintf('%d-%d', $from, $to); $overrides[] = sprintf('%d-%d', $from, $to);
} }
if (! empty($overrides)) { if (! empty($overrides)) {
$stateOverrides .= '!' . $child->getName() . '|' . join(',', $overrides); $stateOverrides .= '!' . $childName . '|' . join(',', $overrides);
} }
} }

View file

@ -41,11 +41,14 @@ class StateOverrides extends FormElement
public function setValue($value) public function setValue($value)
{ {
$cleanedValue = []; $cleanedValue = [];
if (! empty($value)) {
foreach ($value as $from => $to) { foreach ($value as $from => $to) {
if ((int) $from !== (int) $to) { if ((int) $from !== (int) $to) {
$cleanedValue[$from] = $to; $cleanedValue[$from] = $to;
} }
} }
}
return parent::setValue($cleanedValue); return parent::setValue($cleanedValue);
} }