editor: sorry, monster commit :)

This commit is contained in:
Thomas Gelf 2015-03-16 09:08:00 +01:00
parent 65dba837d9
commit e057e61567
41 changed files with 3071 additions and 368 deletions

View file

@ -0,0 +1,30 @@
<?php
use Icinga\Module\Businessprocess\Controller;
class Businessprocess_IndexController extends Controller
{
/**
* Show a welcome page if no process is available
*/
public function indexAction()
{
$this->tabs()->add('welcome', array(
'label' => $this->translate('Business Processes'),
'url' => $this->getRequest()->getUrl()
))->activate('welcome');
$configs = $this->storage()->listProcesses();
if (! empty($configs)) {
// Redirect to show the first process if there is any
$this->redirectNow(
'businessprocess/process/show',
array('config' => key($configs))
);
}
// Check back from time to time, maybe someone created a process
$this->setAutorefreshInterval(30);
}
}

View file

@ -1,52 +1,91 @@
<?php
use Icinga\Module\Businessprocess\Controller;
use Icinga\Module\Businessprocess\Simulation;
use Icinga\Module\Businessprocess\Forms\ProcessForm;
use Icinga\Module\Businessprocess\Forms\SimulationForm;
use Icinga\Web\Url;
/*
config = <file>
process = <node>
*/
class Businessprocess_NodeController extends Controller
{
// rename to config
public function editAction()
{
$bp = $this->loadBp();
$bp = $this->loadModifiedBpConfig();
$node = $bp->getNode($this->getParam('node'));
$detail = Url::fromPath(
'businessprocess/node/edit',
array(
'config' => $this->view->configName,
'node' => $node
)
);
$form = new ProcessForm();
$form->setProcess($bp)
->setSession($this->session())
->setNode($node)
->handleRequest();
$this->view->form = ProcessForm::construct()
->setProcess($bp)
->setSession($this->session())
->setNode($node)
->setRedirectUrl(
sprintf(
'businessprocess/process/show?config=%s&unlocked#!%s',
$bp->getName(),
$detail->getAbsoluteUrl()
)
)
->handleRequest();
$this->view->form = $form;
$this->view->node = $node;
}
public function simulateAction()
{
$bp = $this->loadBp();
$bp = $this->loadBpConfig();
$nodename = $this->getParam('node');
$node = $bp->getNode($nodename);
$detail = Url::fromPath(
$details = Url::fromPath(
'businessprocess/node/simulate',
array('node' => $nodename)
array(
'config' => $this->view->configName,
'node' => $nodename
)
);
$url = sprintf(
'businessprocess/process/show?unlocked&config=%s#!%s',
$bp->getName(),
$details->getAbsoluteUrl()
);
$form = new SimulationForm();
$form->setProcess($bp)
->setSession($this->session())
$this->view->form = SimulationForm::construct()
->setSimulation(new Simulation($bp, $this->session()))
->setNode($node)
// TODO: find a better way to handle redirects
->setRedirectUrl(
sprintf(
'businessprocess/process/show?simulation=1&processName=%s#!%s',
$bp->getName(),
$detail->getAbsoluteUrl()
)
)
->setRedirectUrl($url)
->handleRequest();
$this->view->form = $form;
$this->view->node = $node;
}
public function addAction()
{
$bp = $this->loadBpConfig();
$redirectUrl = Url::fromPath(
'businessprocess/process/show',
array('config' => $bp->getName())
);
$this->view->form = ProcessForm::construct()
->setProcess($bp)
->setSession($this->session())
->setRedirectUrl($redirectUrl)
->handleRequest();
}
public function deleteAction()
{
}
}

View file

@ -1,14 +1,144 @@
<?php
use Icinga\Module\Businessprocess\Controller;
use Icinga\Module\Businessprocess\ConfigDiff;
use Icinga\Module\Businessprocess\Simulation;
use Icinga\Module\Businessprocess\ProcessChanges;
use Icinga\Module\Businessprocess\Storage\LegacyStorage;
use \Exception;
use Icinga\Module\Businessprocess\Forms\BpConfigForm;
use Icinga\Module\Businessprocess\Forms\DeleteConfigForm;
use Icinga\Web\Notification;
use Icinga\Web\Widget\Tabextension\DashboardAction;
class Businessprocess_ProcessController extends Controller
{
/**
* Create a new business process configuration
*/
public function createAction()
{
$this->setTitle($this->translate('Create a new business process'));
$this->tabsForCreate()->activate('create');
$this->view->form = BpConfigForm::construct()
->setStorage($this->storage())
->setRedirectUrl('businessprocess/process/show')
->handleRequest();
}
/**
* Upload an existing business process configuration
*/
public function uploadAction()
{
$this->setTitle($this->translate('Upload a business process config file'));
$this->tabsForCreate()->activate('upload');
}
/**
* Show a business process tree
*/
public function showAction()
{
$this->redirectIfConfigChosen();
if ($this->params->get('unlocked')) {
$bp = $this->loadModifiedBpConfig();
$bp->unlock();
} else {
$bp = $this->loadBpConfig();
}
$this->setTitle('Business Process "%s"', $bp->getTitle());
$this->tabsForShow()->activate('show');
// Do not lock empty configs
if ($bp->isEmpty() && ! $this->view->compact && $bp->isLocked()) {
$this->redirectNow($this->url()->with('unlocked', true));
}
if ($node = $this->params->get('node')) {
// Render a specific node
$this->view->nodeName = $node;
$this->view->bp = $bp->getNode($node);
} else {
// Render a single process
$this->view->bp = $bp;
if ($bp->hasWarnings()) {
$this->view->warnings = $bp->getWarnings();
}
}
$this->setAutorefreshInterval(10);
$bp->retrieveStatesFromBackend();
if ($bp->isLocked()) {
$this->tabs()->extend(new DashboardAction());
} else {
$simulation = new Simulation($bp, $this->session());
if ($this->params->get('dismissSimulations')) {
Notification::success(
sprintf(
$this->translate('%d applied simulation(s) have been dropped'),
$simulation->count()
)
);
$simulation->clear();
$this->redirectNow($this->url()->without('dismissSimulations')->without('unlocked'));
}
$bp->applySimulation($simulation);
}
if ($this->params->get('mode') === 'toplevel') {
$this->render('toplevel');
}
}
/**
* Show a business process from a toplevel perspective
*/
public function toplevelAction()
{
$this->redirectIfConfigChosen();
}
/**
* Show the source code for a process
*/
public function sourceAction()
{
$this->tabsForConfig()->activate('source');
$bp = $this->loadModifiedBpConfig();
$this->view->source = $bp->toLegacyConfigString();
$this->view->showDiff = (bool) $this->params->get('showDiff', false);
if ($this->view->showDiff) {
$this->view->diff = ConfigDiff::create(
$this->storage()->getSource($this->view->configName),
$this->view->source
);
$this->view->title = sprintf(
$this->translate('%s: Source Code Differences'),
$bp->getTitle()
);
} else {
$this->view->title = sprintf(
$this->translate('%s: Source Code'),
$bp->getTitle()
);
}
}
/**
* Download a process configuration file
*/
public function downloadAction()
{
$bp = $this->loadBp();
$bp = $this->loadModifiedBpConfig();
header(
sprintf(
'Content-Disposition: attachment; filename="%s.conf";',
@ -20,73 +150,83 @@ class Businessprocess_ProcessController extends Controller
echo $bp->toLegacyConfigString();
// Didn't have time to lookup how to correctly disable our renderers
// TODO: no exit :)
exit;
$this->doNotRender();
}
public function showAction()
/**
* Modify a business process configuration
*/
public function configAction()
{
if ($this->getRequest()->isPost()) {
$this->redirectNow($this->getRequest()->getUrl()->with('processName', $this->getRequest()->getPost('processName')));
}
$this->view->compact = $this->params->get('view') === 'compact';
$storage = new LegacyStorage($this->Config()->getSection('global'));
$this->view->processList = $storage->listProcesses();
$process = $this->params->get('processName', key($this->view->processList));
$this->view->processName = $process;
$this->tabsForConfig()->activate('config');
$bp = $this->loadModifiedBpConfig();
$this->view->tabs = $this->tabs()->activate('show');
$this->view->title = 'Business Processes';
$this->setTitle(
$this->translate('%s: Configuration'),
$bp->getTitle()
);
$bp = $this->loadBp();
try {
$bp->retrieveStatesFromBackend();
} catch (Exception $e) {
$this->view->errors = array(
'Could not retrieve process state: ' . $e->getMessage()
$url = sprintf(
'businessprocess/process/show?config=%s&unlocked#!%s',
$bp->getName(),
$this->getRequest()->getUrl()
);
$this->view->form = BpConfigForm::construct()
->setProcessConfig($bp)
->setSession($this->session())
->setRedirectUrl($url)
->handleRequest();
$this->view->deleteForm = DeleteConfigForm::construct()
->setStorage($this->storage())
->setController($this)
->setBpConfig($bp)
->handleRequest();
}
/**
* Redirect to our URL plus the chosen config if someone switched the
* config in the appropriate dropdown list
*/
protected function redirectIfConfigChosen()
{
$request = $this->getRequest();
if ($request->isPost()) {
// We switched the process in the config dropdown list
$params = array(
'config' => $request->getPost('config')
);
$this->redirectNow($this->url()->with($params));
}
if ($process = $this->params->get('process')) {
$this->view->bp = $bp->getNode($process);
} else {
$this->view->bp = $bp;
if ($bp->hasWarnings()) {
$this->view->warnings = $bp->getWarnings();
}
}
$this->setAutorefreshInterval(10);
if ($this->params->get('showSource')) {
$this->view->source = $bp->toLegacyConfigString();
$this->render('source');
}
if ($this->params->get('simulation')) {
$bp->setSimulationMode();
$this->addSimulation($bp);
}
if ($this->params->get('edit')) {
$bp->setEditMode();
}
if ($this->params->get('store')) {
$storage->storeProcess($bp);
$this->redirectNow($this->getRequest()->getUrl()->without('store'));
}
if ($this->params->get('mode') === 'toplevel') {
$this->render('toplevel');
}
}
protected function addSimulation($bp)
protected function tabsForShow()
{
$simulations = $this->session()->get('simulations', array());
foreach ($simulations as $node => $s) {
$bp->getNode($node)
->setState($s->state)
->setAck($s->acknowledged)
->setDowntime($s->in_downtime);
}
return $this->tabs()->add('show', array(
'label' => $this->translate('Business Process'),
'url' => $this->url()
));
}
protected function tabsForCreate()
{
return $this->tabs()->add('create', array(
'label' => $this->translate('Create'),
'url' => 'businessprocess/process/create'
))->add('upload', array(
'label' => $this->translate('Upload'),
'url' => 'businessprocess/process/upload'
));
}
protected function tabsForConfig()
{
return $this->tabs()->add('config', array(
'label' => $this->translate('Process Configuration'),
'url' => $this->getRequest()->getUrl()->without('nix')->setPath('businessprocess/process/config')
))->add('source', array(
'label' => $this->translate('Source'),
'url' => $this->getRequest()->getUrl()->without('nix')->setPath('businessprocess/process/source')
));
}
}

View file

@ -0,0 +1,164 @@
<?php
namespace Icinga\Module\Businessprocess\Forms;
use Icinga\Application\Config;
use Icinga\Module\Businessprocess\BusinessProcess;
use Icinga\Module\Businessprocess\Form;
use Icinga\Web\Notification;
use Icinga\Web\Request;
use Icinga\Web\Url;
class BpConfigForm extends Form
{
protected $storage;
protected $backend;
protected $config;
protected $node;
protected $objectList = array();
protected $processList = array();
protected $session;
public function setup()
{
$this->addElement('text', 'name', array(
'label' => $this->translate('Name'),
'required' => true,
'description' => $this->translate(
'This is the unique identifier of this process'
),
));
$this->addElement('text', 'title', array(
'label' => $this->translate('Title'),
'description' => $this->translate(
'Usually this title will be shown for this process. Equals name'
. ' if not given'
),
));
$this->addElement('select', 'backend_name', array(
'label' => $this->translate('Backend'),
'description' => $this->translate(
'Icinga Web Monitoring Backend where current object states for'
. ' this process should be retrieved from'
),
'multiOptions' => array(
null => $this->translate('Use the configured default backend'),
) + $this->listAvailableBackends()
));
$this->addElement('select', 'state_type', array(
'label' => $this->translate('State Type'),
'required' => true,
'description' => $this->translate(
'Whether this process should be based on Icinga hard or soft states'
),
'multiOptions' => array(
'hard' => $this->translate('Use HARD states'),
'soft' => $this->translate('Use SOFT states'),
)
));
$this->addElement('submit', $this->translate('Store'));
}
protected function listAvailableBackends()
{
$keys = array_keys(Config::module('monitoring', 'backends')->toArray());
return array_combine($keys, $keys);
}
public function XXsetBackend($backend)
{
$this->backend = $backend;
return $this;
}
public function setStorage($storage)
{
$this->storage = $storage;
return $this;
}
public function setProcessConfig($config)
{
$this->config = $config;
$this->getElement('name')->setValue($config->getName());
if ($config->hasTitle()) {
$this->getElement('title')->setValue($config->getTitle());
}
if ($config->hasBackend()) {
$this->getElement('backend_name')->setValue(
$config->getBackend()->getName()
);
}
if ($config->usesSoftStates()) {
$this->getElement('state_type')->setValue('soft');
} else {
$this->getElement('state_type')->setValue('hard');
}
return $this;
}
public function setSession($session)
{
$this->session = $session;
return $this;
}
public function onSuccess()
{
$name = $this->getValue('name');
$title = $this->getValue('title');
$backend = $this->getValue('backend');
if ($this->config === null) {
// New config
$config = new BusinessProcess();
$config->setName($name);
if ($title) {
$config->setTitle($title);
}
if ($backend) {
$config->setBackendName($backend);
}
if ($this->getValue('state_type') === 'soft') {
$config->useSoftStates();
} else {
$config->useHardStates();
}
$this->storage->storeProcess($config);
$this->setRedirectUrl(
Url::fromPath(
$this->getRedirectUrl(),
array('config' => $name, 'unlocked' => true)
)
);
Notification::success(sprintf('Process %s has been created', $name));
} else {
Notification::success(sprintf('Process %s has NOT YET been modified', $name));
}
/*
$storage->storeProcess($bp);
$modifications = $this->session->get('modifications', array());
$node = $this->config->getNode($this->getValue('name'));
$node->setChildNames($this->getValue('children'));
$node->setOperator($this->getValue('operator'));
$modifications[$this->config->getName()] = $this->config->toLegacyConfigString();
$this->session->set('modifications', $modifications);
$message = 'Process %s has been modified';
Notification::success(sprintf($message, $this->config->getName()));
*/
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace Icinga\Module\Businessprocess\Forms;
use Icinga\Module\Businessprocess\BusinessProcess;
use Icinga\Module\Businessprocess\Form;
use Icinga\Web\Notification;
class DeleteConfigForm extends Form
{
protected $storage;
protected $controller;
public function setup()
{
$this->addHidden('name');
$this->addElement('submit', $this->translate('Delete this process'));
}
public function setStorage($storage)
{
$this->storage = $storage;
return $this;
}
public function setController($controller)
{
$this->controller = $controller;
return $this;
}
public function setBpConfig($bp)
{
$this->getElement('name')->setValue($bp->getName());
return $this;
}
public function onSuccess()
{
$name = $this->getValue('name');
$this->storage->deleteProcess($name);
$this->setRedirectUrl('businessprocess');
Notification::success(sprintf('Process %s has been deleted', $name));
}
}

View file

@ -2,10 +2,12 @@
namespace Icinga\Module\Businessprocess\Forms;
use Icinga\Web\Form;
use Icinga\Web\Notification;
use Icinga\Web\Request;
use Icinga\Module\Businessprocess\Node;
use Icinga\Module\Businessprocess\BpNode;
use Icinga\Module\Businessprocess\Form;
use Icinga\Module\Businessprocess\ProcessChanges;
class ProcessForm extends Form
{
@ -21,17 +23,22 @@ class ProcessForm extends Form
protected $session;
public function __construct($options = null)
{
parent::__construct($options);
$this->setup();
}
public function setup()
{
$this->addElement('text', 'name', array(
'label' => $this->translate('Process name'),
'label' => $this->translate('Name'),
'required' => true,
'description' => $this->translate(
'This is the unique identifier of this process'
),
));
$this->addElement('text', 'alias', array(
'label' => $this->translate('Title'),
'description' => $this->translate(
'Usually this title will be shown for this node. Equals name'
. ' if not given'
),
));
$this->addElement('select', 'operator', array(
@ -40,7 +47,27 @@ class ProcessForm extends Form
'multiOptions' => array(
'&' => $this->translate('AND'),
'|' => $this->translate('OR'),
'min' => $this->translate('min')
'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'),
)
));
$this->addElement('select', 'display', array(
'label' => $this->translate('Visualization'),
'required' => true,
'description' => $this->translate(
'Where to show this process'
),
'multiOptions' => array(
'0' => $this->translate('Subprocess only'),
'1' => $this->translate('Toplevel Process'),
)
));
@ -49,8 +76,19 @@ class ProcessForm extends Form
'required' => true,
'size' => 14,
'style' => 'width: 25em;',
'description' => $this->translate('Hosts, services or other processes that should be part of this business process')
'description' => $this->translate(
'Hosts, services or other processes that should be part of this'
. ' business process'
)
));
$this->addElement('text', 'url', array(
'label' => $this->translate('Info URL'),
'description' => $this->translate(
'URL pointing to more information about this node'
)
));
$this->addElement('submit', $this->translate('Store'));
}
@ -64,12 +102,15 @@ class ProcessForm extends Form
protected function fillAvailableChildren()
{
$this->getElement('children')->setMultiOptions(
array(
if (empty($this->processList)) {
$children = $this->objectList;
} else {
$children = array(
$this->translate('Other Business Processes') => $this->processList
) + $this->objectList
);
) + $this->objectList;
}
$this->getElement('children')->setMultiOptions($children);
}
public function setProcess($process)
@ -88,14 +129,19 @@ class ProcessForm extends Form
return $this;
}
public function setNode($node)
public function setNode(Node $node)
{
$this->node = $node;
$this->setDefaults(array(
'name' => (string) $node,
'alias' => $node->hasAlias() ? $node->getAlias() : '',
'display' => $node->getDisplay(),
'operator' => $node->getOperator(),
'url' => $node->getInfoUrl(),
'children' => array_keys($node->getChildren())
));
$this->getElement('name')->setAttrib('readonly', true);
return $this;
}
@ -140,13 +186,63 @@ class ProcessForm extends Form
public function onSuccess()
{
$modifications = $this->session->get('modifications', array());
$node = $this->process->getNode($this->getValue('name'));
$node->setChildNames($this->getValue('children'));
$node->setOperator($this->getValue('operator'));
$modifications[$this->process->getName()] = $this->process->toLegacyConfigString();
$this->session->set('modifications', $modifications);
$message = 'Process %s has been modified';
Notification::success(sprintf($message, $this->process->getName()));
$changes = ProcessChanges::construct($this->process, $this->session);
$modifications = array();
$children = $this->getValue('children');
$alias = $this->getValue('alias');
$operator = $this->getValue('operator');
$display = $this->getValue('display');
$url = $this->getValue('url');
if (empty($url)) {
$url = null;
}
if (empty($alias)) {
$alias = null;
}
ksort($children);
// TODO: rename
if ($node = $this->node) {
if ($display !== $node->getDisplay()) {
$modifications['display'] = $display;
}
if ($operator !== $node->getOperator()) {
$modifications['operator'] = $operator;
}
if ($children !== $node->getChildNames()) {
$modifications['childNames'] = $children;
}
if ($url !== $node->getInfoUrl()) {
$modifications['infoUrl'] = $url;
}
if ($alias !== $node->getAlias()) {
$modifications['alias'] = $alias;
}
} else {
$modifications = array(
'display' => $display,
'operator' => $operator,
'childNames' => $children,
'infoUrl' => $url,
'alias' => $alias,
);
}
if (! empty($modifications)) {
if ($this->node === null) {
$changes->createNode($this->getValue('name'), $modifications);
} else {
$changes->modifyNode($this->node, $modifications);
}
Notification::success(
sprintf(
'Process %s has been modified',
$this->process->getName()
)
);
}
}
}

View file

@ -2,20 +2,17 @@
namespace Icinga\Module\Businessprocess\Forms;
use Icinga\Web\Form;
use Icinga\Web\Request;
use Icinga\Web\Notification;
use Icinga\Module\Businessprocess\BpNode;
use Icinga\Module\Businessprocess\Form;
use Icinga\Module\Businessprocess\Simulation;
use Icinga\Web\Notification;
use Icinga\Web\Request;
class SimulationForm extends Form
{
protected $backend;
protected $process;
protected $node;
protected $session;
protected $simulation;
public function __construct($options = null)
{
@ -33,6 +30,7 @@ class SimulationForm extends Form
'1' => $this->translate('WARNING'),
'2' => $this->translate('CRITICAL'),
'3' => $this->translate('UNKNOWN'),
'99' => $this->translate('PENDING'),
)
));
@ -51,19 +49,6 @@ class SimulationForm extends Form
$this->addElement('submit', $this->translate('Apply'));
}
public function setBackend($backend)
{
$this->backend = $backend;
return $this;
}
public function setProcess($process)
{
$this->process = $process;
$this->setBackend($process->getBackend());
return $this;
}
public function setNode($node)
{
$this->node = $node;
@ -72,53 +57,41 @@ class SimulationForm extends Form
'acknowledged' => $node->isAcknowledged(),
'in_downtime' => $node->isInDowntime(),
));
return $this->checkNodeSession();
return $this->checkDefaults();
}
public function setSession($session)
public function setSimulation($simulation)
{
$this->session = $session;
return $this->checkNodeSession();
$this->simulation = $simulation;
return $this->checkDefaults();
}
protected function checkNodeSession()
protected function checkDefaults()
{
if ($this->node === null || $this->session === null) {
return $this;
if ($this->node !== null
&& $this->simulation !== null
&& $this->simulation->hasNode((string) $this->node)
) {
$this->setDefaults((array) $this->simulation->getNode((string) $this->node));
}
$simulations = $this->session->get('simulations', array());
$node = (string) $this->node;
if (array_key_exists($node, $simulations)) {
$this->setDefaults(array(
'simulate' => true,
'state' => $simulations[$node]->state,
'in_downtime' => $simulations[$node]->in_downtime,
'acknowledged' => $simulations[$node]->acknowledged,
));
}
return $this;
}
public function onSuccess()
{
$node = (string) $this->node;
$simulations = $this->session->get('simulations', array());
if ($this->getValue('state') === '') {
if (array_key_exists($node, $simulations)) {
if ($this->simulation->remove($node)) {
Notification::success($this->translate('Simulation has been removed'));
unset($simulations[$node]);
$this->session->set('simulations', $simulations);
}
} else {
$simulations[$node] = (object) array(
Notification::success($this->translate('Simulation has been set'));
$this->simulation->set($node, (object) array(
'state' => $this->getValue('state'),
'acknowledged' => $this->getValue('acknowledged'),
'in_downtime' => $this->getValue('in_downtime'),
);
$this->session->set('simulations', $simulations);
'in_downtime' => $this->getValue('in_downtime'),
));
}
}
}

View file

@ -0,0 +1,101 @@
<?php
namespace Icinga\Module\Businessprocess\Forms;
use Icinga\Application\Config;
use Icinga\Module\Businessprocess\BusinessProcess;
use Icinga\Web\Form;
use Icinga\Web\Notification;
use Icinga\Web\Request;
class CreateConfigForm extends Form
{
public function setup()
{
$this->addElement('text', 'name', array(
'label' => $this->translate('Name'),
'required' => true,
'description' => $this->translate('This is the unique identifier of this process'),
));
$this->addElement('text', 'title', array(
'label' => $this->translate('Title'),
'description' => $this->translate('Usually this title will be shown for this process. Equals name if not given'),
));
$this->addElement('select', 'backend_name', array(
'label' => $this->translate('Backend'),
'required' => true,
'description' => $this->translate('Icinga Web Monitoring Backend where current object states for this process should be retrieved from'),
'multiOptions' => array(
null => $this->translate('Use current default backend'),
) + $this->listAvailableBackends()
));
$this->addElement('select', 'state_type', array(
'label' => $this->translate('State Type'),
'required' => true,
'description' => $this->translate('Whether this process should be based on Icinga hard or soft states'),
'multiOptions' => array(
'hard' => $this->translate('Use HARD states'),
'soft' => $this->translate('Use SOFT states'),
)
));
$this->addElement('submit', $this->translate('Store'));
}
protected function listAvailableBackends()
{
$keys = array_keys(Config::module('monitoring', 'backends')->toArray());
return array_combine($keys, $keys);
}
public function setBackend($backend)
{
$this->backend = $backend;
return $this;
}
public function setProcessConfig($config)
{
$this->process = $config;
$this->getElement('name')->setValue($config->getName());
if ($config->hasTitle()) {
$this->getElement('title')->setValue($config->getTitle());
}
if ($config->hasBackend()) {
$this->getElement('backend_name')->setValue($config->getBackend()->getName());
}
if ($config->usesSoftStates()) {
$this->getElement('state_type')->setValue('soft');
} else {
$this->getElement('state_type')->setValue('hard');
}
return $this;
}
public function setSession($session)
{
$this->session = $session;
return $this;
}
public function onSuccess()
{
/*
$storage->storeProcess($bp);
$modifications = $this->session->get('modifications', array());
$node = $this->process->getNode($this->getValue('name'));
$node->setChildNames($this->getValue('children'));
$node->setOperator($this->getValue('operator'));
$modifications[$this->process->getName()] = $this->process->toLegacyConfigString();
$this->session->set('modifications', $modifications);
$message = 'Process %s has been modified';
Notification::success(sprintf($message, $this->process->getName()));
*/
}
}

View file

@ -2,42 +2,125 @@
# Copyright (C) 2015 Icinga Development Team
# This file is distributed under the same license as Businessprocess Module.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
#
msgid ""
msgstr ""
"Project-Id-Version: Businessprocess Module (2.0.0-beta1)\n"
"Report-Msgid-Bugs-To: dev@icinga.org\n"
"POT-Creation-Date: 2015-03-03 13:25+0100\n"
"PO-Revision-Date: 2015-03-03 13:25+0100\n"
"POT-Creation-Date: 2015-03-16 06:19+0100\n"
"PO-Revision-Date: 2015-03-16 09:04+0100\n"
"Last-Translator: Thomas Gelf <thomas@gelf.net>\n"
"Language: de_DE\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.5.4\n"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:41
#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:83
#, php-format
msgid "%d applied simulation(s) have been dropped"
msgstr "%d angewendete Simulation(en) wurde(n) entfernt"
#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Controller.php:73
#, php-format
msgid "%d pending change(s) have been dropped"
msgstr "%d vorgenommene Änderung(en) wurde(n) verworfen"
#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:165
#, php-format
msgid "%s: Configuration"
msgstr "%s: Konfiguration"
#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:129
#, php-format
msgid "%s: Source Code"
msgstr "%s: Quellcode"
#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:124
#, php-format
msgid "%s: Source Code Differences"
msgstr "%s: Quellcode Unterschiede"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:48
msgid "AND"
msgstr "UND"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:44
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:42
msgid "Acknowledged"
msgstr "Bestätigt"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:51
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/node/add.phtml:8
msgid "Add new process node"
msgstr "Neuen Knoten hinzufügen"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:49
msgid "Apply"
msgstr "Anwenden"
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/index/index.phtml:8
#, php-format
msgid ""
"As no business process has been defined yet you might want to create a %s or "
"upload an %s."
msgstr ""
"Nachdem noch kein Business-Prozess definiert wurde, möchtest du vermutlich "
"einen %s erstellen oder einen %s hochladen."
#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:47
#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:27
msgid "Backend"
msgstr "Backend"
#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:206
msgid "Business Process"
msgstr "Business-Prozess"
#: /usr/local/icingaweb-modules/businessprocess/configuration.php:4
#: /usr/local/icingaweb-modules/businessprocess/application/controllers/IndexController.php:13
msgid "Business Processes"
msgstr "Business-Prozesse"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:34
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:31
msgid "CRITICAL"
msgstr "KRITISCH"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:52
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/index/index.phtml:6
msgid "Configure your first business process"
msgstr "Konfiguriere deinen ersten Business-Prozess"
#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/BusinessProcess.php:297
#, php-format
msgid "Could not retrieve process state: %s"
msgstr "Konnte den Prozess-Status nicht ermitteln: %s"
#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:214
msgid "Create"
msgstr "Erstelle"
#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:21
msgid "Create a new business process"
msgstr "Neuen Business-Prozess erstellen"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/DeleteConfigForm.php:18
msgid "Delete this process"
msgstr "Lösche diesen Prozess"
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/show.phtml:40
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/show.phtml:51
msgid "Dismiss"
msgstr "Verwerfen"
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/source.phtml:6
msgid "Download process configuration"
msgstr "Prozess-Konfiguration herunterladen"
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/source.phtml:10
msgid "Highlight changes"
msgstr "Änderungen hervorheben"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:80
msgid ""
"Hosts, services or other processes that should be part of this business "
"process"
@ -45,71 +128,261 @@ msgstr ""
"Hosts, Services oder andere Prozesse welche Teil dieses Business-Prozesses "
"sein sollen"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:48
#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:49
#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:29
msgid ""
"Icinga Web Monitoring Backend where current object states for this process "
"should be retrieved from"
msgstr ""
"Das Icinga Web Monitoring Backend von welchem die Status-Informationen für "
"diesen Prozess bezogen werden sollen"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:46
msgid "In downtime"
msgstr "In Downtime"
#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Node.php:304
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:86
msgid "Info URL"
msgstr "Info-URL"
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/show.phtml:13
msgid "Lock this process"
msgstr "Sperre diesen Prozess"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:50
msgid "MIN 1"
msgstr "MIN 1"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:51
msgid "MIN 2"
msgstr "MIN 2"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:52
msgid "MIN 3"
msgstr "MIN 3"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:53
msgid "MIN 4"
msgstr "MIN 4"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:54
msgid "MIN 5"
msgstr "MIN 5"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:55
msgid "MIN 6"
msgstr "MIN 6"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:56
msgid "MIN 7"
msgstr "MIN 7"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:57
msgid "MIN 8"
msgstr "MIN 8"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:58
msgid "MIN 9"
msgstr "MIN 9"
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/node/edit.phtml:10
#, php-format
msgid "Modify process node: %s"
msgstr "Bearbeite Knoten: %s"
#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/BpNode.php:268
msgid "Modify this node"
msgstr "Bearbeite diesen Knoten"
#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Node.php:350
msgid "More information"
msgstr "Weitere Informationen"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:32
#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:31
#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:16
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:29
msgid "Name"
msgstr "Name"
#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/BusinessProcess.php:519
#, php-format
msgid "No business process nodes for \"%s\" have been defined yet"
msgstr "Es wurden noch keine Business-Prozess Knoten für \"%s\" definiert"
#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/BusinessProcess.php:467
#, php-format
msgid "Node \"%s\" has been defined twice"
msgstr "Der Knoden \"%s\" wurde doppelt definiert"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:29
msgid "OK"
msgstr "OK"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:42
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:49
msgid "OR"
msgstr "ODER"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:38
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:45
msgid "Operator"
msgstr "Operator"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:69
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:109
msgid "Other Business Processes"
msgstr "Andere Business-Prozesse"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:40
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:33
msgid "PENDING"
msgstr "PENDING"
#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/BusinessProcess.php:538
#, php-format
msgid "Parser waring on %s:%s: %s"
msgstr "Parse-Warnung an %s:%s: %s"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:38
msgid "Plugin output"
msgstr "Plugin-Ausgabe"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:48
#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:225
msgid "Process Configuration"
msgstr "Prozess-Konfiguration"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:75
msgid "Process components"
msgstr "Prozess-Komponenten"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:33
msgid "Process name"
msgstr "Prozess-Name"
#: /usr/local/icingaweb-modules/businessprocess/configuration.php:3
msgid "Reporting"
msgstr "Reporting"
#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Controller.php:40
msgid "Show"
msgstr "Zeige"
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/source.phtml:8
msgid "Show source code"
msgstr "Quellcode anzeigen"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:111
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:86
msgid "Simulation has been removed"
msgstr "Simulation wurde entfernt"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:29
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:89
msgid "Simulation has been set"
msgstr "Simulation wurde gesended"
#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:228
msgid "Source"
msgstr "Quelle"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:26
msgid "State"
msgstr "Status"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:54
#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:58
#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:36
msgid "State Type"
msgstr "Statustyp"
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/show.phtml:36
#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:68
#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:44
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:92
msgid "Store"
msgstr "Speichern"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:35
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:69
msgid "Subprocess only"
msgstr "Nur Sub-Prozess"
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/upload.phtml:8
msgid "This has not been implemented yet"
msgstr "Das wurde noch nicht implementiert"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:34
#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:18
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:32
msgid "This is the unique identifier of this process"
msgstr "Das ist der eindeutige Identifier dieses Prozesses"
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/show.phtml:33
#, php-format
msgid "This process has %d pending change(s)."
msgstr "Dieser Prozess hat %d ungespeicherte Änderung(en)"
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/show.phtml:48
#, php-format
msgid "This process shows %d simulated state(s)."
msgstr "Dieser Prozess zeigt %d simulierte Zustände"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:39
#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:22
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:37
msgid "Title"
msgstr "Titel"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:70
msgid "Toplevel Process"
msgstr "Toplevel-Prozess"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:32
msgid "UNKNOWN"
msgstr "UNBEKANNT"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:31
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:88
msgid "URL pointing to more information about this node"
msgstr "URL zu mehr Informationen über diesen Knoten"
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/show.phtml:10
msgid "Unlock this process"
msgstr "Entsperre diesen Prozess"
#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:217
msgid "Upload"
msgstr "Upload"
#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:35
msgid "Upload a business process config file"
msgstr "Lade eine Business-Prozess Konfigurationsdatei hoch"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:64
#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:40
msgid "Use HARD states"
msgstr "HARD-States benutzen"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:65
#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:41
msgid "Use SOFT states"
msgstr "SOFT-States benutzen"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:31
msgid "Use current default backend"
msgstr "Aktuelles Standard-Backend benutzen"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:28
msgid "Use current state"
msgstr "Aktuellen Status benutzen"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:33
#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:53
msgid "Use the configured default backend"
msgstr "Benutze das konfigurierte Standard-Backend"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:39
msgid ""
"Usually this title will be shown for this node. Equals name if not given"
msgstr ""
"Für gewöhnlich wird dieser Titel für diesen Knoten angezeigt. Entspricht dem "
"Namen, wenn nicht angegeben"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:41
#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:23
msgid ""
"Usually this title will be shown for this process. Equals name if not given"
msgstr ""
"Für gewöhnlich wird dieser Titel für diesen Prozess angezeigt. Entspricht "
"dem Namen, wenn nicht angegeben"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:63
msgid "Visualization"
msgstr "Darstellung"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:30
msgid "WARNING"
msgstr "WARNUNG"
@ -117,6 +390,25 @@ msgstr "WARNUNG"
msgid "Warnings"
msgstr "Warnungen"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:43
msgid "min"
msgstr "min"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:66
msgid "Where to show this process"
msgstr "Wo soll dieser Prozess angezeigt werden"
#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:61
#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:38
msgid "Whether this process should be based on Icinga hard or soft states"
msgstr "Ob dieser Prozess auf Icinga's hard- oder soft-states basieren soll"
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/index/index.phtml:10
msgid "existing one"
msgstr "einen existierenden"
#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/index/index.phtml:9
msgid "new business process"
msgstr "neuen Business-Prozess"
#~ msgid "Show"
#~ msgstr "Zeige"
#~ msgid "min"
#~ msgstr "min"

View file

@ -1,4 +0,0 @@
<a href="<?= $this->url('businessprocess/process/download', array(
'process' => $this->processName
)) ?>" target="_self" title="<?= $this->escape('Download process configuration') ?>"><?= $this->icon('download') ?></a>

