diff --git a/application/forms/AddNodeForm.php b/application/forms/AddNodeForm.php index 43afa4c..5ef11c7 100644 --- a/application/forms/AddNodeForm.php +++ b/application/forms/AddNodeForm.php @@ -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; diff --git a/application/forms/EditNodeForm.php b/application/forms/EditNodeForm.php index eceb065..f26dd04 100644 --- a/application/forms/EditNodeForm.php +++ b/application/forms/EditNodeForm.php @@ -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; diff --git a/application/forms/ProcessForm.php b/application/forms/ProcessForm.php index be1abbf..604b774 100644 --- a/application/forms/ProcessForm.php +++ b/application/forms/ProcessForm.php @@ -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) { diff --git a/doc/09-Operators.md b/doc/09-Operators.md index 58db97c..ba2f51d 100644 --- a/doc/09-Operators.md +++ b/doc/09-Operators.md @@ -17,6 +17,16 @@ The `OR` operator selects the **BEST** state of its child nodes: ![Or Operator #2](screenshot/09_operators/0903_or-operator-without-ok.png) +## XOR + +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. + +![Xor Operator](screenshot/09_operators/0906_xor-operator.png) + +![Xor Operator #2](screenshot/09_operators/0907_xor-operator-not-ok.png) + ## DEGRADED The `DEGRADED` operator behaves like an `AND`, but if the resulting diff --git a/doc/screenshot/09_operators/0901_and-operator.png b/doc/screenshot/09_operators/0901_and-operator.png index a939843..c6e7775 100644 Binary files a/doc/screenshot/09_operators/0901_and-operator.png and b/doc/screenshot/09_operators/0901_and-operator.png differ diff --git a/doc/screenshot/09_operators/0902_or-operator.png b/doc/screenshot/09_operators/0902_or-operator.png index ff6dafa..fd05ec3 100644 Binary files a/doc/screenshot/09_operators/0902_or-operator.png and b/doc/screenshot/09_operators/0902_or-operator.png differ diff --git a/doc/screenshot/09_operators/0903_or-operator-without-ok.png b/doc/screenshot/09_operators/0903_or-operator-without-ok.png index b8097ee..e9fcd4e 100644 Binary files a/doc/screenshot/09_operators/0903_or-operator-without-ok.png and b/doc/screenshot/09_operators/0903_or-operator-without-ok.png differ diff --git a/doc/screenshot/09_operators/0904_min-operator.png b/doc/screenshot/09_operators/0904_min-operator.png index 04fa9be..fd05ec3 100644 Binary files a/doc/screenshot/09_operators/0904_min-operator.png and b/doc/screenshot/09_operators/0904_min-operator.png differ diff --git a/doc/screenshot/09_operators/0906_xor-operator.png b/doc/screenshot/09_operators/0906_xor-operator.png new file mode 100644 index 0000000..fd05ec3 Binary files /dev/null and b/doc/screenshot/09_operators/0906_xor-operator.png differ diff --git a/doc/screenshot/09_operators/0907_xor-operator-not-ok.png b/doc/screenshot/09_operators/0907_xor-operator-not-ok.png new file mode 100644 index 0000000..8ec41b3 Binary files /dev/null and b/doc/screenshot/09_operators/0907_xor-operator-not-ok.png differ diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index a61c984..10e3655 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -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'; diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index a9eb44c..73236ce 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -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(); diff --git a/library/Businessprocess/Storage/LegacyConfigParser.php b/library/Businessprocess/Storage/LegacyConfigParser.php index 834e56d..437e369 100644 --- a/library/Businessprocess/Storage/LegacyConfigParser.php +++ b/library/Businessprocess/Storage/LegacyConfigParser.php @@ -349,7 +349,7 @@ class LegacyConfigParser } $op = '&'; - if (preg_match_all('~(?hasNode($val)) { $node->addChild($bp->getNode($val)); diff --git a/library/Businessprocess/Storage/LegacyConfigRenderer.php b/library/Businessprocess/Storage/LegacyConfigRenderer.php index 5b099fb..430a7a5 100644 --- a/library/Businessprocess/Storage/LegacyConfigRenderer.php +++ b/library/Businessprocess/Storage/LegacyConfigRenderer.php @@ -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 !== '&') {