Merge pull request #379 from Icinga/add-new-operator-XOR
Introduce `XOR (^)` operator
|
|
@ -7,6 +7,7 @@ use Icinga\Module\Businessprocess\BpConfig;
|
|||
use Icinga\Module\Businessprocess\Common\EnumList;
|
||||
use Icinga\Module\Businessprocess\ImportedNode;
|
||||
use Icinga\Module\Businessprocess\Modification\ProcessChanges;
|
||||
use Icinga\Module\Businessprocess\Node;
|
||||
use Icinga\Module\Businessprocess\Storage\Storage;
|
||||
use Icinga\Module\Businessprocess\Web\Form\QuickForm;
|
||||
use Icinga\Module\Businessprocess\Web\Form\Validator\NoDuplicateChildrenValidator;
|
||||
|
|
@ -113,21 +114,7 @@ class AddNodeForm extends QuickForm
|
|||
$this->addElement('select', 'operator', array(
|
||||
'label' => $this->translate('Operator'),
|
||||
'required' => true,
|
||||
'multiOptions' => array(
|
||||
'&' => $this->translate('AND'),
|
||||
'|' => $this->translate('OR'),
|
||||
'!' => $this->translate('NOT'),
|
||||
'%' => $this->translate('DEGRADED'),
|
||||
'1' => $this->translate('MIN 1'),
|
||||
'2' => $this->translate('MIN 2'),
|
||||
'3' => $this->translate('MIN 3'),
|
||||
'4' => $this->translate('MIN 4'),
|
||||
'5' => $this->translate('MIN 5'),
|
||||
'6' => $this->translate('MIN 6'),
|
||||
'7' => $this->translate('MIN 7'),
|
||||
'8' => $this->translate('MIN 8'),
|
||||
'9' => $this->translate('MIN 9'),
|
||||
)
|
||||
'multiOptions' => Node::getOperators()
|
||||
));
|
||||
|
||||
$display = 1;
|
||||
|
|
|
|||
|
|
@ -111,21 +111,7 @@ class EditNodeForm extends QuickForm
|
|||
$this->addElement('select', 'operator', array(
|
||||
'label' => $this->translate('Operator'),
|
||||
'required' => true,
|
||||
'multiOptions' => array(
|
||||
'&' => $this->translate('AND'),
|
||||
'|' => $this->translate('OR'),
|
||||
'!' => $this->translate('NOT'),
|
||||
'%' => $this->translate('DEGRADED'),
|
||||
'1' => $this->translate('MIN 1'),
|
||||
'2' => $this->translate('MIN 2'),
|
||||
'3' => $this->translate('MIN 3'),
|
||||
'4' => $this->translate('MIN 4'),
|
||||
'5' => $this->translate('MIN 5'),
|
||||
'6' => $this->translate('MIN 6'),
|
||||
'7' => $this->translate('MIN 7'),
|
||||
'8' => $this->translate('MIN 8'),
|
||||
'9' => $this->translate('MIN 9'),
|
||||
)
|
||||
'multiOptions' => Node::getOperators()
|
||||
));
|
||||
|
||||
$display = $this->getNode()->getDisplay() ?: 1;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace Icinga\Module\Businessprocess\Forms;
|
|||
use Icinga\Module\Businessprocess\BpNode;
|
||||
use Icinga\Module\Businessprocess\BpConfig;
|
||||
use Icinga\Module\Businessprocess\Modification\ProcessChanges;
|
||||
use Icinga\Module\Businessprocess\Node;
|
||||
use Icinga\Module\Businessprocess\Web\Form\QuickForm;
|
||||
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
|
||||
use Icinga\Web\Notification;
|
||||
|
|
@ -58,21 +59,7 @@ class ProcessForm extends QuickForm
|
|||
$this->addElement('select', 'operator', array(
|
||||
'label' => $this->translate('Operator'),
|
||||
'required' => true,
|
||||
'multiOptions' => array(
|
||||
'&' => $this->translate('AND'),
|
||||
'|' => $this->translate('OR'),
|
||||
'!' => $this->translate('NOT'),
|
||||
'%' => $this->translate('DEGRADED'),
|
||||
'1' => $this->translate('MIN 1'),
|
||||
'2' => $this->translate('MIN 2'),
|
||||
'3' => $this->translate('MIN 3'),
|
||||
'4' => $this->translate('MIN 4'),
|
||||
'5' => $this->translate('MIN 5'),
|
||||
'6' => $this->translate('MIN 6'),
|
||||
'7' => $this->translate('MIN 7'),
|
||||
'8' => $this->translate('MIN 8'),
|
||||
'9' => $this->translate('MIN 9'),
|
||||
)
|
||||
'multiOptions' => Node::getOperators()
|
||||
));
|
||||
|
||||
if ($this->node !== null) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,16 @@ The `OR` operator selects the **BEST** state of its child nodes:
|
|||
|
||||

