config: Use an extra line to store state overrides

Storing overrides as part of a node's name leads to way too complicated
code. A separate field is not only better for compatibility but also
more straightforward to process.
This commit is contained in:
Johannes Meyer 2020-06-26 11:40:08 +02:00
parent 98b6b2c6d0
commit 0c7fca926f
15 changed files with 239 additions and 248 deletions

View file

@ -210,7 +210,7 @@ class AddNodeForm extends QuickForm
$this->addServiceOverrideCheckbox();
if ($this->getSentValue('service_override') === '1') {
$this->addServiceOverrideElements();
$this->addServiceOverrideElement();
}
} else {
$this->setSubmitLabel($this->translate('Next'));
@ -268,23 +268,12 @@ class AddNodeForm extends QuickForm
]);
}
protected function addServiceOverrideElements()
protected function addServiceOverrideElement()
{
$elements = [];
foreach ($this->enumServiceStateList() as $state => $stateName) {
if ($state === 0) {
continue;
}
$this->addElement('select', $stateName, [
'label' => $this->translate($stateName),
'ignore' => true,
'multiOptions' => $this->optionalEnum($this->enumServiceStateList()),
]);
$elements[] = $stateName;
}
$this->addSimpleDisplayGroup($elements, 'override_group', [
'legend' => $this->translate('State Overrides')
$this->addElement('stateOverrides', 'stateOverrides', [
'required' => true,
'label' => $this->translate('State Overrides'),
'states' => $this->enumServiceStateList()
]);
}
@ -439,21 +428,6 @@ class AddNodeForm extends QuickForm
return $serviceStateList;
}
protected function statesToString()
{
$stateString = null;
foreach ($this->enumServiceStateList() as $state => $stateName) {
if ($this->getValue($stateName) !== null && $this->getValue($stateName) !== '') {
if ($stateString !== null) {
$stateString .= ',';
}
$stateString .= $state . '-' . $this->getValue($stateName);
}
}
return $stateString;
}
protected function hasProcesses()
{
return count($this->enumProcesses()) > 0;
@ -528,15 +502,15 @@ class AddNodeForm extends QuickForm
$changes = ProcessChanges::construct($this->bp, $this->session);
switch ($this->getValue('node_type')) {
case 'service':
if ($this->statesToString() !== null) {
$services = [];
foreach ($this->getValue('children') as $service) {
$services[] = $service . ':' . $this->statesToString();
}
$changes->addChildrenToNode($services, $this->parent);
} else {
$changes->addChildrenToNode($this->getValue('children'), $this->parent);
$properties = $this->getValues();
unset($properties['children']);
$services = [];
foreach ($this->getValue('children') as $service) {
$services[$service] = $properties;
}
$changes->addChildrenToNode($services, $this->parent);
break;
case 'host':
case 'process':

View file

@ -36,8 +36,6 @@ class EditNodeForm extends QuickForm
protected $host;
protected $statesOverride = [];
/** @var SessionNamespace */
protected $session;
@ -45,8 +43,7 @@ class EditNodeForm extends QuickForm
{
$this->host = substr($this->getNode()->getName(), 0, strpos($this->getNode()->getName(), ';'));
if ($this->isService()) {
$this->service = $this->getNode()->getShortName();
$this->statesOverride = $this->getNode()->getStatesOverride();
$this->service = substr($this->getNode()->getName(), strpos($this->getNode()->getName(), ';') + 1);
}
$view = $this->getView();
@ -159,6 +156,7 @@ class EditNodeForm extends QuickForm
if ($this->hasParentNode()) {
$this->addElement('hidden', 'node_type', [
'disabled' => true,
'ignore' => true,
'decorators' => ['ViewHelper'],
'value' => $monitoredNodeType
]);
@ -194,14 +192,14 @@ class EditNodeForm extends QuickForm
if ($this->getSentValue('hosts') === null) {
$this->addServicesElement($this->host);
$this->addServiceOverrideCheckbox();
if (!empty($this->statesOverride) || $this->getSentValue('service_override') === '1') {
$this->addServiceOverrideElements();
if (! empty($this->node->getStateOverrides()) || $this->getSentValue('service_override') === '1') {
$this->addServiceOverrideElement();
}
} elseif ($host = $this->getSentValue('hosts')) {
$this->addServicesElement($host);
$this->addServiceOverrideCheckbox();
if ($this->getSentValue('service_override') === '1') {
$this->addServiceOverrideElements();
$this->addServiceOverrideElement();
}
} else {
$this->setSubmitLabel($this->translate('Next'));
@ -225,23 +223,11 @@ class EditNodeForm extends QuickForm
{
$this->addElement('select', 'children', array(
'required' => true,
'value' => $this->service,
'value' => $this->getNode()->getName(),
'multiOptions' => $this->enumServiceList($host),
'label' => $this->translate('Service'),
'description' => $this->translate('The service for this business process node'),
'validators' => [
['Callback', true, [
'callback' => function ($value) {
if ($this->node->getShortName() === $value) {
return true;
}
return ! $this->parent->hasMatchingChild($value);
},
'messages' => [
'callbackValue' => $this->translate('%value% is already defined in this process')
]
]]
]
'validators' => [[new NoDuplicateChildrenValidator($this, $this->bp, $this->parent), true]]
));
}
@ -250,30 +236,19 @@ class EditNodeForm extends QuickForm
$this->addElement('checkbox', 'service_override', [
'ignore' => true,
'class' => 'autosubmit',
'value' => ! empty($this->statesOverride) ? '1' : null,
'value' => ! empty($this->node->getStateOverrides()),
'label' => $this->translate('Override Service State'),
'description' => $this->translate('Enable service state overrides')
]);
}
protected function addServiceOverrideElements()
protected function addServiceOverrideElement()
{
$elements = [];
foreach ($this->enumServiceStateList() as $state => $stateName) {
if ($state == 0) {
continue;
}
$this->addElement('select', $stateName, [
'label' => $this->translate($stateName),
'value' => isset($this->statesOverride[$state]) ? $this->statesOverride[$state] : null,
'ignore' => true,
'multiOptions' => $this->optionalEnum($this->enumServiceStateList()),
]);
$elements[] = $stateName;
}
$this->addSimpleDisplayGroup($elements, 'override_group', [
'legend' => $this->translate('State Overrides')
$this->addElement('stateOverrides', 'stateOverrides', [
'required' => true,
'states' => $this->enumServiceStateList(),
'value' => $this->node->getStateOverrides(),
'label' => $this->translate('State Overrides')
]);
}
@ -409,21 +384,6 @@ class EditNodeForm extends QuickForm
return $serviceStateList;
}
protected function statesToString()
{
$stateString = null;
foreach ($this->enumServiceStateList() as $state => $stateName) {
if ($this->getValue($stateName) !== null && $this->getValue($stateName) !== '') {
if ($stateString !== null) {
$stateString .= ',';
}
$stateString .= $state . '-' . $this->getValue($stateName);
}
}
return $stateString;
}
protected function hasProcesses()
{
return count($this->enumProcesses()) > 0;
@ -489,14 +449,10 @@ class EditNodeForm extends QuickForm
switch ($this->getValue('node_type')) {
case 'service':
if ($this->statesToString() !== null) {
$changes->addChildrenToNode(
$this->getValue('children') . ':' . $this->statesToString(),
$this->parent
);
} else {
$changes->addChildrenToNode($this->getValue('children'), $this->parent);
}
$properties = $this->getValues();
unset($properties['children']);
$services = [$this->getValue('children') => $properties];
$changes->addChildrenToNode($services, $this->parent);
break;
case 'host':
case 'process':

View file

@ -0,0 +1,40 @@
<?php
// Avoid complaints about missing namespace and invalid class name
// @codingStandardsIgnoreStart
class Zend_View_Helper_FormStateOverrides extends Zend_View_Helper_FormElement
{
// @codingStandardsIgnoreEnd
public function formStateOverrides($name, $value = null, $attribs = null)
{
$states = $attribs['states'];
unset($attribs['states']);
$attribs['multiple'] = '';
$html = '';
foreach ($states as $state => $label) {
if ($state === 0) {
continue;
}
$chosen = $state;
if (isset($value[$state])) {
$chosen = $value[$state];
}
$options = [$state => t('Keep actual state')] + $states;
$html .= '<label><span>' . $this->view->escape($label) . '</span>';
$html .= $this->view->formSelect(
sprintf('%s[%d]', substr($name, 0, -2), $state),
$chosen,
$attribs,
$options
);
$html .= '</label>';
}
return $html;
}
}

View file

@ -460,21 +460,16 @@ class BpConfig
return array_key_exists($name, $this->root_nodes);
}
public function createService($host, $service, $statesOverride = null)
public function createService($host, $service)
{
$node = new ServiceNode(
(object) array(
'hostname' => $host,
'service' => $service,
'statesOverride'=> $statesOverride
'service' => $service
)
);
$node->setBpConfig($this);
if (isset($statesOverride)) {
$this->nodes[$host . ';' . $service . ':' . $statesOverride] = $node;
} else {
$this->nodes[$host . ';' . $service] = $node;
}
$this->nodes[$host . ';' . $service] = $node;
$this->hosts[$host] = true;
return $node;
}
@ -662,19 +657,6 @@ class BpConfig
);
}
public function getMatchingNodeNams($name)
{
$matching = [];
foreach ($this->nodes as $nodeName => $node) {
if ($name === $node->getShortName()) {
$matching[] = $nodeName;
}
}
return $matching;
}
/**
* @return BpNode
*/

View file

@ -142,11 +142,6 @@ class BpNode extends Node
return in_array($name, $this->getChildNames());
}
public function hasMatchingChild($name)
{
return in_array($name, $this->getChildShortNames());
}
public function removeChild($name)
{
if (($key = array_search($name, $this->getChildNames())) !== false) {
@ -503,20 +498,6 @@ class BpNode extends Node
return $this->children;
}
protected function getChildShortNames()
{
$ChidrenShortNames = array();
foreach ($this->getChildren() as $child) {
$name = $child->getShortName();
array_push($ChidrenShortNames, $name);
}
return $ChidrenShortNames;
}
/**
* Reorder this node's children, in case manual order is not applied
*/

View file

@ -31,19 +31,26 @@ class NodeAddChildrenAction extends NodeAction
{
$node = $config->getBpNode($this->getNodeName());
foreach ($this->children as $name) {
foreach ($this->children as $name => $properties) {
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 (strpos($name, ';') !== false) {
if (strpos($name, ':') !== false) {
list($host, $service, $statesOverride) = preg_split('~[;:]~', $name, 3);
$config->createService($host, $service, $statesOverride);
} else {
list($host, $service) = preg_split('/;/', $name, 2);
list($host, $service) = preg_split('/;/', $name, 2);
if ($service === 'Hoststatus') {
$config->createHost($host);
} else {
$config->createService($host, $service);
if ($service === 'Hoststatus') {
$child = $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);
}
}
} elseif ($name[0] === '@' && strpos($name, ':') !== false) {

View file

@ -46,7 +46,7 @@ abstract class Node
self::NODE_EMPTY => 0
);
protected $stateOverride = [];
protected $stateOverrides = [];
/** @var string Alias of the node */
protected $alias;
@ -199,26 +199,16 @@ abstract class Node
return $this;
}
protected function setStateOverride($state, $oveRrideState)
public function setStateOverrides(array $overrides)
{
$this->stateOverride[(int) $state] = (int) $oveRrideState;
$this->stateOverrides = $overrides;
return $this;
}
public function setStatesOverride($statesOverrideString)
public function getStateOverrides()
{
$overrides = explode(',', $statesOverrideString);
foreach ($overrides as $overrideState) {
if (strpos($overrideState, '-') !== false) {
list($key, $value) = explode('-', $overrideState);
$this->setStateOverride($key, $value);
}
}
}
public function getStatesOverride()
{
return $this->stateOverride;
return $this->stateOverrides;
}
/**
@ -270,8 +260,8 @@ abstract class Node
);
}
if (isset($this->stateOverride[$this->state])) {
return $this->stateOverride[$this->state];
if (isset($this->stateOverrides[$this->state])) {
return $this->stateOverrides[$this->state];
} else {
return $this->state;
}
@ -406,11 +396,6 @@ abstract class Node
return $this;
}
public function getShortName()
{
return $this->name;
}
public function hasParents()
{
return count($this->parents) > 0;

View file

@ -154,7 +154,7 @@ class TreeRenderer extends Renderer
{
$statesOverrights = [];
$states = $node->getStatesOverride();
$states = $node->getStateOverrides();
foreach ($states as $originalState => $overrideState) {
$statesOverrights[] = (new StateBall(strtolower($node->getStateName($originalState))))->addAttributes([
'title' => sprintf(
@ -290,7 +290,7 @@ class TreeRenderer extends Renderer
'class' => 'left-side',
]);
if (!empty($node->getStatesOverride())) {
if (! empty($node->getStateOverrides())) {
$leftAlign->add($this->getNodeStateOverride($node));
}

View file

@ -19,12 +19,7 @@ class ServiceNode extends MonitoredNode
public function __construct($object)
{
if (isset($object->statesOverride)) {
$this->name = $object->hostname . ';' . $object->service . ':' . $object->statesOverride;
$this->setStatesOverride($object->statesOverride);
} else {
$this->name = $object->hostname . ';' . $object->service;
}
$this->name = $object->hostname . ';' . $object->service;
$this->hostname = $object->hostname;
$this->service = $object->service;
if (isset($object->state)) {
@ -73,11 +68,6 @@ class ServiceNode extends MonitoredNode
return $this->getHostAlias() . ': ' . $this->alias;
}
public function getShortName()
{
return $this->hostname . ';' . $this->service;
}
public function getUrl()
{
$params = array(

View file

@ -122,27 +122,24 @@ class MonitoringState
$key .= ';Hoststatus';
}
$nodesNames = $config->getMatchingNodeNams($key);
foreach ($nodesNames as $nodeName) {
// We fetch more states than we need, so skip unknown ones
if (! $config->hasNode($nodeName)) {
return;
}
// We fetch more states than we need, so skip unknown ones
if (! $config->hasNode($key)) {
return;
}
$node = $config->getNode($nodeName);
$node = $config->getNode($key);
if ($row->state !== null) {
$node->setState($row->state)->setMissing(false);
}
if ($row->last_state_change !== null) {
$node->setLastStateChange($row->last_state_change);
}
if ((int) $row->in_downtime === 1) {
$node->setDowntime(true);
}
if ((int) $row->ack === 1) {
$node->setAck(true);
}
if ($row->state !== null) {
$node->setState($row->state)->setMissing(false);
}
if ($row->last_state_change !== null) {
$node->setLastStateChange($row->last_state_change);
}
if ((int) $row->in_downtime === 1) {
$node->setDowntime(true);
}
if ((int) $row->ack === 1) {
$node->setAck(true);
}
$node->setAlias($row->display_name);

View file

@ -233,6 +233,24 @@ class LegacyConfigParser
$bp->getBpNode($name)->setInfoUrl($url);
}
protected function parseStateOverrides(&$line, BpConfig $bp)
{
// state_overrides <bp-node>!<child>|n-n[,n-n]!<child>|n-n[,n-n]
$segments = preg_split('~\s*!\s*~', substr($line, 16));
$node = $bp->getNode(array_shift($segments));
foreach ($segments as $overrideDef) {
list($childName, $overrides) = preg_split('~\s*\|\s*~', $overrideDef, 2);
$stateOverrides = [];
foreach (preg_split('~\s*,\s*~', $overrides) as $override) {
list($from, $to) = preg_split('~\s*-\s*~', $override, 2);
$stateOverrides[(int) $from] = (int) $to;
}
$node->getChildByName($childName)->setStateOverrides($stateOverrides);
}
}
protected function parseExtraLine(&$line, $typeLength, BpConfig $bp)
{
$type = substr($line, 0, $typeLength);
@ -251,6 +269,9 @@ class LegacyConfigParser
case 'info_url':
$this->parseInfoUrl($line, $bp);
break;
case 'state_overrides':
$this->parseStateOverrides($line, $bp);
break;
case 'template':
// compat, ignoring for now
break;
@ -282,9 +303,9 @@ class LegacyConfigParser
return;
}
// Space found in the first 14 cols? Might be a line with extra information
// Space found in the first 16 cols? Might be a line with extra information
$pos = strpos($line, ' ');
if ($pos !== false && $pos < 14) {
if ($pos !== false && $pos < 16) {
if ($this->parseExtraLine($line, $pos, $bp)) {
return;
}
@ -335,17 +356,11 @@ class LegacyConfigParser
if ($bp->hasNode($val)) {
$node->addChild($bp->getNode($val));
} else {
if (strpos($val, ':') !== false) {
list($host, $service, $statesOverride) = preg_split('~[;:]~', $val, 3);
$node->addChild($bp->createService($host, $service, $statesOverride));
list($host, $service) = preg_split('~;~', $val, 2);
if ($service === 'Hoststatus') {
$node->addChild($bp->createHost($host));
} else {
list($host, $service) = preg_split('~;~', $val, 2);
if ($service === 'Hoststatus') {
$node->addChild($bp->createHost($host));
} else {
$node->addChild($bp->createService($host, $service));
}
$node->addChild($bp->createService($host, $service));
}
}
} elseif ($val[0] === '@') {

View file

@ -158,6 +158,7 @@ class LegacyConfigRenderer
public static function renderSingleBpNode(BpNode $node)
{
return static::renderExpression($node)
. static::renderStateOverrides($node)
. static::renderDisplay($node)
. static::renderInfoUrl($node);
}
@ -214,6 +215,27 @@ class LegacyConfigRenderer
}
}
public static function renderStateOverrides(BpNode $node)
{
$stateOverrides = '';
foreach ($node->getChildren() as $child) {
$overrides = [];
foreach ($child->getStateOverrides() as $from => $to) {
$overrides[] = sprintf('%d-%d', $from, $to);
}
if (! empty($overrides)) {
$stateOverrides .= '!' . $child->getName() . '|' . join(',', $overrides);
}
}
if (! $stateOverrides) {
return '';
}
return 'state_overrides ' . $node->getName() . $stateOverrides . "\n";
}
/**
* @param BpNode $node
* @return string

View file

@ -0,0 +1,52 @@
<?php
namespace Icinga\Module\Businessprocess\Web\Form\Element;
class StateOverrides extends FormElement
{
public $helper = 'formStateOverrides';
/** @var array The overridable states */
protected $states;
/**
* Set the overridable states
*
* @param array $states
*
* @return $this
*/
public function setStates(array $states)
{
$this->states = $states;
return $this;
}
/**
* Get the overridable states
*
* @return array
*/
public function getStates()
{
return $this->states;
}
public function init()
{
$this->setIsArray(true);
}
public function setValue($value)
{
$cleanedValue = [];
foreach ($value as $from => $to) {
if ((int) $from !== (int) $to) {
$cleanedValue[$from] = $to;
}
}
return parent::setValue($cleanedValue);
}
}

View file

@ -43,7 +43,7 @@ class NoDuplicateChildrenValidator extends Zend_Validate_Abstract
} elseif ($this->form instanceof EditNodeForm && $this->form->getNode()->getName() === $value) {
$found = false;
} else {
$found = $this->parent->hasMatchingChild($value);
$found = $this->parent->hasChild($value);
}
if (! $found) {

View file

@ -962,37 +962,27 @@ form dt label {
}
}
fieldset.collapsed {
dd, dt, ul, div {
display: none;
}
}
#stateOverrides-element {
display: inline-table;
table-layout: fixed;
border-spacing: .5em;
padding: 0;
fieldset {
margin-top: 15px;
padding: 0 0 1.5em 0;
border: none;
label {
display: table-row;
legend {
margin: 0em 0 0.5em 0;
font-size: 1em;
box-shadow: 0 3px 4px -4px rgba(0,0,0,0.2);
font-weight: bold;
width: 100%;
padding-left: 0.5em;
line-height: 2em;
user-select: none;
&:hover {
border-color: @text-color;
}
}
dd, dt, ul, div {
display: none;
span, select {
display: table-cell;
}
margin-bottom: 0.2em;
padding-bottom: 0;
span {
width: 10em;
}
select {
width: 26em;
}
}
}
form fieldset {