View file

@ -0,0 +1,12 @@
<div class="controls">
<?= $this->tabs ?>
</div>
<div class="content">
<h1><?= $this->translate('Configure your first business process') ?></h1>
<p><?= sprintf (
$this->translate('As no business process has been defined yet you might want to create a %s or upload an %s.'),
$this->qlink($this->translate('new business process'), 'businessprocess/process/create'),
$this->qlink($this->translate('existing one'), 'businessprocess/process/upload')
) ?></p>
</div>

View file

@ -0,0 +1,10 @@
<?php
use Icinga\Module\Businessprocess\HostNode;
use Icinga\Module\Businessprocess\ServiceNode;
use Icinga\Module\Businessprocess\BpNode;
?><div class="content">
<h1><?= $this->translate('Add new process node') ?></h1>
<?= $this->form ?>
</div>

View file

@ -5,6 +5,11 @@ use Icinga\Module\Businessprocess\ServiceNode;
use Icinga\Module\Businessprocess\BpNode;
?><div class="content">
<h1><?= $this->escape($node) ?></h1>
<h1><?= $this->escape(
sprintf(
$this->translate('Modify process node: %s'),
$node
)
) ?></h1>
<?= $this->form ?>
</div>

View file

@ -0,0 +1,16 @@
<div class="controls">
<?= $this->tabs ?>
<h1><?= $this->escape($this->title) ?>
<a href="<?= $this->url('businessprocess/process/source', array(
'config' => $this->configName
)) ?>" title="<?= $this->escape('Show configuration source') ?>"><?= $this->icon('doc-text') ?></a>
</h1>
</div>
<div class="content">
<?= $this->form ?>
<div data-base-target="_main">
<?= $this->deleteForm ?>
</div>
</div>