|
||||
|
||||
## XOR <a id="xor-operator">
|
||||
|
||||
The `XOR` operator shows OK if only one of n children is OK at the same time. In all other cases the parent node is CRITICAL.
|
||||
Useful for a service on n servers, only one of which may be running. If both were running,
|
||||
race conditions and duplication of data could occur.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## DEGRADED <a id="deg-operator">
|
||||
|
||||
The `DEGRADED` operator behaves like an `AND`, but if the resulting
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 30 KiB |
BIN
doc/screenshot/09_operators/0906_xor-operator.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
doc/screenshot/09_operators/0907_xor-operator-not-ok.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
|
|
@ -10,6 +10,7 @@ class BpNode extends Node
|
|||
{
|
||||
const OP_AND = '&';
|
||||
const OP_OR = '|';
|
||||
const OP_XOR = '^';
|
||||
const OP_NOT = '!';
|
||||
const OP_DEGRADED = '%';
|
||||
|
||||
|
|
@ -303,6 +304,7 @@ class BpNode extends Node
|
|||
switch ($operator) {
|
||||
case self::OP_AND:
|
||||
case self::OP_OR:
|
||||
case self::OP_XOR:
|
||||
case self::OP_NOT:
|
||||
case self::OP_DEGRADED:
|
||||
return;
|
||||
|
|
@ -476,6 +478,21 @@ class BpNode extends Node
|
|||
case self::OP_OR:
|
||||
$sort_state = min($sort_states);
|
||||
break;
|
||||
case self::OP_XOR:
|
||||
$actualGood = 0;
|
||||
foreach ($sort_states as $s) {
|
||||
if ($this->sortStateTostate($s) === self::ICINGA_OK) {
|
||||
$actualGood++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($actualGood === 1) {
|
||||
$this->state = self::ICINGA_OK;
|
||||
} else {
|
||||
$this->state = self::ICINGA_CRITICAL;
|
||||
}
|
||||
|
||||
return $this;
|
||||
case self::OP_DEGRADED:
|
||||
$maxState = max($sort_states);
|
||||
$flags = $maxState & 0xf;
|
||||
|
|
@ -645,6 +662,8 @@ class BpNode extends Node
|
|||
break;
|
||||
case self::OP_OR:
|
||||
return 'OR';
|
||||
case self::OP_XOR:
|
||||
return 'XOR';
|
||||
break;
|
||||
case self::OP_NOT:
|
||||
return 'NOT';
|
||||
|
|
|
|||
|
|
@ -483,6 +483,31 @@ abstract class Node
|
|||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Node operators
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getOperators(): array
|
||||
{
|
||||
return [
|
||||
'&' => t('AND'),
|
||||
'|' => t('OR'),
|
||||
'^' => t('XOR'),
|
||||
'!' => t('NOT'),
|
||||
'%' => t('DEGRADED'),
|
||||
'1' => t('MIN 1'),
|
||||
'2' => t('MIN 2'),
|
||||
'3' => t('MIN 3'),
|
||||
'4' => t('MIN 4'),
|
||||
'5' => t('MIN 5'),
|
||||
'6' => t('MIN 6'),
|
||||
'7' => t('MIN 7'),
|
||||
'8' => t('MIN 8'),
|
||||
'9' => t('MIN 9'),
|
||||
];
|
||||
}
|
||||
|
||||
public function getIdentifier()
|
||||
{
|
||||
return '@' . $this->getBpConfig()->getName() . ':' . $this->getName();
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ class LegacyConfigParser
|
|||
}
|
||||
|
||||
$op = '&';
|
||||
if (preg_match_all('~(?<!\\\\)([\|\+&\!\%])~', $value, $m)) {
|
||||
if (preg_match_all('~(?<!\\\\)([\|\+&\!\%\^])~', $value, $m)) {
|
||||
$op = implode('', $m[1]);
|
||||
for ($i = 1; $i < strlen($op); $i++) {
|
||||
if ($op[$i] !== $op[$i - 1]) {
|
||||
|
|
@ -378,7 +378,7 @@ class LegacyConfigParser
|
|||
|
||||
$cmps = preg_split('~\s*(?<!\\\\)\\' . $op . '\s*~', $value, -1, PREG_SPLIT_NO_EMPTY);
|
||||
foreach ($cmps as $val) {
|
||||
$val = preg_replace('~(\\\\([\|\+&\!\%]))~', '$2', $val);
|
||||
$val = preg_replace('~(\\\\([\|\+&\!\%\^]))~', '$2', $val);
|
||||
if (strpos($val, ';') !== false) {
|
||||
if ($bp->hasNode($val)) {
|
||||
$node->addChild($bp->getNode($val));
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ class LegacyConfigRenderer
|
|||
$op = static::renderOperator($node);
|
||||
$children = $node->getChildNames();
|
||||
$str = implode(' ' . $op . ' ', array_map(function ($val) {
|
||||
return preg_replace('~([\|\+&\!\%])~', '\\\\$1', $val);
|
||||
return preg_replace('~([\|\+&\!\%\^])~', '\\\\$1', $val);
|
||||
}, $children));
|
||||
|
||||
if ((count($children) < 2) && $op !== '&') {
|
||||
|
|
|
|||