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 = '';
foreach ($tree as $name => $subtree) {
/** @var Node $node */
$node = $subtree['node'];
$state = $parent !== null ? $parent->getChildState($node) : $node->getState();
if ($node instanceof HostNode) {
$colors = $this->hostColors[$node->getState()];
$colors = $this->hostColors[$state];
} else {
$colors = $this->serviceColors[$node->getState()];
$colors = $this->serviceColors[$state];
}
$state = sprintf('[%s]', $node->getStateName());
$state = sprintf('[%s]', $node->getStateName($state));
if ($useColors) {
$state = $this->screen->colorize($state, $colors[0], $colors[1]);
}
@ -193,7 +194,10 @@ class ProcessCommand extends Command
$state,
$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;

View file

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

View file

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

View file

@ -26,6 +26,7 @@ class BpNode extends Node
protected $missing = null;
protected $empty = null;
protected $missingChildren;
protected $stateOverrides = [];
protected static $emptyStateSummary = array(
'OK' => 0,
@ -71,7 +72,7 @@ class BpNode extends Node
} elseif ($child->isMissing()) {
$this->counters['MISSING']++;
} else {
$state = $child->getStateName();
$state = $child->getStateName($this->getChildState($child));
$this->counters[$state]++;
}
}
@ -127,9 +128,13 @@ class BpNode extends Node
$problems = array();
foreach ($this->getChildren() as $child) {
if ($child->isProblem()
|| ($child instanceof BpNode && $child->hasProblems())
) {
if (isset($this->stateOverrides[$child->getName()])) {
$problem = $this->getChildState($child) > 0;
} else {
$problem = $child->isProblem() || ($child instanceof BpNode && $child->hasProblems());
}
if ($problem) {
$problems[] = $child;
}
}
@ -187,7 +192,8 @@ class BpNode extends Node
if ($nodeState !== 0) {
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) {
$name = $child->getName();
$tree[$name] = [
@ -327,6 +333,31 @@ class BpNode extends Node
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()
{
return $this->alias ? preg_replace('~_~', ' ', $this->alias) : $this->name;
@ -354,6 +385,27 @@ class BpNode extends Node
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()
{
return 'businessprocess-' . preg_replace('/[\r\n\t\s]/', '_', $this->getName());
@ -391,7 +443,7 @@ class BpNode extends Node
$child->setMissing();
}
$sort_states[] = $child->getSortingState();
$sort_states[] = $child->getSortingState($this->getChildState($child));
$lastStateChange = max($lastStateChange, $child->getLastStateChange());
$bp->endLoopDetection($this->name);
}

View file

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

View file

@ -46,8 +46,6 @@ abstract class Node
self::NODE_EMPTY => 0
);
protected $stateOverrides = [];
/** @var string Alias of the node */
protected $alias;
@ -199,18 +197,6 @@ abstract class Node
return $this;
}
public function setStateOverrides(array $overrides)
{
$this->stateOverrides = $overrides;
return $this;
}
public function getStateOverrides()
{
return $this->stateOverrides;
}
/**
* 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;
}
public function getSortingState()
public function getSortingState($state = null)
{
if ($state === null) {
$state = $this->getState();
}
if (self::$ackIsOk && $this->isAcknowledged()) {
$state = self::ICINGA_OK;

View file

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

View file

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

View file

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

View file

@ -247,7 +247,7 @@ class LegacyConfigParser
$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)
{
$stateOverrides = '';
foreach ($node->getChildren() as $child) {
foreach ($node->getStateOverrides() as $childName => $overrideRules) {
$overrides = [];
foreach ($child->getStateOverrides() as $from => $to) {
foreach ($overrideRules as $from => $to) {
$overrides[] = sprintf('%d-%d', $from, $to);
}
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)
{
$cleanedValue = [];
if (! empty($value)) {
foreach ($value as $from => $to) {
if ((int) $from !== (int) $to) {
$cleanedValue[$from] = $to;
}
}
}
return parent::setValue($cleanedValue);
}