View file

@ -0,0 +1,9 @@
<div class="controls">
<?= $this->tabs ?>
<h1><?= $this->escape($this->title) ?></h1>
</div>
<div class="content">
<?= $this->form ?>
</div>

View file

@ -1,29 +1,66 @@
<?php if (! $this->compact): ?>
<div class="controls">
<?= $this->tabs ?>
<h1><?= $this->escape($this->title) ?>
<a href="<?= $this->url()->with('mode', 'toplevel') ?>" title="<?= $this->escape('Switch to toplevel view') ?>"><?= $this->icon('dashboard') ?></a>
<?= $this->render('simulationlink.phtml') ?>
<?= $this->render('editlink.phtml') ?>
<?= $this->render('downloadlink.phtml') ?>
<h1><form method="post" action="<?= $this->url()->without('node')->without('unlocked') ?>">
<?= $this->formSelect('config', $this->configName, array('class' => 'autosubmit'), $this->processList) ?>
</form>
<a href="<?= $this->url()->with('mode', 'toplevel')->without('unlocked') ?>" title="<?= $this->escape('Switch to toplevel view') ?>" data-base-target="_main"><?= $this->icon('dashboard') ?></a>
<?php if ($this->bpconfig->isLocked()): ?>
<a href="<?= $this->url()->with('unlocked', true) ?>" title="<?= $this->escape($this->translate('Unlock this process')) ?>"><?= $this->icon('lock') ?></a>
<?php else: ?>
<?php if (! $this->bpconfig->isEmpty()): ?>
<a href="<?= $this->url()->without('unlocked') ?>" style="color: red" title="<?= $this->escape($this->translate('Lock this process')) ?>" data-base-target="_main"><?= $this->icon('lock-open') ?></a>
<?php endif ?>
<a href="<?= $this->url('businessprocess/process/config', array('config' => $this->configName)) ?>" title="<?= $this->escape('Modify this process') ?>" data-base-target="_next"><?= $this->icon('wrench') ?></a>
<?php endif ?>
<a href="<?= $this->url('businessprocess/process/create') ?>" title="<?= $this->escape('Create a new business process configuration') ?>" data-base-target="_main"><?= $this->icon('plus') ?></a>
</h1>
</div>
<?php endif ?>
<div class="content<?= $this->compact ? ' compact' : '' ?>" data-base-target="_next">
<?php if (! $this->compact): ?>
<form method="post" action="<?= $this->url()->without('process') ?>" data-base-target="_self">
<?= $this->formSelect('processName', $this->processName, array('class' => 'autosubmit'), $this->processList) ?>
</form>
<?php if (! $this->bpconfig->isLocked()): ?>
<?= $this->qlink('Add new node', 'businessprocess/node/add', array('config' => $this->configName)) ?>
<?php endif ?>
<?php if (! empty($this->errors)): ?>
<?php if ($this->bpconfig->hasErrors() || $this->bpconfig->hasChanges() || $this->bpconfig->hasSimulations()): ?>
<ul class="error">
<?php foreach ($this->errors as $error): ?>
<?php foreach ($this->bpconfig->getErrors() as $error): ?>
<li><?= $this->escape($error) ?></li>
<?php endforeach ?>
<?php if ($this->bpconfig->hasChanges()): ?>
<li><?= sprintf(
$this->translate('This process has %d pending change(s).'),
$this->bpconfig->countChanges()
) ?> <?= $this->qlink(
$this->translate('Store'),
'businessprocess/process/config',
array('config' => $this->configName)
) ?> <?= $this->qlink(
$this->translate('Dismiss'),
$this->url()->with('dismissChanges', true),
null,
array('data-base-target' => '_main')
) ?></li>
<?php endif ?>
<?php if ($this->bpconfig->hasSimulations()): ?>
<li><?= sprintf(
$this->translate('This process shows %d simulated state(s).'),
$this->bpconfig->countSimulations()
) ?> <?= $this->qlink(
$this->translate('Dismiss'),
$this->url()->with('dismissSimulations', true),
null,
array('data-base-target' => '_main')
) ?></li>
<?php endif ?>
</ul>
<?php endif ?>
<div class="bp">
<?= $this->bp->renderHtml($this) ?>
<?php if (! $this->bpconfig->isLocked()): ?>
<?= $this->bp->renderUnbound($this) ?>
<?php endif ?>
</div>
<?= $this->render('warnings.phtml') ?>
</div>

