2019-01-10 12:11:30 +01:00

474 lines
15 KiB
PHP

<?php
namespace Icinga\Module\Director\Controllers;
use Icinga\Module\Director\Forms\SyncCheckForm;
use Icinga\Module\Director\Forms\SyncPropertyForm;
use Icinga\Module\Director\Forms\SyncRuleForm;
use Icinga\Module\Director\Forms\SyncRunForm;
use Icinga\Module\Director\Import\Sync;
use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\Web\ActionBar\AutomationObjectActionBar;
use Icinga\Module\Director\Web\Controller\ActionController;
use Icinga\Module\Director\Objects\SyncRule;
use Icinga\Module\Director\Objects\SyncRun;
use Icinga\Module\Director\Web\Form\CloneSyncRuleForm;
use Icinga\Module\Director\Web\Table\SyncpropertyTable;
use Icinga\Module\Director\Web\Table\SyncRunTable;
use Icinga\Module\Director\Web\Tabs\SyncRuleTabs;
use Icinga\Module\Director\Web\Widget\SyncRunDetails;
use dipl\Html\Html;
use dipl\Html\Link;
class SyncruleController extends ActionController
{
/**
* @throws \Icinga\Exception\NotFoundError
*/
public function indexAction()
{
$this->setAutoRefreshInterval(10);
$rule = $this->requireSyncRule();
$this->tabs(new SyncRuleTabs($rule))->activate('show');
$ruleName = $rule->get('rule_name');
$this->addTitle($this->translate('Sync rule: %s'), $ruleName);
$checkForm = SyncCheckForm::load()->setSyncRule($rule)->handleRequest();
$runForm = SyncRunForm::load()->setSyncRule($rule)->handleRequest();
if ($lastRunId = $rule->getLastSyncRunId()) {
$run = SyncRun::load($lastRunId, $this->db());
} else {
$run = null;
}
$c = $this->content();
$c->add(Html::tag('p', null, $rule->get('description')));
if (! $rule->hasSyncProperties()) {
$this->addPropertyHint($rule);
return;
}
$this->addMainActions();
if (! $run) {
$this->warning($this->translate('This Sync Rule has never been run before.'));
}
switch ($rule->get('sync_state')) {
case 'unknown':
$c->add(Html::tag('p', null, $this->translate(
"It's currently unknown whether we are in sync with this rule."
. ' You should either check for changes or trigger a new Sync Run.'
)));
break;
case 'in-sync':
$c->add(Html::tag('p', null, sprintf(
$this->translate('This Sync Rule was last found to by in Sync at %s.'),
$rule->get('last_attempt')
)));
/*
TODO: check whether...
- there have been imports since then, differing from former ones
- there have been activities since then
*/
break;
case 'pending-changes':
$this->warning($this->translate(
'There are pending changes for this Sync Rule. You should trigger a new'
. ' Sync Run.'
));
break;
case 'failing':
$this->error(sprintf(
$this->translate(
'This Sync Rule failed when last checked at %s: %s'
),
$rule->get('last_attempt'),
$rule->get('last_error_message')
));
break;
}
$c->add($checkForm);
$c->add($runForm);
if ($run) {
$c->add(Html::tag('h3', null, $this->translate('Last sync run details')));
$c->add(new SyncRunDetails($run));
if ($run->get('rule_name') !== $ruleName) {
$c->add(Html::tag('p', null, sprintf(
$this->translate("It has been renamed since then, its former name was %s"),
$run->get('rule_name')
)));
}
}
}
/**
* @param SyncRule $rule
*/
protected function addPropertyHint(SyncRule $rule)
{
$this->warning(Html::sprintf(
$this->translate('You must define some %s before you can run this Sync Rule'),
new Link(
$this->translate('Sync Properties'),
'director/syncrule/property',
['rule_id' => $rule->get('id')]
)
));
}
/**
* @param $msg
*/
protected function warning($msg)
{
$this->content()->add(Html::tag('p', ['class' => 'warning'], $msg));
}
/**
* @param $msg
*/
protected function error($msg)
{
$this->content()->add(Html::tag('p', ['class' => 'error'], $msg));
}
public function addAction()
{
$this->editAction();
}
/**
* @throws \Icinga\Exception\NotFoundError
* @throws \Exception
*/
public function previewAction()
{
$rule = $this->requireSyncRule();
// $rule->set('update_policy', 'replace');
$this->tabs(new SyncRuleTabs($rule))->activate('preview');
$this->addTitle('Sync Preview');
$sync = new Sync($rule);
$modifications = $sync->getExpectedModifications();
$create = [];
$modify = [];
$delete = [];
$modifiedProperties = [];
/** @var IcingaObject $object */
foreach ($modifications as $object) {
if ($object->hasBeenLoadedFromDb()) {
if ($object->shouldBeRemoved()) {
$delete[] = $object;
} else {
$modify[] = $object;
foreach ($object->getModifiedProperties() as $property => $value) {
if (isset($modifiedProperties[$property])) {
$modifiedProperties[$property]++;
} else {
$modifiedProperties[$property] = 1;
}
}
if (! $object instanceof IcingaObject) {
continue;
}
if ($object->hasModifiedGroups()) {
if (isset($modifiedProperties['groups'])) {
$modifiedProperties['groups']++;
} else {
$modifiedProperties['groups'] = 1;
}
}
if ($object->supportsCustomVars()) {
if ($object->vars()->hasBeenModified()) {
foreach ($object->vars() as $var) {
if ($var->isNew()) {
$varName = 'add vars.' . $var->getKey();
} elseif ($var->hasBeenDeleted()) {
$varName = 'remove vars.' . $var->getKey();
} elseif ($var->hasBeenModified()) {
$varName = 'vars.' . $var->getKey();
} else {
continue;
}
if (isset($modifiedProperties[$varName])) {
$modifiedProperties[$varName]++;
} else {
$modifiedProperties[$varName] = 1;
}
}
}
}
}
} else {
$create[] = $object;
}
}
$content = $this->content();
if (! empty($delete)) {
$content->add([
Html::tag('h2', ['class' => 'icon-cancel action-delete'], sprintf(
$this->translate('%d object(s) will be deleted'),
count($delete)
)),
$this->objectList($delete)
]);
}
if (! empty($modify)) {
$content->add([
Html::tag('h2', ['class' => 'icon-wrench action-modify'], sprintf(
$this->translate('%d object(s) will be modified'),
count($modify)
)),
$this->objectList($modify),
$this->listModifiedProperties($modifiedProperties)
]);
}
if (! empty($create)) {
$content->add([
Html::tag('h2', ['class' => 'icon-plus action-create'], sprintf(
$this->translate('%d object(s) will be created'),
count($create)
)),
$this->objectList($create)
]);
}
}
/**
* @param IcingaObject[] $objects
* @return \dipl\Html\HtmlElement
*/
protected function objectList($objects)
{
return Html::tag('p', $this->firstNames($objects));
}
/**
* @param IcingaObject[] $objects
* @param int $max
* @return string
*/
protected function firstNames($objects, $max = 50)
{
$names = [];
$total = count($objects);
$i = 0;
foreach ($objects as $object) {
$i++;
$names[] = $this->getObjectNameString($object);
if ($i === $max) {
break;
}
}
if ($total > $max) {
return implode(', ', $names) . $this->translate('and %d more');
} else {
return implode(', ', $names);
}
}
protected function listModifiedProperties($properties)
{
$parts = [];
foreach ($properties as $property => $cnt) {
$parts[] = "${cnt}x $property";
}
return implode(', ', $parts);
}
protected function getObjectNameString($object)
{
if ($object instanceof IcingaObject) {
return $object->getObjectName();
} else {
/** @var \Icinga\Module\Director\Data\Db\DbObject $object */
return json_encode($object->getKeyParams());
}
}
public function editAction()
{
$form = SyncRuleForm::load()
->setListUrl('director/syncrules')
->setDb($this->db());
if ($id = $this->params->get('id')) {
$form->loadObject((int) $id);
/** @var SyncRule $rule */
$rule = $form->getObject();
$this->tabs(new SyncRuleTabs($rule))->activate('edit');
$this->addTitle(sprintf(
$this->translate('Sync rule: %s'),
$rule->rule_name
));
$this->addMainActions();
if (! $rule->hasSyncProperties()) {
$this->addPropertyHint($rule);
}
} else {
$this->addTitle($this->translate('Add sync rule'));
$this->tabs(new SyncRuleTabs())->activate('add');
}
$form->handleRequest();
$this->content()->add($form);
}
/**
* @throws \Icinga\Exception\MissingParameterException
* @throws \Icinga\Exception\NotFoundError
*/
public function cloneAction()
{
$id = $this->params->getRequired('id');
$rule = SyncRule::loadWithAutoIncId((int) $id, $this->db());
$this->tabs()->add('show', [
'url' => 'director/syncrule',
'urlParams' => ['id' => $id],
'label' => $this->translate('Sync rule'),
])->add('clone', [
'url' => 'director/syncrule/clone',
'urlParams' => ['id' => $id],
'label' => $this->translate('Clone'),
])->activate('clone');
$this->addTitle('Clone: %s', $rule->get('rule_name'));
$this->actions()->add(
Link::create(
$this->translate('Modify'),
'director/syncrule/edit',
['id' => $rule->get('id')],
['class' => 'icon-paste']
)
);
$form = new CloneSyncRuleForm($rule);
$this->content()->add($form);
$form->handleRequest($this->getRequest());
}
/**
* @throws \Icinga\Exception\NotFoundError
*/
public function propertyAction()
{
$rule = $this->requireSyncRule('rule_id');
$this->tabs(new SyncRuleTabs($rule))->activate('property');
$this->actions()->add(Link::create(
$this->translate('Add sync property rule'),
'director/syncrule/addproperty',
['rule_id' => $rule->get('id')],
['class' => 'icon-plus']
));
$this->addTitle($this->translate('Sync properties') . ': ' . $rule->get('rule_name'));
SyncpropertyTable::create($rule)
->handleSortPriorityActions($this->getRequest(), $this->getResponse())
->renderTo($this);
}
/**
* @throws \Icinga\Exception\NotFoundError
*/
public function editpropertyAction()
{
$this->addpropertyAction();
}
/**
* @throws \Icinga\Exception\NotFoundError
*/
public function addpropertyAction()
{
$db = $this->db();
$rule = $this->requireSyncRule('rule_id');
$ruleId = (int) $rule->get('id');
$form = SyncPropertyForm::load()->setDb($db);
if ($id = $this->params->get('id')) {
$form->loadObject((int) $id);
$this->addTitle(
$this->translate('Sync "%s": %s'),
$form->getObject()->get('destination_field'),
$rule->get('rule_name')
);
} else {
$this->addTitle(
$this->translate('Add sync property: %s'),
$rule->get('rule_name')
);
}
$form->setRule($rule);
$form->setSuccessUrl('director/syncrule/property', ['rule_id' => $ruleId]);
$this->actions()->add(new Link(
$this->translate('back'),
'director/syncrule/property',
['rule_id' => $ruleId],
['class' => 'icon-left-big']
));
$this->content()->add($form->handleRequest());
$this->tabs(new SyncRuleTabs($rule))->activate('property');
SyncpropertyTable::create($rule)
->handleSortPriorityActions($this->getRequest(), $this->getResponse())
->renderTo($this);
}
/**
* @throws \Icinga\Exception\NotFoundError
*/
public function historyAction()
{
$this->setAutoRefreshInterval(30);
$rule = $this->requireSyncRule();
$this->tabs(new SyncRuleTabs($rule))->activate('history');
$this->addTitle($this->translate('Sync history') . ': ' . $rule->rule_name);
if ($runId = $this->params->get('run_id')) {
$run = SyncRun::load($runId, $this->db());
$this->content()->add(new SyncRunDetails($run));
}
SyncRunTable::create($rule)->renderTo($this);
}
protected function addMainActions()
{
$this->actions(new AutomationObjectActionBar(
$this->getRequest()
));
$source = $this->requireSyncRule();
$this->actions()->add(Link::create(
$this->translate('Add to Basket'),
'director/basket/add',
[
'type' => 'SyncRule',
'names' => $source->getUniqueIdentifier()
],
[
'class' => 'icon-tag',
'data-base-target' => '_next',
]
));
}
/**
* @param string $key
* @return SyncRule
* @throws \Icinga\Exception\NotFoundError
*/
protected function requireSyncRule($key = 'id')
{
$id = $this->params->get($key);
return SyncRule::loadWithAutoIncId($id, $this->db());
}
}