icingaweb2-module-businessp.../library/Businessprocess/BpConfig.php

808 lines
16 KiB
PHP
Raw Normal View History

<?php
2014-11-30 09:56:58 -05:00
namespace Icinga\Module\Businessprocess;
use Icinga\Exception\IcingaException;
use Icinga\Exception\NotFoundError;
use Icinga\Module\Businessprocess\Exception\NestingError;
use Icinga\Module\Businessprocess\Modification\ProcessChanges;
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
use Exception;
2017-01-11 08:04:45 -05:00
class BpConfig
{
const SOFT_STATE = 0;
2015-02-06 18:13:37 -05:00
const HARD_STATE = 1;
2015-03-16 04:08:00 -04:00
/**
* Name of the configured monitoring backend
*
* @var string
*/
protected $backendName;
/**
* Monitoring backend to retrieve states from
*
* @var MonitoringBackend
*/
protected $backend;
/** @var Metadata */
protected $metadata;
2015-02-06 18:13:37 -05:00
/**
* Business process name
*
* @var string
*/
2014-12-01 08:07:30 -05:00
protected $name;
2015-02-06 18:13:37 -05:00
/**
* Business process title
*
* @var string
*/
protected $title;
2015-02-06 18:13:37 -05:00
/**
* State type, soft or hard
*
* @var int
*/
protected $state_type = self::HARD_STATE;
2015-02-06 18:13:37 -05:00
/**
* Warnings, usually filled at process build time
*
* @var array
*/
protected $warnings = array();
2015-02-06 18:13:37 -05:00
2015-03-16 04:08:00 -04:00
/**
* Errors, usually filled at process build time
*
* @var array
*/
protected $errors = array();
2015-02-06 18:13:37 -05:00
/**
* All used node objects
*
* @var array
*/
protected $nodes = array();
2015-02-06 18:13:37 -05:00
/**
* Root node objects
*
* @var array
*/
protected $root_nodes = array();
2015-02-06 18:13:37 -05:00
/**
* All host names { 'hostA' => true, ... }
*
* @var array
*/
protected $hosts = array();
2015-02-06 18:13:37 -05:00
/** @var bool Whether catchable errors should be thrown nonetheless */
protected $throwErrors = false;
protected $loopDetection = array();
2015-02-06 18:13:37 -05:00
/**
2015-03-16 04:08:00 -04:00
* Applied state simulation
2015-02-06 18:13:37 -05:00
*
2015-03-16 04:08:00 -04:00
* @var Simulation
2015-02-06 18:13:37 -05:00
*/
2015-03-16 04:08:00 -04:00
protected $simulation;
2015-02-06 18:13:37 -05:00
2015-03-16 04:08:00 -04:00
protected $changeCount = 0;
protected $simulationCount = 0;
/** @var ProcessChanges */
protected $appliedChanges;
public function __construct()
{
}
2017-01-11 03:48:16 -05:00
/**
* Retrieve metadata for this configuration
*
* @return Metadata
*/
public function getMetadata()
{
if ($this->metadata === null) {
2016-12-09 08:56:18 -05:00
$this->metadata = new Metadata($this->name);
}
return $this->metadata;
}
2017-01-11 03:48:16 -05:00
/**
* Set metadata
*
* @param Metadata $metadata
*
* @return $this
*/
public function setMetadata(Metadata $metadata)
{
$this->metadata = $metadata;
return $this;
}
2017-01-11 03:48:16 -05:00
/**
* Apply pending process changes
*
* @param ProcessChanges $changes
*
* @return $this
*/
2015-03-16 04:08:00 -04:00
public function applyChanges(ProcessChanges $changes)
{
$cnt = 0;
foreach ($changes->getChanges() as $change) {
$cnt++;
$change->applyTo($this);
}
$this->changeCount = $cnt;
$this->appliedChanges = $changes;
2015-03-16 04:08:00 -04:00
return $this;
}
2017-01-11 03:48:16 -05:00
/**
* Apply a state simulation
*
* @param Simulation $simulation
*
* @return $this
*/
2015-03-16 04:08:00 -04:00
public function applySimulation(Simulation $simulation)
{
$cnt = 0;
foreach ($simulation->simulations() as $node => $s) {
if (! $this->hasNode($node)) {
continue;
}
$cnt++;
$this->getNode($node)
->setState($s->state)
->setAck($s->acknowledged)
->setDowntime($s->in_downtime)
->setMissing(false);
2015-03-16 04:08:00 -04:00
}
$this->simulationCount = $cnt;
2017-01-11 03:48:16 -05:00
return $this;
2015-03-16 04:08:00 -04:00
}
2017-01-11 03:48:16 -05:00
/**
* Number of applied changes
*
* @return int
*/
2015-03-16 04:08:00 -04:00
public function countChanges()
{
return $this->changeCount;
}
2017-01-11 03:48:16 -05:00
/**
* Whether changes have been applied to this configuration
*
* @return int
*/
2015-03-16 04:08:00 -04:00
public function hasChanges()
{
return $this->countChanges() > 0;
}
/**
* @param $name
*
* @return $this
*/
2014-12-01 08:07:30 -05:00
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return string
*/
2014-12-01 08:07:30 -05:00
public function getName()
{
return $this->name;
}
/**
* @return string
*/
public function getHtmlId()
{
return 'businessprocess-' . preg_replace('/[\r\n\t\s]/', '_', $this->getName());
}
public function setTitle($title)
{
$this->title = $title;
return $this;
}
public function getTitle()
{
return $this->getMetadata()->getTitle();
}
2015-03-16 04:08:00 -04:00
public function hasTitle()
{
return $this->getMetadata()->has('Title');
}
2015-03-16 04:08:00 -04:00
public function getBackendName()
{
return $this->getMetadata()->get('Backend');
2015-03-16 04:08:00 -04:00
}
public function hasBackendName()
{
return $this->getMetadata()->has('Backend');
2015-03-16 04:08:00 -04:00
}
public function setBackend(MonitoringBackend $backend)
{
$this->backend = $backend;
return $this;
}
public function getBackend()
{
if ($this->backend === null) {
$this->backend = MonitoringBackend::instance(
2015-03-16 04:08:00 -04:00
$this->getBackendName()
);
}
2015-03-16 04:08:00 -04:00
return $this->backend;
}
public function hasBackend()
{
return $this->backend !== null;
}
public function hasBeenChanged()
{
return false;
}
2015-03-16 04:08:00 -04:00
public function hasSimulations()
{
2015-03-16 04:08:00 -04:00
return $this->countSimulations() > 0;
}
public function countSimulations()
{
return $this->simulationCount;
}
public function clearAppliedChanges()
{
if ($this->appliedChanges !== null) {
$this->appliedChanges->clear();
}
return $this;
}
public function useSoftStates()
{
$this->state_type = self::SOFT_STATE;
return $this;
}
public function useHardStates()
{
$this->state_type = self::HARD_STATE;
return $this;
}
2015-02-12 19:55:46 -05:00
public function usesSoftStates()
{
return $this->state_type === self::SOFT_STATE;
}
public function usesHardStates()
{
return $this->state_type === self::HARD_STATE;
2015-02-12 19:55:46 -05:00
}
public function addRootNode($name)
{
$this->root_nodes[$name] = $this->getNode($name);
return $this;
}
public function removeRootNode($name)
{
if ($this->isRootNode($name)) {
unset($this->root_nodes[$name]);
}
return $this;
}
public function isRootNode($name)
{
return array_key_exists($name, $this->root_nodes);
}
/**
* @return BpNode[]
*/
public function getChildren()
{
return $this->getRootNodes();
}
2017-01-11 03:48:16 -05:00
/**
* @return int
*/
public function countChildren()
{
return count($this->root_nodes);
}
2016-12-09 08:54:31 -05:00
/**
* @return BpNode[]
*/
public function getRootNodes()
{
2014-12-02 05:35:21 -05:00
ksort($this->root_nodes);
return $this->root_nodes;
}
public function listRootNodes()
{
$names = array_keys($this->root_nodes);
sort($names);
return $names;
}
public function getNodes()
{
return $this->nodes;
}
public function hasNode($name)
{
return array_key_exists($name, $this->nodes);
}
public function hasRootNode($name)
{
return array_key_exists($name, $this->root_nodes);
}
public function createService($host, $service)
{
$node = new ServiceNode(
$this,
(object) array(
'hostname' => $host,
'service' => $service
)
);
$this->nodes[$host . ';' . $service] = $node;
2015-02-06 18:01:30 -05:00
$this->hosts[$host] = true;
return $node;
}
public function createHost($host)
{
$node = new HostNode($this, (object) array('hostname' => $host));
$this->nodes[$host . ';Hoststatus'] = $node;
2015-02-06 18:01:30 -05:00
$this->hosts[$host] = true;
return $node;
}
public function calculateAllStates()
{
foreach ($this->getRootNodes() as $node) {
$node->getState();
}
return $this;
}
public function clearAllStates()
{
foreach ($this->getBpNodes() as $node) {
$node->clearState();
}
return $this;
}
public function listInvolvedHostNames()
{
return array_keys($this->hosts);
}
/**
* Create and attach a new process (BpNode)
*
* @param string $name Process name
* @param string $operator Operator (defaults to &)
*
* @return BpNode
*/
public function createBp($name, $operator = '&')
{
$node = new BpNode($this, (object) array(
'name' => $name,
'operator' => $operator,
'child_names' => array(),
));
$this->addNode($name, $node);
return $node;
}
public function createMissingBp($name)
2015-02-12 19:55:46 -05:00
{
return $this->createBp($name)->setMissing();
}
public function createImportedNode($config, $name = null)
{
$params = (object) array('configName' => $config);
if ($name !== null) {
$params->node = $name;
}
$node = new ImportedNode($this, $params);
$this->nodes[$node->getName()] = $node;
2015-02-12 19:55:46 -05:00
return $node;
}
/**
* @param $name
* @return Node
* @throws Exception
*/
public function getNode($name)
{
if (array_key_exists($name, $this->nodes)) {
return $this->nodes[$name];
}
// Fallback: if it is a service, create an empty one:
$this->warn(sprintf('The node "%s" doesn\'t exist', $name));
$pos = strpos($name, ';');
if ($pos !== false) {
$host = substr($name, 0, $pos);
$service = substr($name, $pos + 1);
2016-11-29 05:20:54 -05:00
// TODO: deactivated, this scares me, test it
2017-01-11 08:04:45 -05:00
if ($service === 'Hoststatus') {
2016-11-29 05:20:54 -05:00
return $this->createHost($host);
} else {
return $this->createService($host, $service);
}
}
throw new Exception(
sprintf('The node "%s" doesn\'t exist', $name)
);
}
/**
* @param $name
* @return BpNode
*
* @throws NotFoundError
*/
public function getBpNode($name)
{
if ($this->hasBpNode($name)) {
return $this->nodes[$name];
} else {
throw new NotFoundError('Trying to access a missing business process node "%s"', $name);
}
}
/**
* @param $name
*
* @return bool
*/
public function hasBpNode($name)
{
return array_key_exists($name, $this->nodes)
&& $this->nodes[$name] instanceof BpNode;
}
/**
* Set the state for a specific node
*
* @param string $name Node name
* @param int $state Desired state
*
* @return $this
*/
public function setNodeState($name, $state)
{
$this->getNode($name)->setState($state);
return $this;
}
2017-01-11 03:48:16 -05:00
/**
* Add the given node to the given BpNode
*
* @param $name
* @param BpNode $node
*
* @return $this
*/
public function addNode($name, BpNode $node)
{
if (array_key_exists($name, $this->nodes)) {
$this->warn(
sprintf(
2015-03-16 04:08:00 -04:00
mt('businessprocess', 'Node "%s" has been defined twice'),
$name
)
);
}
2015-02-06 18:01:30 -05:00
$this->nodes[$name] = $node;
if ($node->getDisplay() > 0) {
if (! $this->isRootNode($name)) {
$this->addRootNode($name);
}
} else {
if ($this->isRootNode($name)) {
$this->removeRootNode($name);
}
}
return $this;
}
2017-01-11 03:48:16 -05:00
/**
* Remove all occurrences of a specific node by name
*
* @param $name
*/
public function removeNode($name)
{
unset($this->nodes[$name]);
if (array_key_exists($name, $this->root_nodes)) {
unset($this->root_nodes[$name]);
}
2017-01-11 03:48:16 -05:00
foreach ($this->getBpNodes() as $node) {
if ($node->hasChild($name)) {
$node->removeChild($name);
}
}
}
2016-12-09 08:56:18 -05:00
/**
2017-01-11 03:48:16 -05:00
* Get all business process nodes
*
2016-12-09 08:56:18 -05:00
* @return BpNode[]
*/
public function getBpNodes()
{
$nodes = array();
foreach ($this->nodes as $node) {
2016-12-09 08:56:18 -05:00
if ($node instanceof BpNode) {
$nodes[$node->getName()] = $node;
}
2016-12-09 08:56:18 -05:00
}
return $nodes;
}
2016-12-09 08:56:18 -05:00
/**
* List all business process node names
*
* @return array
*/
public function listBpNodes()
{
$nodes = array();
foreach ($this->getBpNodes() as $name => $node) {
$alias = $node->getAlias();
$nodes[$name] = $name === $alias ? $name : sprintf('%s (%s)', $alias, $node);
}
natsort($nodes);
return $nodes;
}
2016-12-09 08:56:18 -05:00
/**
* All business process nodes defined in this config but not
* assigned to any parent
*
* @return BpNode[]
*/
2015-03-16 04:08:00 -04:00
public function getUnboundNodes()
{
$nodes = array();
2016-12-09 08:56:18 -05:00
foreach ($this->getBpNodes() as $name => $node) {
2015-03-16 04:08:00 -04:00
if ($node->hasParents()) {
continue;
}
if ($node->getDisplay() === 0) {
2016-12-09 08:56:18 -05:00
$nodes[$name] = $node;
2015-03-16 04:08:00 -04:00
}
}
return $nodes;
}
2016-12-09 08:56:18 -05:00
/**
* @return bool
*/
public function hasWarnings()
{
return ! empty($this->warnings);
}
2016-12-09 08:56:18 -05:00
/**
* @return array
*/
public function getWarnings()
{
return $this->warnings;
}
2016-12-09 08:56:18 -05:00
/**
* @return bool
*/
2015-03-16 04:08:00 -04:00
public function hasErrors()
{
return ! empty($this->errors) || $this->isEmpty();
}
2016-12-09 08:56:18 -05:00
/**
* @return array
*/
2015-03-16 04:08:00 -04:00
public function getErrors()
{
$errors = $this->errors;
if ($this->isEmpty()) {
$errors[] = sprintf(
$this->translate(
'No business process nodes for "%s" have been defined yet'
),
$this->getTitle()
);
}
return $errors;
}
2017-01-11 03:48:16 -05:00
/**
* Translation helper
*
* @param $msg
*
* @return mixed|string
*/
public function translate($msg)
2015-03-16 04:08:00 -04:00
{
return mt('businessprocess', $msg);
}
2017-01-11 03:48:16 -05:00
/**
* Add a message to our warning stack
*
* @param $msg
*/
protected function warn($msg)
{
2015-03-16 04:08:00 -04:00
$args = func_get_args();
array_shift($args);
$this->warnings[] = vsprintf($msg, $args);
}
2014-11-30 05:30:59 -05:00
/**
* @param string $msg,...
*
* @return $this
*
* @throws IcingaException
*/
public function addError($msg)
{
$args = func_get_args();
array_shift($args);
$msg = vsprintf($msg, $args);
if ($this->throwErrors) {
throw new IcingaException($msg);
}
$this->errors[] = $msg;
return $this;
}
2017-01-11 03:48:16 -05:00
/**
* Decide whether errors should be thrown or collected
*
* @param bool $throw
*
* @return $this
*/
public function throwErrors($throw = true)
2015-03-16 04:08:00 -04:00
{
$this->throwErrors = $throw;
return $this;
2015-03-16 04:08:00 -04:00
}
2017-01-11 03:48:16 -05:00
/**
* Begin loop detection for the given name
*
* Will throw a NestingError in case this node will be met again below itself
*
* @param $name
*
* @throws NestingError
*/
public function beginLoopDetection($name)
{
// echo "Begin loop $name\n";
if (array_key_exists($name, $this->loopDetection)) {
$loop = array_keys($this->loopDetection);
$loop[] = $name;
$this->loopDetection = array();
throw new NestingError('Loop detected: %s', implode(' -> ', $loop));
}
$this->loopDetection[$name] = true;
}
2017-01-11 03:48:16 -05:00
/**
* Remove the given name from the loop detection stack
*
* @param $name
*/
public function endLoopDetection($name)
{
// echo "End loop $this->name\n";
unset($this->loopDetection[$name]);
}
2017-01-11 03:48:16 -05:00
/**
* Whether this configuration has any Nodes
*
* @return bool
*/
2015-03-16 04:08:00 -04:00
public function isEmpty()
{
// This is faster
if (! empty($this->root_nodes)) {
return false;
}
return count($this->listBpNodes()) === 0;
2015-03-16 04:08:00 -04:00
}
}