View file

@ -1,25 +1,37 @@
<div class="controls">
<?= $this->tabs ?>
<h1><?= $this->escape($this->title) ?>
<a href="<?= $this->url()->without('mode') ?>" title="<?= $this->escape('Switch to tree view') ?>" style="float: right"><?= $this->icon('sitemap') ?></a>
<?= $this->render('simulationlink.phtml') ?>
<?= $this->render('editlink.phtml') ?>
<a href="<?= $this->url('businessprocess/process/download', array(
'config' => $this->configName
)) ?>" target="_self" title="<?= $this->escape($this->translate('Download process configuration')) ?>"><?= $this->icon('download') ?></a>
<?php if ($this->showDiff): ?>
<a href="<?= $this->url()->without('showDiff') ?>" title="<?= $this->escape($this->translate('Show source code')) ?>"><?= $this->icon('doc-text') ?></a>
<?php else: ?>
<a href="<?= $this->url()->with('showDiff', true) ?>" title="<?= $this->escape($this->translate('Highlight changes')) ?>"><?= $this->icon('flapping') ?></a>
<?php endif ?>
</h1>
</div>
<div class="content" data-base-target="_next">
<pre>
<div class="content">
<?php if ($this->showDiff): ?>
<div class="diff">
<?= $this->diff->renderHtml() ?>
</div>
<?php else: ?>
<table class="sourcecode">
<?php
$cnt = 0;
$lines = preg_split('~\r?\n~', $this->source);
$len = ceil(log(count($lines), 10));
$rowhtml = sprintf('<tr><th>%%0%dd: </th><td>%%s<br></td></tr>', $len);
foreach ($lines as $line) {
$cnt++;
printf("%0" . $len . "d: %s\n", $cnt, $this->escape($line));
printf($rowhtml, $cnt, $this->escape($line));
}
?>
</pre>
</table>
<?php endif ?>
</div>

View file

@ -14,28 +14,22 @@ if ($count < 20) {
<?php if (! $this->compact): ?>
<div class="controls">
<?= $this->tabs ?>
<h1><?= $this->escape($this->title) ?>
<h1><form method="post" action="<?= $this->url()->without('process') ?>" data-base-target="_self">
<?= $this->formSelect('node', $this->nodeName, array('class' => 'autosubmit'), $this->processList) ?>
</form>
<a href="<?= $this->url()->without('mode') ?>" title="<?= $this->escape('Switch to tree view') ?>" style="float: right"><?= $this->icon('sitemap') ?></a>
<?= $this->render('simulationlink.phtml') ?>
<?= $this->render('editlink.phtml') ?>
<?= $this->render('downloadlink.phtml') ?>
</h1>
</div>
<?php endif ?>
<div class="content<?= $this->compact ? ' compact' : '' ?>" data-base-target="_next">
<?php if (! $this->compact): ?>
<form method="post" action="<?= $this->url()->without('process') ?>" data-base-target="_self">
<?= $this->formSelect('processName', $this->processName, array('class' => 'autosubmit'), $this->processList) ?>
</form>
<?php endif ?>
<div class="toplevel <?= $howMany ?>" data-base-target="_next">
<?php foreach ($this->bp->getChildren() as $name => $node): ?>
<div class="<?= strtolower($node->getStateName()) ?><?= $node->isHandled() ? ' handled' : '' ?>">
<?php if ($node instanceof ImportedNode): ?>
<a href="<?= $this->url('businessprocess/process/show', array('processName' => $node->getConfigName(), 'process' => $name)) ?>"><?= $this->escape($node->getAlias()) ?></a>
<a href="<?= $this->url('businessprocess/process/show', array('config' => $this->configName, 'node' => $name)) ?>"><?= $this->escape($node->getAlias()) ?></a>
<?php else: ?>
<a href="<?= $this->url('businessprocess/process/show', array('processName' => $this->processName, 'process' => $name)) ?>"><?= $this->escape($node->getAlias()) ?></a>
<a href="<?= $this->url('businessprocess/process/show', array('config' => $this->configName, 'node' => $this->nodeName)) ?>"><?= $this->escape($node->getAlias()) ?></a>
<?php endif ?>
</div>
<?php endforeach ?>

View file

@ -0,0 +1,11 @@
<div class="controls">
<?= $this->tabs ?>
<h1><?= $this->escape($this->title) ?></h1>
</div>
<div class="content">
<ul class="error">
<li><?= $this->translate('This has not been implemented yet') ?></li>
</ul>
</div>

View file

@ -2,5 +2,5 @@
$section = $this->menuSection($this->translate('Reporting'))
->add($this->translate('Business Processes'))
->setUrl('businessprocess/process/show');
->setUrl('businessprocess');

View file

@ -3,6 +3,7 @@
namespace Icinga\Module\Businessprocess;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\ProgrammingError;
class BpNode extends Node
{
@ -37,7 +38,7 @@ class BpNode extends Node
{
if ($this->counters === null) {
$this->getState();
$this->counters = array(0, 0, 0, 0);
$this->counters = array(0, 0, 0, 0, 99 => 0);
foreach ($this->children as $child) {
if ($child instanceof BpNode) {
$counters = $child->getStateSummary();
@ -160,36 +161,42 @@ class BpNode extends Node
}
switch ($this->operator) {
case self::OP_AND:
$state = max($sort_states);
$sort_state = max($sort_states);
break;
case self::OP_OR:
$state = min($sort_states);
$sort_state = min($sort_states);
break;
default:
// MIN:
sort($sort_states);
// default -> unknown
$state = 2 << self::SHIFT_FLAGS;
$sort_state = 3 << self::SHIFT_FLAGS;
for ($i = 1; $i <= $this->operator; $i++) {
$state = array_shift($sort_states);
$sort_state = array_shift($sort_states);
}
}
if ($state & self::FLAG_DOWNTIME) {
if ($sort_state & self::FLAG_DOWNTIME) {
$this->setDowntime(true);
}
if ($state & self::FLAG_ACK) {
if ($sort_state & self::FLAG_ACK) {
$this->setAck(true);
}
$state = $state >> self::SHIFT_FLAGS;
$sort_state = $sort_state >> self::SHIFT_FLAGS;
if ($state === 3) {
if ($sort_state === 4) {
$this->state = 2;
} elseif ($state === 2) {
} elseif ($sort_state === 3) {
$this->state = 3;
} elseif ($sort_state === 2) {
$this->state = 1;
} elseif ($sort_state === 1) {
$this->state = 99;
} elseif ($sort_state === 0) {
$this->state = 0;
} else {
$this->state = $state;
throw new ProgrammingError('Got invalid sorting state %s', $sort_state);
}
}
@ -209,13 +216,24 @@ class BpNode extends Node
return $this;
}
public function getDisplay()
{
return $this->display;
}
public function setChildNames($names)
{
sort($names);
$this->child_names = $names;
$this->children = null;
return $this;
}
public function getChildNames()
{
return $this->child_names;
}
public function getChildren()
{
if ($this->children === null) {
@ -223,6 +241,7 @@ class BpNode extends Node
natsort($this->child_names);
foreach ($this->child_names as $name) {
$this->children[$name] = $this->bp->getNode($name);
$this->children[$name]->addParent($this);
}
}
return $this->children;
@ -235,15 +254,21 @@ class BpNode extends Node
}
}
public function renderLink($view)
protected function getActionIcons($view)
{
if (! $this->bp->isEditMode()) {
return parent::renderLink($view);
$icons = array();
if (! $this->bp->isLocked()) {
$icons[] = $this->actionIcon(
$view,
'wrench',
$view->url('businessprocess/node/edit', array(
'config' => $this->bp->getName(),
'node' => $this->name
)),
mt('businessprocess', 'Modify this node')
);
}
return $view->qlink($this->name, 'businessprocess/node/edit', array(
'node' => $this->name,
'processName' => $this->bp->getName()
));
return $icons;
}
public function toLegacyConfigString(& $rendered = array())

View file

@ -2,6 +2,7 @@
namespace Icinga\Module\Businessprocess;
use Icinga\Application\Benchmark;
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
use Icinga\Data\Filter\Filter;
use Exception;
@ -12,6 +13,13 @@ class BusinessProcess
const HARD_STATE = 1;
/**
* Name of the configured monitoring backend
*
* @var string
*/
protected $backendName;
/**
* Monitoring backend to retrieve states from
*
@ -47,6 +55,13 @@ class BusinessProcess
*/
protected $warnings = array();
/**
* Errors, usually filled at process build time
*
* @var array
*/
protected $errors = array();
/**
* All used node objects
*
@ -76,11 +91,11 @@ class BusinessProcess
protected $hosts = array();
/**
* Whether we are in simulation mode
* Applied state simulation
*
* @var boolean
* @var Simulation
*/
protected $simulationMode = false;
protected $simulation;
/**
* Whether we are in edit mode
@ -89,10 +104,55 @@ class BusinessProcess
*/
protected $editMode = false;
protected $locked = true;
protected $changeCount = 0;
protected $simulationCount = 0;
public function __construct()
{
}
public function applyChanges(ProcessChanges $changes)
{
$cnt = 0;
foreach ($changes->getChanges() as $change) {
$cnt++;
$change->applyTo($this);
}
$this->changeCount = $cnt;
return $this;
}
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);
}
$this->simulationCount = $cnt;
}
public function countChanges()
{
return $this->changeCount;
}
public function hasChanges()
{
return $this->countChanges() > 0;
}
public function setName($name)
{
$this->name = $name;
@ -115,12 +175,27 @@ class BusinessProcess
return $this->title ?: $this->getName();
}
public function loadBackendByName($name)
public function hasTitle()
{
$this->backend = MonitoringBackend::createBackend($name);
return $this->title !== null;
}
public function setBackendName($name)
{
$this->backendName = $name;
return $this;
}
public function getBackendName()
{
return $this->backendName;
}
public function hasBackendName()
{
return $this->backendName !== null;
}
public function setBackend(MonitoringBackend $backend)
{
$this->backend = $backend;
@ -130,8 +205,11 @@ class BusinessProcess
public function getBackend()
{
if ($this->backend === null) {
$this->backend = MonitoringBackend::createBackend();
$this->backend = MonitoringBackend::createBackend(
$this->getBackendName()
);
}
return $this->backend;
}
@ -145,15 +223,30 @@ class BusinessProcess
return false;
}
public function setSimulationMode($mode = true)
public function isLocked()
{
$this->simulationMode = (bool) $mode;
return $this->locked;
}
public function lock($lock = true)
{
$this->locked = (bool) $lock;
return $this;
}
public function isSimulationMode()
public function unlock()
{
return $this->simulationMode;
return $this->lock(false);
}
public function hasSimulations()
{
return $this->countSimulations() > 0;
}
public function countSimulations()
{
return $this->simulationCount;
}
public function setEditMode($mode = true)
@ -197,6 +290,19 @@ class BusinessProcess
public function retrieveStatesFromBackend()
{
try {
$this->reallyRetrieveStatesFromBackend();
} catch (Exception $e) {
$this->error(
$this->translate('Could not retrieve process state: %s'),
$e->getMessage()
);
}
}
public function reallyRetrieveStatesFromBackend()
{
Benchmark::measure('Retrieving states for business process ' . $this->getName());
$backend = $this->getBackend();
// TODO: Split apart, create a dedicated function.
// Separate "parse-logic" from "retrieve-state-logic"
@ -221,6 +327,10 @@ class BusinessProcess
$filter->addFilter(Filter::where('host_name', $host));
}
if ($filter->isEmpty()) {
return $this;
}
$hostStatus = $backend->select()->from('hostStatus', array(
'hostname' => 'host_name',
'last_state_change' => $hostStateChangeColumn,
@ -263,6 +373,8 @@ class BusinessProcess
}
}
ksort($this->root_nodes);
Benchmark::measure('Got states for business process ' . $this->getName());
return $this;
}
@ -352,7 +464,7 @@ class BusinessProcess
if (array_key_exists($name, $this->nodes)) {
$this->warn(
sprintf(
'Node "%s" has been defined twice',
mt('businessprocess', 'Node "%s" has been defined twice'),
$name
)
);
@ -362,6 +474,27 @@ class BusinessProcess
return $this;
}
public function getUnboundNodes()
{
$nodes = array();
foreach ($this->nodes as $node) {
if (! $node instanceof BpNode) {
continue;
}
if ($node->hasParents()) {
continue;
}
if ($node->getDisplay() === '0') {
$nodes[(string) $node] = $node;
}
}
return $nodes;
}
public function hasWarnings()
{
return ! empty($this->warnings);
@ -372,35 +505,73 @@ class BusinessProcess
return $this->warnings;
}
public function hasErrors()
{
return ! empty($this->errors) || $this->isEmpty();
}
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;
}
protected function translate($msg)
{
return mt('businessprocess', $msg);
}
protected function warn($msg)
{
$args = func_get_args();
array_shift($args);
if (isset($this->parsing_line_number)) {
$this->warnings[] = sprintf(
'Parser waring on %s:%s: %s',
$this->translate('Parser waring on %s:%s: %s'),
$this->filename,
$this->parsing_line_number,
$msg
vsprintf($msg, $args)
);
} else {
$this->warnings[] = $msg;
$this->warnings[] = vsprintf($msg, $args);
}
}
protected function error($msg)
{
$args = func_get_args();
array_shift($args);
$this->errors[] = vsprintf($msg, $args);
}
public function toLegacyConfigString()
{
$conf = sprintf(
"### Busines Process Config File ###\n"
. "#\n"
. "# Title : %s\n"
// . "# Owner : %s\n"
. "# Backend : %s\n"
. "# Statetype : %s\n",
$this->getTitle(),
$this->backend->getName(),
$this->state_type = self::SOFT_STATE ? 'soft' : 'hard'
);
$settings = array();
if ($this->hasTitle()) {
$settings['Title'] = $this->getTitle();
}
// TODO: backendName?
if ($this->backend) {
$settings['Backend'] = $this->backend->getName();
}
$settings['Statetype'] = $this->usesSoftStates() ? 'soft' : 'hard';
if (false) $conf .= sprintf("# xSLA Hosts: %s\n", implode(', ', array()));
if (false) {
$settings['SLA Hosts'] = implode(', ', array());
}
$conf = "### Business Process Config File ###\n#\n";
foreach ($settings as $key => $value) {
$conf .= sprintf("# %-9s : %s\n", $key, $value);
}
$conf .= sprintf(
"#\n"
@ -412,17 +583,46 @@ class BusinessProcess
foreach ($this->getChildren() as $child) {
$conf .= $child->toLegacyConfigString($rendered);
}
foreach ($this->getUnboundNodes() as $node) {
$conf .= $node->toLegacyConfigString($rendered);
}
return $conf . "\n";
}
public function isEmpty()
{
return $this->countChildren() === 0;
}
public function renderHtml($view)
{
$html = '<div class="bp">';
$html = '';
foreach ($this->getRootNodes() as $name => $node) {
// showNode($this, $node, $this->slas, $this->opened, 'bp_')
$html .= $node->renderHtml($view);
}
$html .= '</div>';
return $html;
}
public function renderUnbound($view)
{
$html = '';
$unbound = $this->getUnboundNodes();
if (empty($unbound)) {
return $html;
}
$parent = new BpNode($this, (object) array(
'name' => '__unbound__',
'operator' => '|',
'child_names' => array_keys($unbound)
));
$parent->getState();
$parent->setMissing()
->setDowntime(false)
->setAck(false)
->setAlias('Unbound nodes');
$html .= $parent->renderHtml($view);
return $html;
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Icinga\Module\Businessprocess;
use FineDiff;
class ConfigDiff
{
protected $a;
protected $b;
protected $diff;
protected $opcodes;
protected function __construct($a, $b)
{
$this->a = $a;
$this->b = $b;
require_once dirname(__DIR__) . '/vendor/PHP-FineDiff/finediff.php';
$granularity = FineDiff::$paragraphGranularity; // character, word, sentence, paragraph
$this->diff = new FineDiff($a, $b, $granularity);
}
public function renderHtml()
{
return $this->diff->renderDiffToHTML();
}
public function __toString()
{
return $this->renderHtml();
}
public static function create($a, $b)
{
$diff = new static($a, $b);
return $diff;
}
}

View file

@ -2,26 +2,27 @@
namespace Icinga\Module\Businessprocess;
use Exception;
use Icinga\Application\Icinga;
use Icinga\Web\Controller\ModuleActionController;
use Icinga\Module\Monitoring\Backend;
use Icinga\Module\Businessprocess\Storage\LegacyStorage;
use Icinga\Module\Businessprocess\BusinessProcess;
use Icinga\Module\Businessprocess\Form\ProcessForm;
use Icinga\Module\Businessprocess\Form\SimulationForm;
use Icinga\Module\Businessprocess\Storage\LegacyStorage;
use Icinga\Module\Monitoring\Backend;
use Icinga\Web\Controller as ModuleController;
use Icinga\Web\Notification;
use Icinga\Web\Url;
use Icinga\Web\Widget;
use Exception;
class Controller extends ModuleActionController
class Controller extends ModuleController
{
protected $config;
protected $backend;
protected $views;
private $storage;
protected $aliases;
private $url;
public function init()
{
@ -29,26 +30,25 @@ class Controller extends ModuleActionController
if (! $m->hasLoaded('monitoring') && $m->hasInstalled('monitoring')) {
$m->loadModule('monitoring');
}
$this->view->errors = array();
$this->config = $this->Config();
$this->view->compact = $this->params->get('view') === 'compact';
}
protected function url()
{
if ($this->url === null) {
$this->url = clone $this->getRequest()->getUrl();
}
return $this->url;
}
protected function tabs()
{
$url = Url::fromRequest();
$tabs = Widget::create('tabs')->add('show', array(
'title' => $this->translate('Show'),
'url' => 'businessprocess/process/show',
));
foreach (array('processName', 'process') as $param) {
if ($process = $this->params->get($param)) {
foreach ($tabs->getTabs() as $tab) {
$tab->setUrlParams(array($param => $process));
}
}
if ($this->view->tabs === null) {
$this->view->tabs = Widget::create('tabs');
}
return $tabs;
return $this->view->tabs;
}
protected function session()
@ -56,15 +56,54 @@ class Controller extends ModuleActionController
return $this->Window()->getSessionNamespace('businessprocess');
}
protected function loadBp()
protected function setTitle($title)
{
$storage = new LegacyStorage($this->Config()->getSection('global'));
$args = func_get_args();
array_shift($args);
$this->view->title = vsprintf($title, $args);
}
protected function loadModifiedBpConfig()
{
$bp = $this->loadBpConfig();
$changes = ProcessChanges::construct($bp, $this->session());
if ($this->params->get('dismissChanges')) {
Notification::success(
sprintf(
$this->translate('%d pending change(s) have been dropped'),
$changes->count()
)
);
$changes->clear();
$this->redirectNow($this->url()->without('dismissChanges')->without('unlocked'));
}
$bp->applyChanges($changes);
return $bp;
}
protected function loadBpConfig()
{
$storage = $this->storage();
$this->view->processList = $storage->listProcesses();
$process = $this->params->get('processName', key($this->view->processList));
$this->view->processName = $process;
$bp = $storage->loadProcess($process);
// No process found? Go to welcome page
if (empty($this->view->processList)) {
$this->redirectNow('businessprocess');
}
$name = $this->params->get(
'config',
key($this->view->processList)
);
$modifications = $this->session()->get('modifications', array());
if (array_key_exists($name, $modifications)) {
$bp = $storage->loadFromString($name, $modifications[$name]);
} else {
$bp = $storage->loadProcess($name);
}
// allow URL parameter to override configured state type
if (null !== ($stateType = $this->params->get('state_type'))) {
if ($stateType === 'soft') {
$bp->useSoftStates();
@ -74,9 +113,23 @@ class Controller extends ModuleActionController
}
}
$this->view->bpconfig = $bp;
$this->view->configName = $bp->getName();
return $bp;
}
protected function storage()
{
if ($this->storage === null) {
$this->storage = new LegacyStorage(
$this->Config()->getSection('global')
);
}
return $this->storage;
}
protected function loadSlas()
{
$bpconf = $this->bpconf;

View file

@ -0,0 +1,36 @@
<?php
namespace Icinga\Module\Businessprocess;
use Icinga\Web\Request;
use Icinga\Web\Form as WebForm;
class Form extends WebForm
{
public function __construct($options = null)
{
parent::__construct($options);
$this->setup();
}
public function addHidden($name, $value = null)
{
$this->addElement('hidden', $name);
$this->getElement($name)->setDecorators(array('ViewHelper'));
if ($value !== null) {
$this->setDefault($name, $value);
}
return $this;
}
public function handleRequest(Request $request = null)
{
parent::handleRequest();
return $this;
}
public static function construct()
{
return new static;
}
}

View file

@ -22,13 +22,13 @@ class HostNode extends Node
{
if ($this->bp->isSimulationMode()) {
return $view->qlink($this->getHostname(), 'businessprocess/host/simulate', array(
'node' => $this->name,
'processName' => $this->bp->getName()
'config' => $this->bp->getName(),
'node' => $this->name
));
} else {
return $view->qlink($this->getHostname(), 'monitoring/host/show', array(
'host' => $this->getHostname(),
'processName' => $this->bp->getName()
'config' => $this->bp->getName(),
'host' => $this->getHostname()
));
}
}

View file

@ -3,6 +3,7 @@
namespace Icinga\Module\Businessprocess;
use Icinga\Application\Config;
use Icinga\Web\Url;
use Icinga\Module\Businessprocess\Storage\LegacyStorage;
class ImportedNode extends Node
@ -38,11 +39,6 @@ class ImportedNode extends Node
return $this->state;
}
public function getSortingState()
{
return $this->importedNode()->getSortingState();
}
public function getAlias()
{
return $this->importedNode()->getAlias();
@ -50,7 +46,8 @@ class ImportedNode extends Node
public function isMissing()
{
return $this->getState() === null;
return $this->importedNode()->isMissing();
// TODO: WHY? return $this->getState() === null;
}
public function isInDowntime()
@ -86,19 +83,34 @@ class ImportedNode extends Node
}
return $this->importedNode;
}
protected function getActionIcons($view)
{
$icons = array();
if (! $this->bp->isLocked()) {
$url = Url::fromPath( 'businessprocess/node/simulate', array(
'config' => $this->bp->getName(),
'node' => $this->name
));
$icons[] = $this->actionIcon(
$view,
'magic',
$url,
'Simulation'
);
}
return $icons;
}
public function renderLink($view)
{
if ($this->bp->isSimulationMode()) {
return $view->qlink($this->getAlias(), 'businessprocess/importednode/simulate', array(
'processName' => $this->configName,
'node' => $this->name
));
} else {
return $view->qlink($this->getAlias(), 'businessprocess/process/show', array(
'processName' => $this->configName,
'process' => $this->name
));
}
return $view->qlink($this->getAlias(), 'businessprocess/process/show', array(
'config' => $this->configName,
'process' => $this->name
));
}
}

View file

@ -2,6 +2,8 @@
namespace Icinga\Module\Businessprocess;
use Icinga\Web\Url;
use Icinga\Exception\ProgrammingError;
use Exception;
abstract class Node
@ -12,6 +14,15 @@ abstract class Node
const FLAG_NONE = 8;
const SHIFT_FLAGS = 4;
const ICINGA_OK = 0;
const ICINGA_WARNING = 1;
const ICINGA_CRITICAL = 2;
const ICINGA_UNKNOWN = 3;
const ICINGA_UP = 0;
const ICINGA_DOWN = 1;
const ICINGA_UNREACHABLE = 2;
const ICINGA_PENDING = 99;
/**
* Main business process object
*
@ -20,11 +31,11 @@ abstract class Node
protected $bp;
/**
* Parent node
* Parent nodes
*
* @var Node
* @var array
*/
protected $parent;
protected $parents = array();
/**
* Node identifier
@ -70,7 +81,8 @@ abstract class Node
'OK',
'WARNING',
'CRITICAL',
'UNKNOWN'
'UNKNOWN',
99 => 'PENDING'
);
abstract public function __construct(BusinessProcess $bp, $object);
@ -107,7 +119,7 @@ abstract class Node
);
}
$this->childs[(string) $node] = $node;
$node->setParent($this);
$node->addParent($this);
return $this;
}
@ -137,7 +149,7 @@ abstract class Node
public function getState()
{
if ($this->state === null) {
throw new Exception(
throw new ProgrammingError(
sprintf(
'Node %s is unable to retrieve it\'s state',
$this->name
@ -150,18 +162,32 @@ abstract class Node
public function getSortingState()
{
$state = $this->getState();
if ($state === 3) {
$state = 2;
} elseif ($state === 2) {
$state = 3;
switch ($state) {
case 0:
$sort = 0;
break;
case 1:
$sort = 2;
break;
case 2:
$sort = 4;
break;
case 3:
$sort = 3;
break;
case 99:
$sort = 1;
break;
default:
throw new ProgrammingError('Got invalid state %d', $state);
}
$state = ($state << self::SHIFT_FLAGS)
$sort = ($sort << self::SHIFT_FLAGS)
+ ($this->isInDowntime() ? self::FLAG_DOWNTIME : 0)
+ ($this->isAcknowledged() ? self::FLAG_ACK : 0);
if (! ($state & (self::FLAG_DOWNTIME | self::FLAG_ACK))) {
$state |= self::FLAG_NONE;
if (! ($sort & (self::FLAG_DOWNTIME | self::FLAG_ACK))) {
$sort |= self::FLAG_NONE;
}
return $state;
return $sort;
}
public function getLastStateChange()
@ -175,9 +201,9 @@ abstract class Node
return $this;
}
public function setParent(Node $parent)
public function addParent(Node $parent)
{
$this->parent = $parent;
$this->parents[] = $parent;
return $this;
}
@ -242,6 +268,11 @@ abstract class Node
return $this->name;
}
public function hasParents()
{
return count($this->parents) > 0;
}
protected function renderHtmlForChildren($view)
{
$html = '';
@ -304,19 +335,22 @@ abstract class Node
$this->renderLink($view)
);
$icons = array();
foreach ($this->getActionIcons($view) as $icon) {
$icons[] = $icon;
}
if ($this->hasInfoUrl()) {
$url = $this->getInfoUrl();
$target = preg_match('~^https?://~', $url) ? ' target="_blank"' : '';
$title = sprintf(
' <a href="%s" %stitle="%s: %s" style="float: right">%s</a>%s',
$icons[] = $this->actionIcon(
$view,
'help',
$url,
$target,
mt('businessprocess', 'More information'),
$url,
$view->icon('help'),
$title
sprintf('%s: %s', mt('businessprocess', 'More information'), $url)
);
}
$title = implode("\n", $icons) . $title;
$html .= sprintf(
'<td>%s</td></tr>',
@ -329,6 +363,28 @@ abstract class Node
return $html;
}
protected function getActionIcons($view)
{
return array();
}
protected function actionIcon($view, $icon, $url, $title)
{
if ($url instanceof Url || ! preg_match('~^https?://~', $url)) {
$target = '';
} else {
$target = ' target="_blank"';
}
return sprintf(
' <a href="%s" %stitle="%s" style="float: right">%s</a>',
$url,
$target,
$view->escape($title),
$view->icon($icon)
);
}
public function renderLink($view)
{
return '<a href="#">' . ($this->hasAlias() ? $this->getAlias() : $this->name) . '</a>';
@ -361,6 +417,6 @@ abstract class Node
public function __destruct()
{
unset($this->parent);
$this->parents = array();
}
}

View file

@ -0,0 +1,93 @@
<?php
namespace Icinga\Module\Businessprocess;
use Icinga\Exception\ProgrammingError;
abstract class NodeAction
{
const TYPE_CREATE = 'create';
const TYPE_REMOVE = 'remove';
const TYPE_MODIFY = 'modify';
const TYPE_CHILD_ADD = 'childRemove';
const TYPE_CHILD_REMOVE = 'childAdd';
protected $nodeName;
protected $actionName;
protected $preserveProperties = array();
public function __construct($node)
{
$this->nodeName = (string) $node;
}
abstract public function applyTo(BusinessProcess $bp);
public function getNodeName()
{
return $this->nodeName;
}
public function is($actionName)
{
return $this->getActionName() === $actionName;
}
public static function create($actionName, $nodeName)
{
$classname = __NAMESPACE__ . '\\Node' . ucfirst($actionName) . 'Action';
$object = new $classname($nodeName);
return $object;
}
public function serialize()
{
$object = (object) array(
'actionName' => $this->getActionName(),
'nodeName' => $this->getNodeName(),
'properties' => array()
);
foreach ($this->preserveProperties as $key) {
$func = 'get' . ucfirst($key);
$object->properties[$key] = $this->$func();
}
return json_encode($object);
}
public static function unserialize($string)
{
$object = json_decode($string);
$action = self::create($object->actionName, $object->nodeName);
foreach ($object->properties as $key => $val) {
$func = 'set' . ucfirst($key);
$action->$func($val);
}
return $action;
}
public function getActionName()
{
if ($this->actionName === null) {
if (! preg_match('/\\\Node(\w+)Action$/', get_class($this), $m)) {
throw new ProgrammingError(
'"%s" is not a NodeAction class',
get_class($this)
);
}
$this->actionName = lcfirst($m[1]);
}
return $this->actionName;
}
}

View file

@ -0,0 +1,69 @@
<?php
namespace Icinga\Module\Businessprocess;
class NodeCreateAction extends NodeAction
{
protected $parentName;
protected $properties = array();
protected $preserveProperties = array('parentName', 'properties');
public function setParent(Node $name)
{
$this->parentName = $name;
}
public function hasParent()
{
return $this->parentName !== null;
}
public function getParentName()
{
return $this->parentName;
}
public function setParentName($name)
{
$this->parentName = $name;
}
public function getProperties()
{
return $this->properties;
}
public function setProperties($properties)
{
$this->properties = $properties;
return $this;
}
public function appliesTo(BusinessProcess $bp)
{
return ! $bp->hasNode($this->getNodeName());
}
public function applyTo(BusinessProcess $bp)
{
$node = new BpNode($bp, (object) array(
'name' => $this->getNodeName(),
'operator' => $this->properties->operator,
'child_names' => $this->properties->childNames
));
foreach ($this->properties as $key => $val) {
$func = 'set' . ucfirst($key);
$node->$func($val);
}
$bp->addNode($this->getNodeName(), $node);
if ($this->hasParent()) {
$node->addParent($bp->getNode($this->getParentName()));
}
return $node;
}
}

View file

@ -0,0 +1,86 @@
<?php
namespace Icinga\Module\Businessprocess;
class NodeModifyAction extends NodeAction
{
protected $properties = array();
protected $formerProperties = array();
protected $preserveProperties = array('formerProperties', 'properties');
// Can be called multiple times
public function setNodeProperties(Node $node, $properties)
{
$old = array();
foreach (array_keys($properties) as $key) {
$this->properties[$key] = $properties[$key];
if (array_key_exists($key, $this->formerProperties)) {
continue;
}
$func = 'get' . ucfirst($key);
$this->formerProperties[$key] = $node->$func();
}
return $this;
}
public function appliesTo(BusinessProcess $bp)
{
$name = $this->getNodeName();
if (! $bp->hasNode($name)) {
return false;
}
$node = $bp->getNode($name);
foreach ($this->properties as $key => $val) {
$func = 'get' . ucfirst($key);
if ($this->formerProperties[$key] !== $node->$func()) {
return false;
}
}
return true;
}
public function applyTo(BusinessProcess $bp)
{
$node = $bp->getNode($this->getNodeName());
foreach ($this->properties as $key => $val) {
$func = 'set' . ucfirst($key);
$node->$func($val);
}
return $this;
}
public function setProperties($properties)
{
$this->properties = $properties;
return $this;
}
public function setFormerProperties($properties)
{
$this->formerProperties = $properties;
return $this;
}
public function getProperties()
{
return $this->properties;
}
public function getFormerProperties()
{
return $this->formerProperties;
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace Icinga\Module\Businessprocess;
class NodeRemoveAction extends NodeAction
{
public function appliesTo(BusinessProcess $bp)
{
return $bp->hasNode($this->getNodeName());
}
public function applyTo(BusinessProcess $bp)
{
$bp->removeNode($this->getNodeName());
}
}

View file

@ -0,0 +1,143 @@
<?php
namespace Icinga\Module\Businessprocess;
use Icinga\Web\Session\SessionNamespace as Session;
use Icinga\Module\Businessprocess\NodeAction;
class ProcessChanges
{
protected $changes = array();
protected $session;
protected $hasBeenModified = false;
protected $sessionKey;
private function __construct()
{
}
public static function construct(BusinessProcess $bp, Session $session)
{
$key = 'changes.' . $bp->getName();
$changes = new ProcessChanges();
$changes->sessionKey = $key;
if ($actions = $session->get($key)) {
foreach ($actions as $string) {
$changes->push(NodeAction::unserialize($string));
}
}
$changes->session = $session;
return $changes;
}
public function modifyNode(Node $node, $properties)
{
$action = new NodeModifyAction($node);
$action->setNodeProperties($node, $properties);
return $this->push($action);
}
public function createNode($nodeName, $properties, Node $parent = null)
{
$action = new NodeCreateAction($nodeName);
$action->setProperties($properties);
if ($parent !== null) {
$action->setParent($parent);
}
return $this->push($action);
}
public function deleteNode(Node $node)
{
return $this->push(new NodeDeleteAction($node));
}
public function push(NodeAction $change)
{
$this->changes[] = $change;
$this->hasBeenModified = true;
return $this;
}
public function getChanges()
{
return $this->changes;
}
public function clear()
{
$this->hasBeenModified = true;
$this->changes = array();
$this->session->set($this->getSessionKey(), null);
return $this;
}
public function isEmpty()
{
return $this->count() === 0;
}
public function count()
{
return count($this->changes);
}
public function shift()
{
if ($this->isEmpty()) {
return false;
}
$this->hasBeenModified = true;
return array_shift($this->changes);
}
public function pop()
{
if ($this->isEmpty()) {
return false;
}
$this->hasBeenModified = true;
return array_pop($this->changes);
}
protected function getSessionKey()
{
return $this->sessionKey;
}
protected function hasBeenModified()
{
return $this->hasBeenModified;
}
public function serialize()
{
$serialized = array();
foreach ($this->getChanges() as $change) {
$serialized[] = $change->serialize();
}
return $serialized;
}
public function __destruct()
{
if (! $this->hasBeenModified()) {
unset($this->session);
return;
}
$session = $this->session;
$key = $this->getSessionKey();
if (! $this->isEmpty()) {
$session->set($key, $this->serialize());
}
unset($this->session);
}
}

View file

@ -2,9 +2,12 @@
namespace Icinga\Module\Businessprocess;
use Icinga\Web\Url;
class ServiceNode extends Node
{
protected $hostname;
protected $service;
public function __construct(BusinessProcess $bp, $object)
@ -22,18 +25,40 @@ class ServiceNode extends Node
public function renderLink($view)
{
if ($this->bp->isSimulationMode()) {
return $view->qlink($this->getAlias(), 'businessprocess/node/simulate', array(
'node' => $this->name,
'processName' => $this->bp->getName()
));
} else {
return $view->qlink($this->getAlias(), 'monitoring/service/show', array(
'host' => $this->getHostname(),
'service' => $this->getServiceDescription(),
'processName' => $this->bp->getName()
));
if ($this->isMissing()) {
return '<span class="missing">' . $view->escape($this->getAlias()) . '</span>';
}
$params = array(
'host' => $this->getHostname(),
'service' => $this->getServiceDescription()
);
if ($this->bp->hasBackendName()) {
$params['backend'] = $this->bp->getBackendName();
}
return $view->qlink($this->getAlias(), 'monitoring/service/show', $params);
}
protected function getActionIcons($view)
{
$icons = array();
if (! $this->bp->isLocked()) {
$url = Url::fromPath( 'businessprocess/node/simulate', array(
'config' => $this->bp->getName(),
'node' => $this->name
));
$icons[] = $this->actionIcon(
$view,
'magic',
$url,
'Simulation'
);
}
return $icons;
}
public function getHostname()

View file

@ -0,0 +1,106 @@
<?php
namespace Icinga\Module\Businessprocess;
use Icinga\Web\Session\SessionNamespace;
class Simulation
{
/**
* @var Session
*/
protected $session;
/**
* @var BusinessProcess
*/
protected $bp;
/**
* @var string
*/
protected $key;
protected $simulations;
public function __construct(BusinessProcess $bp, SessionNamespace $session)
{
$this->bp = $bp;
$this->session = $session;
$this->key = 'simulations.' . $bp->getName();
}
public function simulations()
{
if ($this->simulations === null) {
$this->simulations = $this->fetchSimulations();
}
return $this->simulations;
}
protected function setSimulations($simulations)
{
$this->simulations = $simulations;
$this->session->set($this->key, $simulations);
return $this;
}
protected function fetchSimulations()
{
return $this->session->get($this->key, array());
}
public function clear()
{
$this->simulations = array();
$this->session->set($this->key, array());
}
public function count()
{
return count($this->simulations());
}
public function isEmpty()
{
return $this->count() === 0;
}
public function set($node, $properties)
{
$simulations = $this->simulations();
$simulations[$node] = $properties;
$this->setSimulations($simulations);
}
public function hasNode($name)
{
$simulations = $this->simulations();
return array_key_exists($name, $simulations);
}
public function getNode($name)
{
$simulations = $this->simulations();
if (! array_key_exists($name, $simulations)) {
throw new ProgrammingError('Trying to access invalid node %s', $name);
}
return $simulations[$name];
}
public function remove($node)
{
$simulations = $this->simulations();
if (array_key_exists($node, $simulations)) {
unset($simulations[$node]);
$this->setSimulations($simulations);
return true;
} else {
return false;
}
}
}

View file

@ -10,6 +10,7 @@ use Icinga\Module\Businessprocess\BpNode;
use Icinga\Module\Businessprocess\Storage\Storage;
use DirectoryIterator;
use Icinga\Exception\SystemPermissionException;
use Icinga\Application\Benchmark;
class LegacyStorage extends Storage
{
@ -61,7 +62,11 @@ class LegacyStorage extends Storage
if (substr($filename, -5) === '.conf') {
$name = substr($filename, 0, -5);
$header = $this->readHeader($file->getPathname(), $name);
$files[$name] = $header['Title'];
if ($header['Title'] === null) {
$files[$name] = $name;
} else {
$files[$name] = sprintf('%s (%s)', $header['Title'], $name);
}
}
}
return $files;
@ -72,7 +77,7 @@ class LegacyStorage extends Storage
$fh = fopen($file, 'r');
$cnt = 0;
$header = array(
'Title' => $name,
'Title' => null,
'Owner' => null,
'Backend' => null,
'Statetype' => 'soft',
@ -101,6 +106,11 @@ class LegacyStorage extends Storage
);
}
public function getSource($name)
{
return file_get_contents($this->getFilename($name));
}
public function getFilename($name)
{
return $this->getConfigDir() . '/' . $name . '.conf';
@ -115,15 +125,22 @@ class LegacyStorage extends Storage
return $bp;
}
public function deleteProcess($name)
{
unlink($this->getFilename($name));
}
/**
* @return BusinessProcess
*/
public function loadProcess($name)
{
Benchmark::measure('Loading business process ' . $name);
$bp = new BusinessProcess();
$bp->setName($name);
$this->parseFile($name, $bp);
$this->loadHeader($name, $bp);
Benchmark::measure('Business process ' . $name . ' loaded');
return $bp;
}
@ -134,7 +151,10 @@ class LegacyStorage extends Storage
$header = $this->readHeader($file, $name);
$bp->setTitle($header['Title']);
if ($header['Backend']) {
$bp->loadBackendByName($header['Backend']);
$bp->setBackendName($header['Backend']);
}
if ($header['Statetype'] === 'soft') {
$bp->useSoftStates();
}
}

View file

@ -32,4 +32,6 @@ abstract class Storage
/**
*/
abstract public function storeProcess(BusinessProcess $name);
abstract public function deleteProcess($name);
}

688
library/vendor/PHP-FineDiff/finediff.php vendored Normal file
View file

@ -0,0 +1,688 @@
<?php
/**
* FINE granularity DIFF
*
* Computes a set of instructions to convert the content of
* one string into another.
*
* Copyright (c) 2011 Raymond Hill (http://raymondhill.net/blog/?p=441)
*
* Licensed under The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @copyright Copyright 2011 (c) Raymond Hill (http://raymondhill.net/blog/?p=441)
* @link http://www.raymondhill.net/finediff/
* @version 0.6
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
/**
* Usage (simplest):
*
* include 'finediff.php';
*
* // for the stock stack, granularity values are:
* // FineDiff::$paragraphGranularity = paragraph/line level
* // FineDiff::$sentenceGranularity = sentence level
* // FineDiff::$wordGranularity = word level
* // FineDiff::$characterGranularity = character level [default]
*
* $opcodes = FineDiff::getDiffOpcodes($from_text, $to_text [, $granularityStack = null] );
* // store opcodes for later use...
*
* ...
*
* // restore $to_text from $from_text + $opcodes
* include 'finediff.php';
* $to_text = FineDiff::renderToTextFromOpcodes($from_text, $opcodes);
*
* ...
*/
/**
* Persisted opcodes (string) are a sequence of atomic opcode.
* A single opcode can be one of the following:
* c | c{n} | d | d{n} | i:{c} | i{length}:{s}
* 'c' = copy one character from source
* 'c{n}' = copy n characters from source
* 'd' = skip one character from source
* 'd{n}' = skip n characters from source
* 'i:{c} = insert character 'c'
* 'i{n}:{s}' = insert string s, which is of length n
*
* Do not exist as of now, under consideration:
* 'm{n}:{o} = move n characters from source o characters ahead.
* It would be essentially a shortcut for a delete->copy->insert
* command (swap) for when the inserted segment is exactly the same
* as the deleted one, and with only a copy operation in between.
* TODO: How often this case occurs? Is it worth it? Can only
* be done as a postprocessing method (->optimize()?)
*/
abstract class FineDiffOp {
abstract public function getFromLen();
abstract public function getToLen();
abstract public function getOpcode();
}
class FineDiffDeleteOp extends FineDiffOp {
public function __construct($len) {
$this->fromLen = $len;
}
public function getFromLen() {
return $this->fromLen;
}
public function getToLen() {
return 0;
}
public function getOpcode() {
if ( $this->fromLen === 1 ) {
return 'd';
}
return "d{$this->fromLen}";
}
}
class FineDiffInsertOp extends FineDiffOp {
public function __construct($text) {
$this->text = $text;
}
public function getFromLen() {
return 0;
}
public function getToLen() {
return strlen($this->text);
}
public function getText() {
return $this->text;
}
public function getOpcode() {
$to_len = strlen($this->text);
if ( $to_len === 1 ) {
return "i:{$this->text}";
}
return "i{$to_len}:{$this->text}";
}
}
class FineDiffReplaceOp extends FineDiffOp {
public function __construct($fromLen, $text) {
$this->fromLen = $fromLen;
$this->text = $text;
}
public function getFromLen() {
return $this->fromLen;
}
public function getToLen() {
return strlen($this->text);
}
public function getText() {
return $this->text;
}
public function getOpcode() {
if ( $this->fromLen === 1 ) {
$del_opcode = 'd';
}
else {
$del_opcode = "d{$this->fromLen}";
}
$to_len = strlen($this->text);
if ( $to_len === 1 ) {
return "{$del_opcode}i:{$this->text}";
}
return "{$del_opcode}i{$to_len}:{$this->text}";
}
}
class FineDiffCopyOp extends FineDiffOp {
public function __construct($len) {
$this->len = $len;
}
public function getFromLen() {
return $this->len;
}
public function getToLen() {
return $this->len;
}
public function getOpcode() {
if ( $this->len === 1 ) {
return 'c';
}
return "c{$this->len}";
}
public function increase($size) {
return $this->len += $size;
}
}
/**
* FineDiff ops
*
* Collection of ops
*/
class FineDiffOps {
public function appendOpcode($opcode, $from, $from_offset, $from_len) {
if ( $opcode === 'c' ) {
$edits[] = new FineDiffCopyOp($from_len);
}
else if ( $opcode === 'd' ) {
$edits[] = new FineDiffDeleteOp($from_len);
}
else /* if ( $opcode === 'i' ) */ {
$edits[] = new FineDiffInsertOp(substr($from, $from_offset, $from_len));
}
}
public $edits = array();
}
/**
* FineDiff class
*
* TODO: Document
*
*/
class FineDiff {
/**------------------------------------------------------------------------
*
* Public section
*
*/
/**
* Constructor
* ...
* The $granularityStack allows FineDiff to be configurable so that
* a particular stack tailored to the specific content of a document can
* be passed.
*/
public function __construct($from_text = '', $to_text = '', $granularityStack = null) {
// setup stack for generic text documents by default
$this->granularityStack = $granularityStack ? $granularityStack : FineDiff::$characterGranularity;
$this->edits = array();
$this->from_text = $from_text;
$this->doDiff($from_text, $to_text);
}
public function getOps() {
return $this->edits;
}
public function getOpcodes() {
$opcodes = array();
foreach ( $this->edits as $edit ) {
$opcodes[] = $edit->getOpcode();
}
return implode('', $opcodes);
}
public function renderDiffToHTML() {
$in_offset = 0;
ob_start();
foreach ( $this->edits as $edit ) {
$n = $edit->getFromLen();
if ( $edit instanceof FineDiffCopyOp ) {
FineDiff::renderDiffToHTMLFromOpcode('c', $this->from_text, $in_offset, $n);
}
else if ( $edit instanceof FineDiffDeleteOp ) {
FineDiff::renderDiffToHTMLFromOpcode('d', $this->from_text, $in_offset, $n);
}
else if ( $edit instanceof FineDiffInsertOp ) {
FineDiff::renderDiffToHTMLFromOpcode('i', $edit->getText(), 0, $edit->getToLen());
}
else /* if ( $edit instanceof FineDiffReplaceOp ) */ {
FineDiff::renderDiffToHTMLFromOpcode('d', $this->from_text, $in_offset, $n);
FineDiff::renderDiffToHTMLFromOpcode('i', $edit->getText(), 0, $edit->getToLen());
}
$in_offset += $n;
}
return ob_get_clean();
}
/**------------------------------------------------------------------------
* Return an opcodes string describing the diff between a "From" and a
* "To" string
*/
public static function getDiffOpcodes($from, $to, $granularities = null) {
$diff = new FineDiff($from, $to, $granularities);
return $diff->getOpcodes();
}
/**------------------------------------------------------------------------
* Return an iterable collection of diff ops from an opcodes string
*/
public static function getDiffOpsFromOpcodes($opcodes) {
$diffops = new FineDiffOps();
FineDiff::renderFromOpcodes(null, $opcodes, array($diffops,'appendOpcode'));
return $diffops->edits;
}
/**------------------------------------------------------------------------
* Re-create the "To" string from the "From" string and an "Opcodes" string
*/
public static function renderToTextFromOpcodes($from, $opcodes) {
ob_start();
FineDiff::renderFromOpcodes($from, $opcodes, array('FineDiff','renderToTextFromOpcode'));
return ob_get_clean();
}
/**------------------------------------------------------------------------
* Render the diff to an HTML string
*/
public static function renderDiffToHTMLFromOpcodes($from, $opcodes) {
ob_start();
FineDiff::renderFromOpcodes($from, $opcodes, array('FineDiff','renderDiffToHTMLFromOpcode'));
return ob_get_clean();
}
/**------------------------------------------------------------------------
* Generic opcodes parser, user must supply callback for handling
* single opcode
*/
public static function renderFromOpcodes($from, $opcodes, $callback) {
if ( !is_callable($callback) ) {
return;
}
$opcodes_len = strlen($opcodes);
$from_offset = $opcodes_offset = 0;
while ( $opcodes_offset < $opcodes_len ) {
$opcode = substr($opcodes, $opcodes_offset, 1);
$opcodes_offset++;
$n = intval(substr($opcodes, $opcodes_offset));
if ( $n ) {
$opcodes_offset += strlen(strval($n));
}
else {
$n = 1;
}
if ( $opcode === 'c' ) { // copy n characters from source
call_user_func($callback, 'c', $from, $from_offset, $n, '');
$from_offset += $n;
}
else if ( $opcode === 'd' ) { // delete n characters from source
call_user_func($callback, 'd', $from, $from_offset, $n, '');
$from_offset += $n;
}
else /* if ( $opcode === 'i' ) */ { // insert n characters from opcodes
call_user_func($callback, 'i', $opcodes, $opcodes_offset + 1, $n);
$opcodes_offset += 1 + $n;
}
}
}
/**
* Stock granularity stacks and delimiters
*/
const paragraphDelimiters = "\n\r";
public static $paragraphGranularity = array(
FineDiff::paragraphDelimiters
);
const sentenceDelimiters = ".\n\r";
public static $sentenceGranularity = array(
FineDiff::paragraphDelimiters,
FineDiff::sentenceDelimiters
);
const wordDelimiters = " \t.\n\r";
public static $wordGranularity = array(
FineDiff::paragraphDelimiters,
FineDiff::sentenceDelimiters,
FineDiff::wordDelimiters
);
const characterDelimiters = "";
public static $characterGranularity = array(
FineDiff::paragraphDelimiters,
FineDiff::sentenceDelimiters,
FineDiff::wordDelimiters,
FineDiff::characterDelimiters
);
public static $textStack = array(
".",
" \t.\n\r",
""
);
/**------------------------------------------------------------------------
*
* Private section
*
*/
/**
* Entry point to compute the diff.
*/
private function doDiff($from_text, $to_text) {
$this->last_edit = false;
$this->stackpointer = 0;
$this->from_text = $from_text;
$this->from_offset = 0;
// can't diff without at least one granularity specifier
if ( empty($this->granularityStack) ) {
return;
}
$this->_processGranularity($from_text, $to_text);
}
/**
* This is the recursive function which is responsible for
* handling/increasing granularity.
*
* Incrementally increasing the granularity is key to compute the
* overall diff in a very efficient way.
*/
private function _processGranularity($from_segment, $to_segment) {
$delimiters = $this->granularityStack[$this->stackpointer++];
$has_next_stage = $this->stackpointer < count($this->granularityStack);
foreach ( FineDiff::doFragmentDiff($from_segment, $to_segment, $delimiters) as $fragment_edit ) {
// increase granularity
if ( $fragment_edit instanceof FineDiffReplaceOp && $has_next_stage ) {
$this->_processGranularity(
substr($this->from_text, $this->from_offset, $fragment_edit->getFromLen()),
$fragment_edit->getText()
);
}
// fuse copy ops whenever possible
else if ( $fragment_edit instanceof FineDiffCopyOp && $this->last_edit instanceof FineDiffCopyOp ) {
$this->edits[count($this->edits)-1]->increase($fragment_edit->getFromLen());
$this->from_offset += $fragment_edit->getFromLen();
}
else {
/* $fragment_edit instanceof FineDiffCopyOp */
/* $fragment_edit instanceof FineDiffDeleteOp */
/* $fragment_edit instanceof FineDiffInsertOp */
$this->edits[] = $this->last_edit = $fragment_edit;
$this->from_offset += $fragment_edit->getFromLen();
}
}
$this->stackpointer--;
}
/**
* This is the core algorithm which actually perform the diff itself,
* fragmenting the strings as per specified delimiters.
*
* This function is naturally recursive, however for performance purpose
* a local job queue is used instead of outright recursivity.
*/
private static function doFragmentDiff($from_text, $to_text, $delimiters) {
// Empty delimiter means character-level diffing.
// In such case, use code path optimized for character-level
// diffing.
if ( empty($delimiters) ) {
return FineDiff::doCharDiff($from_text, $to_text);
}
$result = array();
// fragment-level diffing
$from_text_len = strlen($from_text);
$to_text_len = strlen($to_text);
$from_fragments = FineDiff::extractFragments($from_text, $delimiters);
$to_fragments = FineDiff::extractFragments($to_text, $delimiters);
$jobs = array(array(0, $from_text_len, 0, $to_text_len));
$cached_array_keys = array();
while ( $job = array_pop($jobs) ) {
// get the segments which must be diff'ed
list($from_segment_start, $from_segment_end, $to_segment_start, $to_segment_end) = $job;
// catch easy cases first
$from_segment_length = $from_segment_end - $from_segment_start;
$to_segment_length = $to_segment_end - $to_segment_start;
if ( !$from_segment_length || !$to_segment_length ) {
if ( $from_segment_length ) {
$result[$from_segment_start * 4] = new FineDiffDeleteOp($from_segment_length);
}
else if ( $to_segment_length ) {
$result[$from_segment_start * 4 + 1] = new FineDiffInsertOp(substr($to_text, $to_segment_start, $to_segment_length));
}
continue;
}
// find longest copy operation for the current segments
$best_copy_length = 0;
$from_base_fragment_index = $from_segment_start;
$cached_array_keys_for_current_segment = array();
while ( $from_base_fragment_index < $from_segment_end ) {
$from_base_fragment = $from_fragments[$from_base_fragment_index];
$from_base_fragment_length = strlen($from_base_fragment);
// performance boost: cache array keys
if ( !isset($cached_array_keys_for_current_segment[$from_base_fragment]) ) {
if ( !isset($cached_array_keys[$from_base_fragment]) ) {
$to_all_fragment_indices = $cached_array_keys[$from_base_fragment] = array_keys($to_fragments, $from_base_fragment, true);
}
else {
$to_all_fragment_indices = $cached_array_keys[$from_base_fragment];
}
// get only indices which falls within current segment
if ( $to_segment_start > 0 || $to_segment_end < $to_text_len ) {
$to_fragment_indices = array();
foreach ( $to_all_fragment_indices as $to_fragment_index ) {
if ( $to_fragment_index < $to_segment_start ) { continue; }
if ( $to_fragment_index >= $to_segment_end ) { break; }
$to_fragment_indices[] = $to_fragment_index;
}
$cached_array_keys_for_current_segment[$from_base_fragment] = $to_fragment_indices;
}
else {
$to_fragment_indices = $to_all_fragment_indices;
}
}
else {
$to_fragment_indices = $cached_array_keys_for_current_segment[$from_base_fragment];
}
// iterate through collected indices
foreach ( $to_fragment_indices as $to_base_fragment_index ) {
$fragment_index_offset = $from_base_fragment_length;
// iterate until no more match
for (;;) {
$fragment_from_index = $from_base_fragment_index + $fragment_index_offset;
if ( $fragment_from_index >= $from_segment_end ) {
break;
}
$fragment_to_index = $to_base_fragment_index + $fragment_index_offset;
if ( $fragment_to_index >= $to_segment_end ) {
break;
}
if ( $from_fragments[$fragment_from_index] !== $to_fragments[$fragment_to_index] ) {
break;
}
$fragment_length = strlen($from_fragments[$fragment_from_index]);
$fragment_index_offset += $fragment_length;
}
if ( $fragment_index_offset > $best_copy_length ) {
$best_copy_length = $fragment_index_offset;
$best_from_start = $from_base_fragment_index;
$best_to_start = $to_base_fragment_index;
}
}
$from_base_fragment_index += strlen($from_base_fragment);
// If match is larger than half segment size, no point trying to find better
// TODO: Really?
if ( $best_copy_length >= $from_segment_length / 2) {
break;
}
// no point to keep looking if what is left is less than
// current best match
if ( $from_base_fragment_index + $best_copy_length >= $from_segment_end ) {
break;
}
}
if ( $best_copy_length ) {
$jobs[] = array($from_segment_start, $best_from_start, $to_segment_start, $best_to_start);
$result[$best_from_start * 4 + 2] = new FineDiffCopyOp($best_copy_length);
$jobs[] = array($best_from_start + $best_copy_length, $from_segment_end, $best_to_start + $best_copy_length, $to_segment_end);
}
else {
$result[$from_segment_start * 4 ] = new FineDiffReplaceOp($from_segment_length, substr($to_text, $to_segment_start, $to_segment_length));
}
}
ksort($result, SORT_NUMERIC);
return array_values($result);
}
/**
* Perform a character-level diff.
*
* The algorithm is quite similar to doFragmentDiff(), except that
* the code path is optimized for character-level diff -- strpos() is
* used to find out the longest common subequence of characters.
*
* We try to find a match using the longest possible subsequence, which
* is at most the length of the shortest of the two strings, then incrementally
* reduce the size until a match is found.
*
* I still need to study more the performance of this function. It
* appears that for long strings, the generic doFragmentDiff() is more
* performant. For word-sized strings, doCharDiff() is somewhat more
* performant.
*/
private static function doCharDiff($from_text, $to_text) {
$result = array();
$jobs = array(array(0, strlen($from_text), 0, strlen($to_text)));
while ( $job = array_pop($jobs) ) {
// get the segments which must be diff'ed
list($from_segment_start, $from_segment_end, $to_segment_start, $to_segment_end) = $job;
$from_segment_len = $from_segment_end - $from_segment_start;
$to_segment_len = $to_segment_end - $to_segment_start;
// catch easy cases first
if ( !$from_segment_len || !$to_segment_len ) {
if ( $from_segment_len ) {
$result[$from_segment_start * 4 + 0] = new FineDiffDeleteOp($from_segment_len);
}
else if ( $to_segment_len ) {
$result[$from_segment_start * 4 + 1] = new FineDiffInsertOp(substr($to_text, $to_segment_start, $to_segment_len));
}
continue;
}
if ( $from_segment_len >= $to_segment_len ) {
$copy_len = $to_segment_len;
while ( $copy_len ) {
$to_copy_start = $to_segment_start;
$to_copy_start_max = $to_segment_end - $copy_len;
while ( $to_copy_start <= $to_copy_start_max ) {
$from_copy_start = strpos(substr($from_text, $from_segment_start, $from_segment_len), substr($to_text, $to_copy_start, $copy_len));
if ( $from_copy_start !== false ) {
$from_copy_start += $from_segment_start;
break 2;
}
$to_copy_start++;
}
$copy_len--;
}
}
else {
$copy_len = $from_segment_len;
while ( $copy_len ) {
$from_copy_start = $from_segment_start;
$from_copy_start_max = $from_segment_end - $copy_len;
while ( $from_copy_start <= $from_copy_start_max ) {
$to_copy_start = strpos(substr($to_text, $to_segment_start, $to_segment_len), substr($from_text, $from_copy_start, $copy_len));
if ( $to_copy_start !== false ) {
$to_copy_start += $to_segment_start;
break 2;
}
$from_copy_start++;
}
$copy_len--;
}
}
// match found
if ( $copy_len ) {
$jobs[] = array($from_segment_start, $from_copy_start, $to_segment_start, $to_copy_start);
$result[$from_copy_start * 4 + 2] = new FineDiffCopyOp($copy_len);
$jobs[] = array($from_copy_start + $copy_len, $from_segment_end, $to_copy_start + $copy_len, $to_segment_end);
}
// no match, so delete all, insert all
else {
$result[$from_segment_start * 4] = new FineDiffReplaceOp($from_segment_len, substr($to_text, $to_segment_start, $to_segment_len));
}
}
ksort($result, SORT_NUMERIC);
return array_values($result);
}
/**
* Efficiently fragment the text into an array according to
* specified delimiters.
* No delimiters means fragment into single character.
* The array indices are the offset of the fragments into
* the input string.
* A sentinel empty fragment is always added at the end.
* Careful: No check is performed as to the validity of the
* delimiters.
*/
private static function extractFragments($text, $delimiters) {
// special case: split into characters
if ( empty($delimiters) ) {
$chars = str_split($text, 1);
$chars[strlen($text)] = '';
return $chars;
}
$fragments = array();
$start = $end = 0;
for (;;) {
$end += strcspn($text, $delimiters, $end);
$end += strspn($text, $delimiters, $end);
if ( $end === $start ) {
break;
}
$fragments[$start] = substr($text, $start, $end - $start);
$start = $end;
}
$fragments[$start] = '';
return $fragments;
}
/**
* Stock opcode renderers
*/
private static function renderToTextFromOpcode($opcode, $from, $from_offset, $from_len) {
if ( $opcode === 'c' || $opcode === 'i' ) {
echo substr($from, $from_offset, $from_len);
}
}
private static function renderDiffToHTMLFromOpcode($opcode, $from, $from_offset, $from_len) {
if ( $opcode === 'c' ) {
echo htmlentities(substr($from, $from_offset, $from_len));
}
else if ( $opcode === 'd' ) {
$deletion = substr($from, $from_offset, $from_len);
if ( strcspn($deletion, " \n\r") === 0 ) {
$deletion = str_replace(array("\n","\r"), array('\n','\r'), $deletion);
}
echo '<del>', htmlentities($deletion), '</del>';
}
else /* if ( $opcode === 'i' ) */ {
echo '<ins>', htmlentities(substr($from, $from_offset, $from_len)), '</ins>';
}
}
}

View file

@ -9,6 +9,10 @@ h1 {
font-weight: normal;
}
.controls h1 form {
display: inline;
}
h1 a:focus {
outline: none;
}
@ -52,11 +56,6 @@ table.bp {
}
}
/* We try our best to avoid flickering */
table.bp th, table.bp td a {
/* SHIT margin-bottom: 5px; */
}
table.bp th {
font-weight: bold;
}
@ -123,7 +122,7 @@ table.bp.process {
}
}
table.bp th > a, table.bp td > a {
table.bp th > a, table.bp td > a, table.bp td > span {
display: block;
text-decoration: none;
}
@ -174,26 +173,28 @@ table.bp {
border-color: transparent;
}
table.bp tr, table.bp tbody, table.bp th, table.bp td, table.bp.node td > a {
table.bp tr, table.bp tbody, table.bp th, table.bp td, table.bp.node td > a, table.node.missing td > span {
border-width: 0;
border-style: inherit;
border-color: inherit;
}
table.bp td > a {
table.bp td > a, table.node.missing td > span {
line-height: 2em;
padding-left: 0.5em;
display: block;
}
table.bp.node td > a:last-child {
table.bp.node td > a:last-child, table.node.missing td > span {
padding-left: 2.5em;
background-repeat: no-repeat;
background-position: 0.5em 0.5em;
border-left-width: 0.8em;
}
table.bp.node.handled td > a:last-child, table.bp.node.ok td > a:last-child {
table.bp.node.handled td > a:last-child, table.bp.node.ok td > a:last-child,
table.node.missing td > span
{
border-left-width: 0.3em;
background-position: 1em 0.5em;
padding-left: 3em;
@ -214,6 +215,7 @@ table.bp.operator > tbody > tr:first-child > * {
border-top-width: 1px;
border-top-style: dotted;
}
table.bp.operator.hovered > tbody > tr:first-child > * {
border-top-style: solid;
}
@ -227,6 +229,7 @@ table.bp {
&.critical.handled { border-color: @colorCriticalHandled; }
&.unknown { border-color: @colorUnknown; }
&.unknown.handled { border-color: @colorUnknownHandled; }
&.pending { border-color: @colorPending; }
&.missing { border-color: #ccc; }
&.hovered {
&.ok > tbody > tr > {
@ -250,8 +253,11 @@ table.bp {
&.unknown.handled > tbody > tr > {
th, td > a { background-color: @colorCUnknownHandled; }
}
&.pending > tbody > tr > {
th, td > a { background-color: @colorPending; }
}
&.missing > tbody > tr > {
th, td > a { background-color: #ccc; }
th, td > a, td > span { background-color: #ccc; }
}
}
}
@ -275,7 +281,7 @@ table.bp {
-o-transition: @val1, @val2;
-webkit-transition: @val1, @val2;
}
> tbody > tr > td > a:last-child, > tbody > tr > th {
> tbody > tr > td > a:last-child, > tbody > tr > td > span, > tbody > tr > th {
// Fade out
.transition(color 0.5s 0.1s step-start,
background-color 0.5s 0.1s ease-out
@ -283,7 +289,7 @@ table.bp {
}
&.hovered > tbody > tr {
> td > a:last-child, > th {
> td > a:last-child, > td > span, > th {
// Fade in
.transition(color 0.0s 0.0s step-start,
background-color 0.0s 0.0s ease-out
@ -401,4 +407,47 @@ ul.error {
color: white;
padding: 0.3em 0.8em;
}
li a {
color: inherit;
}
}
table.sourcecode {
font-family: monospace;
white-space: pre-wrap;
th {
vertical-align: top;
padding-right: 0.5em;
user-select: none;
-moz-user-select: none;
-o-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
font-weight: bold;
}
td {
vertical-align: top;
}
}
.diff {
font-family: monospace;
white-space: pre-wrap;
del, ins {
text-decoration: none;
}
del {
color: @colorCritical;
background-color: #fdd;
}
ins {
color: @colorOk;
background-color: #dfd;
}
}

View file

@ -29,6 +29,7 @@
this.module.on('mouseenter', 'table.bp > tbody > tr > td > a', this.procMouseOver);
this.module.on('mouseenter', 'table.bp > tbody > tr > th', this.procMouseOver);
this.module.on('mouseenter', 'table.node.missing > tbody > tr > td > span', this.procMouseOver);
this.module.on('mouseleave', 'div.bp', this.procMouseOut);
this.module.icinga.logger.debug('BP module loaded');