mirror of
https://github.com/Icinga/icingaweb2-module-director.git
synced 2025-07-31 01:34:12 +02:00
Merge branch 'feature/job-scheduling-11627'
This commit is contained in:
commit
7d06c23d48
@ -5,7 +5,7 @@ namespace Icinga\Module\Director\Clicommands;
|
||||
use Icinga\Application\Benchmark;
|
||||
use Icinga\Module\Director\Cli\Command;
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
|
||||
use Icinga\Module\Director\Data\Db\DbObject;
|
||||
use Icinga\Module\Director\Util;
|
||||
|
||||
/**
|
||||
* Generate, show and deploy Icinga 2 configuration
|
||||
|
@ -10,7 +10,7 @@ class CommandController extends ObjectController
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
if ($this->object) {
|
||||
if ($this->object && ! $this->object->isExternal()) {
|
||||
$this->getTabs()->add('arguments', array(
|
||||
'url' => 'director/command/arguments',
|
||||
'urlParams' => array('name' => $this->object->object_name),
|
||||
@ -22,7 +22,10 @@ class CommandController extends ObjectController
|
||||
public function argumentsAction()
|
||||
{
|
||||
$this->getTabs()->activate('arguments');
|
||||
$this->view->title = $this->translate('Command arguments');
|
||||
$this->view->title = sprintf(
|
||||
$this->translate('Command arguments: %s'),
|
||||
$this->object->object_name
|
||||
);
|
||||
|
||||
$this->view->table = $this
|
||||
->loadTable('icingaCommandArgument')
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Icinga\Module\Director\Controllers;
|
||||
|
||||
use Icinga\Module\Director\ConfigDiff;
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
|
||||
use Icinga\Module\Director\Util;
|
||||
use Icinga\Module\Director\Web\Controller\ActionController;
|
||||
@ -173,6 +174,56 @@ class ConfigController extends ActionController
|
||||
);
|
||||
}
|
||||
|
||||
public function diffAction()
|
||||
{
|
||||
$db = $this->db();
|
||||
$this->view->title = $this->translate('Config diff');
|
||||
|
||||
$tabs = $this->getTabs()->add('diff', array(
|
||||
'label' => $this->translate('Config diff'),
|
||||
'url' => $this->getRequest()->getUrl()
|
||||
))->activate('diff');
|
||||
|
||||
$leftSum = $this->view->leftSum = $this->params->get('left');
|
||||
$rightSum = $this->view->rightSum = $this->params->get('right');
|
||||
$left = IcingaConfig::load(Util::hex2binary($leftSum), $db);
|
||||
|
||||
$this->view->configs = $db->enumDeployedConfigs();
|
||||
if ($rightSum === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$right = IcingaConfig::load(Util::hex2binary($rightSum), $db);
|
||||
$this->view->table = $this
|
||||
->loadTable('ConfigFileDiff')
|
||||
->setConnection($this->db())
|
||||
->setLeftChecksum($leftSum)
|
||||
->setRightChecksum($rightSum);
|
||||
}
|
||||
|
||||
public function filediffAction()
|
||||
{
|
||||
$db = $this->db();
|
||||
$leftSum = $this->params->get('left');
|
||||
$rightSum = $this->params->get('right');
|
||||
$filename = $this->view->filename = $this->params->get('file_path');
|
||||
|
||||
$left = IcingaConfig::load(Util::hex2binary($leftSum), $db);
|
||||
$right = IcingaConfig::load(Util::hex2binary($rightSum), $db);
|
||||
|
||||
$leftFile = $left->getFile($filename);
|
||||
$rightFile = $right->getFile($filename);
|
||||
|
||||
$d = ConfigDiff::create($leftFile, $rightFile);
|
||||
|
||||
$this->view->title = sprintf(
|
||||
$this->translate('Config file "%s"'),
|
||||
$filename
|
||||
);
|
||||
|
||||
$this->view->output = $d->renderHtml();
|
||||
}
|
||||
|
||||
protected function overviewTabs()
|
||||
{
|
||||
$this->view->tabs = $this->getTabs()->add(
|
||||
|
@ -137,7 +137,7 @@ class DataController extends ActionController
|
||||
$listId = $list->id;
|
||||
|
||||
$form = $this->view->form = $this->loadForm('directorDatalistentry')
|
||||
->setSuccessUrl('director/data/listentry')
|
||||
->setSuccessUrl('director/data/listentry?list_id=' . $listId)
|
||||
->setList($list)
|
||||
->setDb($this->db());
|
||||
|
||||
|
@ -1,66 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Controllers;
|
||||
|
||||
use Icinga\Module\Director\Web\Controller\ActionController;
|
||||
|
||||
class DatalistentryController extends ActionController
|
||||
{
|
||||
public function addAction()
|
||||
{
|
||||
$this->indexAction();
|
||||
}
|
||||
|
||||
public function editAction()
|
||||
{
|
||||
$this->indexAction(true);
|
||||
}
|
||||
|
||||
public function indexAction($edit = false)
|
||||
{
|
||||
$request = $this->getRequest();
|
||||
|
||||
$listId = $this->params->get('list_id');
|
||||
$this->view->lastId = $listId;
|
||||
|
||||
if ($this->params->get('list_id') && $entryName = $this->params->get('entry_name')) {
|
||||
$edit = true;
|
||||
}
|
||||
|
||||
if ($edit) {
|
||||
$this->view->title = $this->translate('Edit entry');
|
||||
$this->getTabs()->add('editentry', array(
|
||||
'url' => 'director/datalistentry/edit' . '?list_id=' . $listId . '&entry_name=' . $entryName,
|
||||
'label' => $this->view->title,
|
||||
))->activate('editentry');
|
||||
} else {
|
||||
$this->view->title = $this->translate('Add entry');
|
||||
$this->getTabs()->add('addlistentry', array(
|
||||
'url' => 'director/datalistentry/add' . '?list_id=' . $listId,
|
||||
'label' => $this->view->title,
|
||||
))->activate('addlistentry');
|
||||
}
|
||||
|
||||
$form = $this->view->form = $this->loadForm('directorDatalistentry')
|
||||
->setListId($listId)
|
||||
->setSuccessUrl('director/datalistentry' . '?list_id=' . $listId)
|
||||
->setDb($this->db());
|
||||
|
||||
if ($request->isPost()) {
|
||||
$listId = $request->getParam('list_id');
|
||||
$entryName = $request->getParam('entry_name');
|
||||
}
|
||||
|
||||
if ($edit) {
|
||||
$form->loadObject(array('list_id' => $listId, 'entry_name' => $entryName));
|
||||
if ($el = $form->getElement('entry_name')) {
|
||||
// TODO: Doesn't work without setup
|
||||
$el->setAttribs(array('readonly' => true));
|
||||
}
|
||||
}
|
||||
|
||||
$form->handleRequest();
|
||||
|
||||
$this->render('object/form', null, true);
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Icinga\Module\Director\Controllers;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Exception\NotFoundError;
|
||||
use Icinga\Module\Director\Objects\IcingaEndpoint;
|
||||
use Icinga\Module\Director\Objects\IcingaZone;
|
||||
@ -78,7 +79,24 @@ class HostController extends ObjectController
|
||||
$this->view->title = 'Agent deployment instructions';
|
||||
// TODO: Fail when no ticket
|
||||
$this->view->certname = $this->object->object_name;
|
||||
$this->view->ticket = Util::getIcingaTicket($this->view->certname, $this->api()->getTicketSalt());
|
||||
|
||||
try {
|
||||
$this->view->ticket = Util::getIcingaTicket(
|
||||
$this->view->certname,
|
||||
$this->api()->getTicketSalt()
|
||||
);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->view->ticket = 'ERROR';
|
||||
$this->view->error = sprintf(
|
||||
$this->translate(
|
||||
'A ticket for this agent could not have been requested from'
|
||||
. ' your deployment endpoint: %s'
|
||||
),
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
$this->view->master = $this->db()->getDeploymentEndpointName();
|
||||
$this->view->masterzone = $this->db()->getMasterZoneName();
|
||||
$this->view->globalzone = $this->db()->getDefaultGlobalZoneName();
|
||||
@ -95,48 +113,11 @@ class HostController extends ObjectController
|
||||
throw new NotFoundError('The host "%s" is not an agent', $host->object_name);
|
||||
}
|
||||
|
||||
return $this->sendJson(Util::getIcingaTicket($host->object_name, $this->api()->getTicketSalt()));
|
||||
}
|
||||
|
||||
public function renderAction()
|
||||
{
|
||||
$this->renderAgentExtras();
|
||||
return parent::renderAction();
|
||||
}
|
||||
|
||||
protected function renderAgentExtras()
|
||||
{
|
||||
$host = $this->object;
|
||||
$db = $this->db();
|
||||
if ($host->object_type !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($host->getResolvedProperty('has_agent') !== 'y') {
|
||||
return;
|
||||
}
|
||||
|
||||
$name = $host->object_name;
|
||||
if (IcingaEndpoint::exists($name, $db)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$props = array(
|
||||
'object_name' => $name,
|
||||
'object_type' => 'object',
|
||||
'log_duration' => 0
|
||||
);
|
||||
if ($host->getResolvedProperty('master_should_connect') === 'y') {
|
||||
$props['host'] = $host->getResolvedProperty('address');
|
||||
$props['zone_id'] = $host->getResolvedProperty('zone_id');
|
||||
}
|
||||
|
||||
$this->view->extraObjects = array(
|
||||
IcingaEndpoint::create($props),
|
||||
IcingaZone::create(array(
|
||||
'object_name' => $name,
|
||||
'parent' => $db->getMasterZoneName()
|
||||
), $db)->setEndpointList(array($name))
|
||||
return $this->sendJson(
|
||||
Util::getIcingaTicket(
|
||||
$host->object_name,
|
||||
$this->api()->getTicketSalt()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,10 @@
|
||||
namespace Icinga\Module\Director\Controllers;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Module\Director\Db\Migrations;
|
||||
use Icinga\Module\Director\Objects\DirectorJob;
|
||||
use Icinga\Module\Director\Objects\ImportSource;
|
||||
use Icinga\Module\Director\Objects\SyncRule;
|
||||
use Icinga\Module\Director\Web\Controller\ActionController;
|
||||
|
||||
class IndexController extends ActionController
|
||||
@ -29,9 +33,98 @@ class IndexController extends ActionController
|
||||
'url' => $this->getRequest()->getUrl(),
|
||||
'label' => $this->translate('Overview')
|
||||
))->activate('overview');
|
||||
|
||||
$migrations = new Migrations($this->db());
|
||||
|
||||
if ($migrations->hasPendingMigrations()) {
|
||||
$this->view->migrationsForm = $this
|
||||
->loadForm('applyMigrations')
|
||||
->setMigrations($migrations)
|
||||
->handleRequest();
|
||||
}
|
||||
|
||||
try {
|
||||
$this->fetchSyncState()
|
||||
->fetchImportState()
|
||||
->fetchJobState();
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function fetchSyncState()
|
||||
{
|
||||
$syncs = SyncRule::loadAll($this->db());
|
||||
if (count($syncs) > 0) {
|
||||
$state = 'ok';
|
||||
} else {
|
||||
$state = null;
|
||||
}
|
||||
|
||||
foreach ($syncs as $sync) {
|
||||
if ($sync->sync_state !== 'in-sync') {
|
||||
if ($sync->sync_state === 'failing') {
|
||||
$state = 'critical';
|
||||
break;
|
||||
} else {
|
||||
$state = 'warning';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->view->syncState = $state;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function fetchImportState()
|
||||
{
|
||||
$srcs = ImportSource::loadAll($this->db());
|
||||
if (count($srcs) > 0) {
|
||||
$state = 'ok';
|
||||
} else {
|
||||
$state = null;
|
||||
}
|
||||
|
||||
foreach ($srcs as $src) {
|
||||
if ($src->import_state !== 'in-sync') {
|
||||
if ($src->import_state === 'failing') {
|
||||
$state = 'critical';
|
||||
break;
|
||||
} else {
|
||||
$state = 'warning';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->view->importState = $state;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function fetchJobState()
|
||||
{
|
||||
$jobs = DirectorJob::loadAll($this->db());
|
||||
if (count($jobs) > 0) {
|
||||
$state = 'ok';
|
||||
} else {
|
||||
$state = null;
|
||||
}
|
||||
|
||||
foreach ($jobs as $job) {
|
||||
if ($job->isPending()) {
|
||||
$state = 'pending';
|
||||
} elseif (! $job->lastAttemptSucceeded()) {
|
||||
$state = 'critical';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->view->jobState = $state;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function hasDeploymentEndpoint()
|
||||
{
|
||||
try {
|
||||
|
78
application/controllers/JobController.php
Normal file
78
application/controllers/JobController.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Controllers;
|
||||
|
||||
use Icinga\Module\Director\Web\Controller\ActionController;
|
||||
use Icinga\Module\Director\Objects\Job;
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Web\Notification;
|
||||
use Icinga\Web\Url;
|
||||
|
||||
class JobController extends ActionController
|
||||
{
|
||||
public function addAction()
|
||||
{
|
||||
$this->indexAction();
|
||||
}
|
||||
|
||||
public function editAction()
|
||||
{
|
||||
$this->indexAction();
|
||||
}
|
||||
|
||||
public function runAction()
|
||||
{
|
||||
// TODO: Form, POST
|
||||
$id = $this->params->get('id');
|
||||
$job = Job::load($id, $this->db());
|
||||
if ($job->run()) {
|
||||
Notification::success('Job has successfully been completed');
|
||||
$this->redirectNow(
|
||||
Url::fromPath(
|
||||
'director/job',
|
||||
array('id' => $id)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
Notification::success('Job run failed');
|
||||
}
|
||||
}
|
||||
|
||||
public function indexAction()
|
||||
{
|
||||
$form = $this->view->form = $this->loadForm('directorJob')
|
||||
->setSuccessUrl('director/job')
|
||||
->setDb($this->db());
|
||||
|
||||
if ($id = $this->params->get('id')) {
|
||||
$this->prepareTabs($id)->activate('edit');
|
||||
$form->loadObject($id);
|
||||
$this->view->title = sprintf(
|
||||
$this->translate('Job %s'),
|
||||
$form->getObject()->job_name
|
||||
);
|
||||
} else {
|
||||
$this->view->title = $this->translate('Add job');
|
||||
$this->prepareTabs()->activate('add');
|
||||
}
|
||||
|
||||
$form->handleRequest();
|
||||
$this->setViewScript('object/form');
|
||||
}
|
||||
|
||||
protected function prepareTabs($id = null)
|
||||
{
|
||||
if ($id) {
|
||||
return $this->getTabs()->add('edit', array(
|
||||
'url' => 'director/job/edit',
|
||||
'urlParams' => array('id' => $id),
|
||||
'label' => $this->translate('Job'),
|
||||
));
|
||||
} else {
|
||||
return $this->getTabs()->add('add', array(
|
||||
'url' => 'director/job/add',
|
||||
'label' => $this->translate('Job'),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
33
application/controllers/JobsController.php
Normal file
33
application/controllers/JobsController.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Controllers;
|
||||
|
||||
use Icinga\Module\Director\Web\Controller\ActionController;
|
||||
|
||||
class JobsController extends ActionController
|
||||
{
|
||||
public function indexAction()
|
||||
{
|
||||
$this->setAutoRefreshInterval(10);
|
||||
$this->view->title = $this->translate('Jobs');
|
||||
|
||||
$this->getTabs()->add('jobs', array(
|
||||
'url' => 'director/jobs',
|
||||
'label' => $this->translate('Jobs'),
|
||||
))->activate('jobs');
|
||||
|
||||
$this->view->addLink = $this->view->qlink(
|
||||
$this->translate('Add'),
|
||||
'director/job',
|
||||
null,
|
||||
array('class' => 'icon-plus')
|
||||
);
|
||||
|
||||
$this->view->table = $this->applyPaginationLimits(
|
||||
$this->loadTable('job')
|
||||
->setConnection($this->db())
|
||||
);
|
||||
$this->setViewScript('list/table');
|
||||
|
||||
}
|
||||
}
|
@ -4,9 +4,11 @@ namespace Icinga\Module\Director\Controllers;
|
||||
|
||||
use Icinga\Module\Director\Web\Controller\ActionController;
|
||||
use Icinga\Module\Director\Objects\SyncRule;
|
||||
use Icinga\Module\Director\Objects\SyncRun;
|
||||
use Icinga\Module\Director\Import\Sync;
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Web\Notification;
|
||||
use Icinga\Web\Url;
|
||||
|
||||
class SyncruleController extends ActionController
|
||||
{
|
||||
@ -22,81 +24,67 @@ class SyncruleController extends ActionController
|
||||
|
||||
public function runAction()
|
||||
{
|
||||
$sync = new Sync(SyncRule::load($this->params->get('id'), $this->db()));
|
||||
$id = $this->params->get('id');
|
||||
$sync = new Sync(SyncRule::load($id, $this->db()));
|
||||
if ($runId = $sync->apply()) {
|
||||
Notification::success('Source has successfully been synchronized');
|
||||
$this->redirectNow('director/list/syncrule');
|
||||
$this->redirectNow(
|
||||
Url::fromPath(
|
||||
'director/syncrule/history',
|
||||
array(
|
||||
'id' => $id,
|
||||
'run_id' => $runId
|
||||
)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
public function indexAction()
|
||||
{
|
||||
$edit = false;
|
||||
|
||||
if ($id = $this->params->get('id')) {
|
||||
$edit = true;
|
||||
}
|
||||
|
||||
if ($edit) {
|
||||
$this->view->title = $this->translate('Edit sync rule');
|
||||
$this->getTabs()->add('edit', array(
|
||||
'url' => 'director/syncrule/edit',
|
||||
'urlParams' => array('id' => $id),
|
||||
'label' => $this->view->title,
|
||||
))->add('property', array(
|
||||
'label' => $this->translate('Properties'),
|
||||
'url' => 'director/syncrule/property',
|
||||
'urlParams' => array('rule_id' => $id)
|
||||
))->activate('edit');
|
||||
} else {
|
||||
$this->view->title = $this->translate('Add sync rule');
|
||||
$this->getTabs()->add('add', array(
|
||||
'url' => 'director/syncrule/add',
|
||||
'label' => $this->view->title,
|
||||
))->activate('add');
|
||||
}
|
||||
|
||||
$form = $this->view->form = $this->loadForm('syncRule')
|
||||
->setSuccessUrl('director/list/syncrule')
|
||||
->setDb($this->db());
|
||||
|
||||
if ($edit) {
|
||||
if ($id = $this->params->get('id')) {
|
||||
$this->prepareRuleTabs($id)->activate('edit');
|
||||
$form->loadObject($id);
|
||||
$this->view->title = sprintf(
|
||||
$this->translate('Sync rule: %s'),
|
||||
$form->getObject()->rule_name
|
||||
);
|
||||
} else {
|
||||
$this->view->title = $this->translate('Add sync rule');
|
||||
$this->prepareRuleTabs()->activate('add');
|
||||
}
|
||||
|
||||
$form->handleRequest();
|
||||
|
||||
$this->render('object/form', null, true);
|
||||
$this->setViewScript('object/form');
|
||||
}
|
||||
|
||||
public function propertyAction()
|
||||
{
|
||||
$this->view->stayHere = true;
|
||||
|
||||
$db = $this->db();
|
||||
$id = $this->params->get('rule_id');
|
||||
$rule = SyncRule::load($id, $db);
|
||||
|
||||
$this->view->addLink = $this->view->icon('plus')
|
||||
. ' '
|
||||
. $this->view->qlink(
|
||||
$this->translate('Add sync property rule'),
|
||||
'director/syncrule/addproperty',
|
||||
array('rule_id' => $id)
|
||||
);
|
||||
$this->getTabs()->add('edit', array(
|
||||
'url' => 'director/syncrule/edit',
|
||||
'urlParams' => array('id' => $id),
|
||||
'label' => $this->translate('Edit sync rule'),
|
||||
))->add('property', array(
|
||||
'label' => $this->translate('Properties'),
|
||||
'url' => 'director/syncrule/property',
|
||||
'urlParams' => array('rule_id' => $id)
|
||||
))->activate('property');
|
||||
$this->prepareRuleTabs($id)->activate('property');
|
||||
|
||||
$this->view->title = $this->translate('Sync properties: ');
|
||||
$this->view->addLink = $this->view->qlink(
|
||||
$this->translate('Add sync property rule'),
|
||||
'director/syncrule/addproperty',
|
||||
array('rule_id' => $id),
|
||||
array('class' => 'icon-plus')
|
||||
);
|
||||
|
||||
$this->view->title = $this->translate('Sync properties') . ': ' . $rule->rule_name;
|
||||
$this->view->table = $this->loadTable('syncproperty')
|
||||
->enforceFilter(Filter::where('rule_id', $id))
|
||||
->setConnection($this->db());
|
||||
$this->render('list/table', null, true);
|
||||
$this->setViewScript('list/table');
|
||||
}
|
||||
|
||||
public function editpropertyAction()
|
||||
@ -109,49 +97,102 @@ class SyncruleController extends ActionController
|
||||
$this->view->stayHere = true;
|
||||
$edit = false;
|
||||
|
||||
$db = $this->db();
|
||||
$ruleId = $this->params->get('rule_id');
|
||||
$rule = SyncRule::load($ruleId, $db);
|
||||
|
||||
if ($id = $this->params->get('id')) {
|
||||
$edit = true;
|
||||
}
|
||||
|
||||
$form = $this->view->form = $this->loadForm('syncProperty')->setDb($this->db());
|
||||
$this->view->addLink = $this->view->qlink(
|
||||
$this->translate('back'),
|
||||
'director/syncrule/property',
|
||||
array('rule_id' => $ruleId),
|
||||
array('class' => 'icon-left-big')
|
||||
);
|
||||
|
||||
$form = $this->view->form = $this->loadForm('syncProperty')->setDb($db);
|
||||
|
||||
if ($edit) {
|
||||
$form->loadObject($id);
|
||||
$rule_id = $form->getObject()->rule_id;
|
||||
$form->setRule(SyncRule::load($rule_id, $this->db()));
|
||||
$form->setRule(SyncRule::load($rule_id, $db));
|
||||
} elseif ($rule_id = $this->params->get('rule_id')) {
|
||||
$form->setRule(SyncRule::load($rule_id, $this->db()));
|
||||
$form->setRule(SyncRule::load($rule_id, $db));
|
||||
}
|
||||
$form->setSuccessUrl('director/syncrule/property', array('rule_id' => $rule_id));
|
||||
|
||||
$form->setSuccessUrl('director/syncrule/property', array('rule_id' => $rule_id));
|
||||
$form->handleRequest();
|
||||
|
||||
$tabs = $this->getTabs()->add('edit', array(
|
||||
'url' => 'director/syncrule/edit',
|
||||
'urlParams' => array('id' => $rule_id),
|
||||
'label' => $this->translate('Edit sync rule'),
|
||||
));
|
||||
$this->prepareRuleTabs($rule_id)->activate('property');
|
||||
|
||||
if ($edit) {
|
||||
$tabs->add('property', array(
|
||||
'label' => $this->translate('Properties'),
|
||||
'url' => 'director/syncrule/property',
|
||||
'urlParams' => array('rule_id' => $rule_id)
|
||||
));
|
||||
$this->view->title = sprintf(
|
||||
$this->translate('Sync "%s": %s'),
|
||||
$form->getObject()->destination_field,
|
||||
$rule->rule_name
|
||||
);
|
||||
} else {
|
||||
$tabs->add('property', array(
|
||||
'label' => $this->translate('Properties'),
|
||||
'url' => 'director/syncrule/property',
|
||||
'urlParams' => array('rule_id' => $rule_id)
|
||||
));
|
||||
$this->view->title = sprintf(
|
||||
$this->translate('Add sync property: %s'),
|
||||
$rule->rule_name
|
||||
);
|
||||
}
|
||||
|
||||
$tabs->activate('property');
|
||||
|
||||
$this->view->title = $this->translate('Sync property'); // add/edit
|
||||
$this->view->table = $this->loadTable('syncproperty')
|
||||
->enforceFilter(Filter::where('rule_id', $rule_id))
|
||||
->setConnection($this->db());
|
||||
$this->render('list/table', null, true);
|
||||
$this->setViewScript('list/table');
|
||||
}
|
||||
|
||||
public function historyAction()
|
||||
{
|
||||
$this->view->stayHere = true;
|
||||
|
||||
$db = $this->db();
|
||||
$id = $this->params->get('id');
|
||||
$rule = SyncRule::load($id, $db);
|
||||
|
||||
$this->prepareRuleTabs($id)->activate('history');
|
||||
$this->view->title = $this->translate('Sync history') . ': ' . $rule->rule_name;
|
||||
$this->view->table = $this->loadTable('syncRun')
|
||||
->enforceFilter(Filter::where('rule_id', $id))
|
||||
->setConnection($this->db());
|
||||
|
||||
if ($runId = $this->params->get('run_id')) {
|
||||
$this->view->run = SyncRun::load($runId, $db);
|
||||
$this->view->formerId = $db->fetchActivityLogIdByChecksum(
|
||||
$this->view->run->last_former_activity
|
||||
);
|
||||
|
||||
$this->view->lastId = $db->fetchActivityLogIdByChecksum(
|
||||
$this->view->run->last_related_activity
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function prepareRuleTabs($ruleId = null)
|
||||
{
|
||||
if ($ruleId) {
|
||||
return $this->getTabs()->add('edit', array(
|
||||
'url' => 'director/syncrule/edit',
|
||||
'urlParams' => array('id' => $ruleId),
|
||||
'label' => $this->translate('Sync rule'),
|
||||
))->add('property', array(
|
||||
'label' => $this->translate('Properties'),
|
||||
'url' => 'director/syncrule/property',
|
||||
'urlParams' => array('rule_id' => $ruleId)
|
||||
))->add('history', array(
|
||||
'label' => $this->translate('History'),
|
||||
'url' => 'director/syncrule/history',
|
||||
'urlParams' => array('id' => $ruleId)
|
||||
));
|
||||
} else {
|
||||
return $this->getTabs()->add('add', array(
|
||||
'url' => 'director/syncrule/add',
|
||||
'label' => $this->translate('Sync rule'),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
37
application/forms/ApplyMigrationsForm.php
Normal file
37
application/forms/ApplyMigrationsForm.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Forms;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Module\Director\Db\Migrations;
|
||||
use Icinga\Module\Director\Web\Form\QuickForm;
|
||||
|
||||
class ApplyMigrationsForm extends QuickForm
|
||||
{
|
||||
protected $migrations;
|
||||
|
||||
public function setup()
|
||||
{
|
||||
$this->setSubmitLabel($this->translate('Apply schema migrations'));
|
||||
}
|
||||
|
||||
public function onSuccess()
|
||||
{
|
||||
try {
|
||||
$this->setSuccessMessage($this->translate(
|
||||
'Pending database schema migrations have successfully been applied'
|
||||
));
|
||||
|
||||
$this->migrations->applyPendingMigrations();
|
||||
parent::onSuccess();
|
||||
} catch (Exception $e) {
|
||||
$this->addError($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function setMigrations(Migrations $migrations)
|
||||
{
|
||||
$this->migrations = $migrations;
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -74,6 +74,8 @@ class DirectorDatafieldForm extends DirectorObjectForm
|
||||
$el->setValue($val);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setButtons();
|
||||
}
|
||||
|
||||
protected function addSettings($class = null)
|
||||
|
117
application/forms/DirectorJobForm.php
Normal file
117
application/forms/DirectorJobForm.php
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Forms;
|
||||
|
||||
use Icinga\Module\Director\Web\Form\DirectorObjectForm;
|
||||
use Icinga\Web\Hook;
|
||||
|
||||
class DirectorJobForm extends DirectorObjectForm
|
||||
{
|
||||
public function setup()
|
||||
{
|
||||
$this->addElement('select', 'job_class', array(
|
||||
'label' => $this->translate('Job Type'),
|
||||
'required' => true,
|
||||
'multiOptions' => $this->optionalEnum($this->enumJobTypes()),
|
||||
'description' => $this->translate(
|
||||
'These are different available job types'
|
||||
),
|
||||
'class' => 'autosubmit'
|
||||
));
|
||||
|
||||
if (! $jobClass = $this->getJobClass()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($desc = $jobClass::getDescription($this)) {
|
||||
$this->addHtmlHint($desc);
|
||||
}
|
||||
|
||||
$this->addBoolean(
|
||||
'disabled',
|
||||
array(
|
||||
'label' => $this->translate('Disabled'),
|
||||
'description' => $this->translate(
|
||||
'This allows to temporarily disable this job'
|
||||
)
|
||||
),
|
||||
'n'
|
||||
);
|
||||
|
||||
$this->addElement('text', 'run_interval', array(
|
||||
'label' => $this->translate('Run interval'),
|
||||
'description' => $this->translate(
|
||||
'Execution interval for this job, in seconds'
|
||||
),
|
||||
'value' => $jobClass::getSuggestedRunInterval($this)
|
||||
));
|
||||
|
||||
$this->addElement('text', 'job_name', array(
|
||||
'label' => $this->translate('Job name'),
|
||||
'description' => $this->translate(
|
||||
'A short name identifying this job. Use something meaningful,'
|
||||
. ' like "Import Puppet Hosts"'
|
||||
),
|
||||
'required' => true,
|
||||
));
|
||||
|
||||
$this->addSettings();
|
||||
$this->setButtons();
|
||||
}
|
||||
|
||||
public function getSentOrObjectSetting($name, $default = null)
|
||||
{
|
||||
if ($this->hasObject()) {
|
||||
$value = $this->getSentValue($name);
|
||||
if ($value === null) {
|
||||
$object = $this->getObject();
|
||||
|
||||
return $object->getSetting($name, $default);
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
} else {
|
||||
return $this->getSentValue($name, $default);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getJobClass($class = null)
|
||||
{
|
||||
if ($class === null) {
|
||||
$class = $this->getSentOrObjectValue('job_class');
|
||||
}
|
||||
|
||||
if (array_key_exists($class, $this->enumJobTypes())) {
|
||||
return $class;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function addSettings($class = null)
|
||||
{
|
||||
if (! $class = $this->getJobClass($class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$class::addSettingsFormFields($this);
|
||||
foreach ($this->object()->getSettings() as $key => $val) {
|
||||
if ($el = $this->getElement($key)) {
|
||||
$el->setValue($val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function enumJobTypes()
|
||||
{
|
||||
$hooks = Hook::all('Director\\Job');
|
||||
|
||||
$enum = array();
|
||||
foreach ($hooks as $hook) {
|
||||
$enum[get_class($hook)] = $hook->getName();
|
||||
}
|
||||
asort($enum);
|
||||
|
||||
return $enum;
|
||||
}
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Forms;
|
||||
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
|
||||
use Icinga\Module\Director\Objects\IcingaObject;
|
||||
use Icinga\Module\Director\Objects\IcingaHost;
|
||||
use Icinga\Module\Director\Web\Form\QuickForm;
|
||||
|
||||
class IcingaAssignServiceToHostForm extends QuickForm
|
||||
{
|
||||
|
||||
// NOT TOUCHED
|
||||
|
||||
/**
|
||||
*
|
||||
* Please note that $object would conflict with logic in parent class
|
||||
*/
|
||||
protected $icingaObject;
|
||||
|
||||
protected $db;
|
||||
|
||||
public function setDb($db)
|
||||
{
|
||||
$this->db = $db;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setIcingaObject($object)
|
||||
{
|
||||
$this->icingaObject = $object;
|
||||
// $this->className = get_class($object) . 'Field';
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setup()
|
||||
{
|
||||
$this->addHidden('service_id', $this->icingaObject->id);
|
||||
|
||||
if ($this->icingaObject->isTemplate()) {
|
||||
$this->addHtmlHint(
|
||||
'Assign all services importing this service template to one or'
|
||||
. ' more hosts'
|
||||
);
|
||||
} else {
|
||||
$this->addHtmlHint(
|
||||
'Assign this service to one or more hosts'
|
||||
);
|
||||
}
|
||||
|
||||
$this->addElement('select', 'object_type', array(
|
||||
'label' => 'Assign',
|
||||
'required' => true,
|
||||
'multiOptions' => $this->optionalEnum(
|
||||
array(
|
||||
'host_group' => $this->translate('to a host group'),
|
||||
'host_property' => $this->translate('by host property'),
|
||||
'host_group_property' => $this->translate('by host group property'),
|
||||
)
|
||||
),
|
||||
'class' => 'autosubmit'
|
||||
|
||||
));
|
||||
|
||||
switch ($this->getSentValue('object_type')) {
|
||||
case 'host_group':
|
||||
$this->addHostGroupElements();
|
||||
break;
|
||||
case 'host_property':
|
||||
$this->addHostPropertyElements();
|
||||
break;
|
||||
case 'host_property':
|
||||
$this->addHostFilterElements();
|
||||
break;
|
||||
}
|
||||
|
||||
$this->setSubmitLabel(
|
||||
$this->translate('Assign')
|
||||
);
|
||||
}
|
||||
|
||||
protected function addHostGroupElements()
|
||||
{
|
||||
$this->addElement('select', 'host_id', array(
|
||||
'label' => 'Hostgroup',
|
||||
'required' => true,
|
||||
'multiOptions' => $this->optionalEnum($this->db->enumHostgroups())
|
||||
));
|
||||
}
|
||||
|
||||
protected function addHostPropertyElements()
|
||||
{
|
||||
$this->addElement('select', 'host_property', array(
|
||||
'label' => 'Host property',
|
||||
'required' => true,
|
||||
'multiOptions' => $this->optionalEnum(IcingaHost::enumProperties($this->db))
|
||||
));
|
||||
$this->addElement('text', 'filter_expression', array(
|
||||
'label' => 'Filter expression',
|
||||
'required' => true,
|
||||
));
|
||||
}
|
||||
|
||||
protected function addHostFilterElements()
|
||||
{
|
||||
$this->addElement('text', 'host_filter', array(
|
||||
'label' => 'Host filter string',
|
||||
'required' => true,
|
||||
));
|
||||
}
|
||||
|
||||
public function onSuccess()
|
||||
{
|
||||
switch ($this->getValue('object_type')) {
|
||||
case 'host_group':
|
||||
$this->db->insert('icinga_service_assignment', array(
|
||||
'service_id' => $this->getValue('service_id'),
|
||||
// TODO: in?
|
||||
'filter_string' => 'groups=' . $this->getValue('host_group'),
|
||||
));
|
||||
break;
|
||||
case 'host_property':
|
||||
$this->db->insert('icinga_service_assignment', array(
|
||||
'service_id' => $this->getValue('service_id'),
|
||||
'filter_string' => sprintf(
|
||||
'host.%s=%s',
|
||||
$this->getValue('host_property'),
|
||||
c::renderString($this->getValue('filter_expression'))
|
||||
)
|
||||
));
|
||||
break;
|
||||
case 'host_filter':
|
||||
$this->db->insert('icinga_service_assignment', array(
|
||||
'service_id' => $this->getValue('service_id'),
|
||||
'filter_string' => $this->getValue('filter_string'),
|
||||
));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -27,9 +27,10 @@ class KickstartForm extends QuickForm
|
||||
$this->migrateDbLabel = $this->translate('Apply schema migrations');
|
||||
|
||||
$this->addResourceConfigElements();
|
||||
$this->addResourceDisplayGroup();
|
||||
|
||||
if (!$this->config()->get('db', 'resource')
|
||||
|| ($this->config()->get('db', 'resource') !== $this->getResourceName())) {
|
||||
$this->addResourceDisplayGroup();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -66,6 +67,9 @@ class KickstartForm extends QuickForm
|
||||
));
|
||||
|
||||
$this->addHtmlHint($hint, array('name' => 'HINT_ready'));
|
||||
$this->getDisplayGroup('config')->addElements(
|
||||
array($this->getElement('HINT_ready'))
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -168,7 +172,6 @@ class KickstartForm extends QuickForm
|
||||
|
||||
$this->addHtmlHint($hint, array('name' => 'HINT_db_perms'));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -214,10 +217,11 @@ class KickstartForm extends QuickForm
|
||||
{
|
||||
$elements = array(
|
||||
'HINT_no_resource',
|
||||
'HINT_ready',
|
||||
'resource',
|
||||
'HINT_ready',
|
||||
'HINT_schema',
|
||||
'HINT_db_perms'
|
||||
'HINT_db_perms',
|
||||
'HINT_config_store'
|
||||
);
|
||||
|
||||
$this->addDisplayGroup($elements, 'config', array(
|
||||
@ -258,26 +262,41 @@ class KickstartForm extends QuickForm
|
||||
try {
|
||||
$config->saveIni();
|
||||
$this->setSuccessMessage($this->translate('Configuration has been stored'));
|
||||
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
$this->getElement('resource')->addError(
|
||||
sprintf(
|
||||
$this->translate('Unable to store the configuration to "%s"'),
|
||||
$this->translate(
|
||||
'Unable to store the configuration to "%s". Please check'
|
||||
. ' file permissions or manually store the content shown below'
|
||||
),
|
||||
$config->getConfigFile()
|
||||
)
|
||||
)->removeDecorator('description');
|
||||
$this->addHtmlHint(
|
||||
'<pre>' . $config . '</pre>'
|
||||
);
|
||||
}
|
||||
$this->addHtmlHint(
|
||||
'<pre>' . $config . '</pre>',
|
||||
array('name' => 'HINT_config_store')
|
||||
);
|
||||
|
||||
$this->getDisplayGroup('config')->addElements(
|
||||
array($this->getElement('HINT_config_store'))
|
||||
);
|
||||
$this->removeElement('HINT_ready');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function onSuccess()
|
||||
{
|
||||
try {
|
||||
if ($this->getSubmitLabel() === $this->storeConfigLabel) {
|
||||
$this->storeResourceConfig();
|
||||
return parent::onSuccess();
|
||||
if ($this->storeResourceConfig()) {
|
||||
return parent::onSuccess();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->getSubmitLabel() === $this->createDbLabel
|
||||
|
@ -25,7 +25,6 @@ class SyncPropertyForm extends DirectorObjectForm
|
||||
|
||||
public function setup()
|
||||
{
|
||||
$this->addHtml(sprintf('<h3>%s</h3>', $this->getView()->escape($this->rule->rule_name)));
|
||||
$this->addHidden('rule_id', $this->rule_id);
|
||||
|
||||
$this->addElement('select', 'source_id', array(
|
||||
|
151
application/tables/ConfigFileDiffTable.php
Normal file
151
application/tables/ConfigFileDiffTable.php
Normal file
@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Tables;
|
||||
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Module\Director\Web\Table\QuickTable;
|
||||
use Icinga\Module\Director\Util;
|
||||
|
||||
class ConfigFileDiffTable extends QuickTable
|
||||
{
|
||||
protected $leftChecksum;
|
||||
|
||||
protected $rightChecksum;
|
||||
|
||||
public function getColumns()
|
||||
{
|
||||
throw new ProgrammingError('Accessing getColumns() is not supported');
|
||||
}
|
||||
|
||||
protected function listTableClasses()
|
||||
{
|
||||
return array_merge(array('config-diff'), parent::listTableClasses());
|
||||
}
|
||||
|
||||
protected function getRowClasses($row)
|
||||
{
|
||||
return 'file-' . $row->file_action;
|
||||
}
|
||||
|
||||
protected function getActionUrl($row)
|
||||
{
|
||||
$params = array('file_path' => $row->file_path);
|
||||
|
||||
if ($row->file_checksum_left === $row->file_checksum_right) {
|
||||
$params['config_checksum'] = $row->config_checksum_right;
|
||||
} elseif ($row->file_checksum_left === null) {
|
||||
$params['config_checksum'] = $row->config_checksum_right;
|
||||
} elseif ($row->file_checksum_right === null) {
|
||||
$params['config_checksum'] = $row->config_checksum_left;
|
||||
} else {
|
||||
$params['left'] = $row->config_checksum_left;
|
||||
$params['right'] = $row->config_checksum_right;
|
||||
return $this->url('director/config/filediff', $params);
|
||||
}
|
||||
|
||||
return $this->url('director/config/file', $params);
|
||||
}
|
||||
|
||||
public function setLeftChecksum($checksum)
|
||||
{
|
||||
$this->leftChecksum = $checksum;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRightChecksum($checksum)
|
||||
{
|
||||
$this->rightChecksum = $checksum;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitles()
|
||||
{
|
||||
$view = $this->view();
|
||||
return array(
|
||||
'file_action' => $view->translate('Action'),
|
||||
'file_path' => $view->translate('File'),
|
||||
);
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
$db = $this->connection()->getConnection();
|
||||
$query = clone($this->getBaseQuery());
|
||||
$query->reset('order');
|
||||
$this->applyFiltersToQuery($query);
|
||||
return $db->fetchOne($db->select()->from(
|
||||
array('cntsub' => $query),
|
||||
array('cnt' => 'COUNT(*)')
|
||||
));
|
||||
}
|
||||
|
||||
public function fetchData()
|
||||
{
|
||||
$db = $this->connection()->getConnection();
|
||||
$query = $this->getBaseQuery();
|
||||
|
||||
if ($this->hasLimit() || $this->hasOffset()) {
|
||||
$query->limit($this->getLimit(), $this->getOffset());
|
||||
}
|
||||
|
||||
$this->applyFiltersToQuery($query);
|
||||
|
||||
return $db->fetchAll($query);
|
||||
}
|
||||
|
||||
public function getBaseQuery()
|
||||
{
|
||||
$conn = $this->connection();
|
||||
$db = $conn->getConnection();
|
||||
|
||||
$left = $db->select()
|
||||
->from(
|
||||
array('cfl' => 'director_generated_config_file'),
|
||||
array(
|
||||
'file_path' => 'COALESCE(cfl.file_path, cfr.file_path)',
|
||||
'config_checksum_left' => $conn->dbHexFunc('cfl.config_checksum'),
|
||||
'config_checksum_right' => $conn->dbHexFunc('cfr.config_checksum'),
|
||||
'file_checksum_left' => $conn->dbHexFunc('cfl.file_checksum'),
|
||||
'file_checksum_right' => $conn->dbHexFunc('cfr.file_checksum'),
|
||||
'file_action' => '(CASE WHEN cfr.config_checksum IS NULL'
|
||||
. " THEN 'removed' WHEN cfl.file_checksum = cfr.file_checksum"
|
||||
. " THEN 'unmodified' ELSE 'modified' END)",
|
||||
)
|
||||
)->joinLeft(
|
||||
array('cfr' => 'director_generated_config_file'),
|
||||
$db->quoteInto(
|
||||
'cfl.file_path = cfr.file_path AND cfr.config_checksum = ?',
|
||||
$conn->quoteBinary(Util::hex2binary($this->rightChecksum))
|
||||
),
|
||||
array()
|
||||
)->where(
|
||||
'cfl.config_checksum = ?',
|
||||
$conn->quoteBinary(Util::hex2binary($this->leftChecksum))
|
||||
);
|
||||
|
||||
$right = $db->select()
|
||||
->from(
|
||||
array('cfl' => 'director_generated_config_file'),
|
||||
array(
|
||||
'file_path' => 'COALESCE(cfr.file_path, cfl.file_path)',
|
||||
'config_checksum_left' => $conn->dbHexFunc('cfl.config_checksum'),
|
||||
'config_checksum_right' => $conn->dbHexFunc('cfr.config_checksum'),
|
||||
'file_checksum_left' => $conn->dbHexFunc('cfl.file_checksum'),
|
||||
'file_checksum_right' => $conn->dbHexFunc('cfr.file_checksum'),
|
||||
'file_action' => "('created')",
|
||||
)
|
||||
)->joinRight(
|
||||
array('cfr' => 'director_generated_config_file'),
|
||||
$db->quoteInto(
|
||||
'cfl.file_path = cfr.file_path AND cfl.config_checksum = ?',
|
||||
$conn->quoteBinary(Util::hex2binary($this->leftChecksum))
|
||||
),
|
||||
array()
|
||||
)->where(
|
||||
'cfr.config_checksum = ?',
|
||||
$conn->quoteBinary(Util::hex2binary($this->rightChecksum))
|
||||
)->where('cfl.file_checksum IS NULL');
|
||||
|
||||
return $db->select()->union(array($left, $right))->order('file_path');
|
||||
}
|
||||
}
|
@ -45,22 +45,29 @@ class DeploymentLogTable extends QuickTable
|
||||
|
||||
public function getColumns()
|
||||
{
|
||||
$db = $this->connection();
|
||||
|
||||
$columns = array(
|
||||
'id' => 'l.id',
|
||||
'peer_identity' => 'l.peer_identity',
|
||||
'identifier' => "l.peer_identity || ' (' || SUBSTRING(",
|
||||
'start_time' => 'l.start_time',
|
||||
'stage_collected' => 'l.stage_collected',
|
||||
'dump_succeeded' => 'l.dump_succeeded',
|
||||
'stage_name' => 'l.stage_name',
|
||||
'startup_succeeded' => 'l.startup_succeeded',
|
||||
'checksum' => 'LOWER(HEX(c.checksum))',
|
||||
'checksum' => $db->dbHexFunc('c.checksum'),
|
||||
'duration' => "l.duration_dump || 'ms'",
|
||||
);
|
||||
|
||||
if ($this->connection->isPgsql()) {
|
||||
$columns['checksum'] = "LOWER(ENCODE(c.checksum, 'hex'))";
|
||||
$columns['identifier'] .= $columns['checksum'] . ' FROM 1 FOR 7)';
|
||||
} else {
|
||||
$columns['identifier'] .= $columns['checksum'] . ', 1, 7)';
|
||||
}
|
||||
|
||||
$columns['identifier'] .= " || ')'";
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
@ -73,8 +80,8 @@ class DeploymentLogTable extends QuickTable
|
||||
{
|
||||
$view = $this->view();
|
||||
return array(
|
||||
'peer_identity' => $view->translate('Icinga Node'),
|
||||
'start_time' => $view->translate('Time'),
|
||||
'identifier' => $view->translate('Icinga Node'),
|
||||
'start_time' => $view->translate('Time'),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,7 @@ class IcingaHostTable extends IcingaObjectTable
|
||||
$db = $this->connection()->getConnection();
|
||||
$sub = clone($this->getBaseQuery());
|
||||
$sub->columns($this->getColumns());
|
||||
$this->applyFiltersToQuery($sub);
|
||||
$query = $db->select()->from(
|
||||
array('sub' => $sub),
|
||||
'COUNT(*)'
|
||||
|
@ -9,8 +9,6 @@ use Exception;
|
||||
|
||||
class ImportsourceTable extends QuickTable
|
||||
{
|
||||
protected $revalidate = false;
|
||||
|
||||
protected $searchColumns = array(
|
||||
'source_name',
|
||||
);
|
||||
@ -18,9 +16,11 @@ class ImportsourceTable extends QuickTable
|
||||
public function getColumns()
|
||||
{
|
||||
return array(
|
||||
'id' => 's.id',
|
||||
'source_name' => 's.source_name',
|
||||
'provider_class' => 's.provider_class',
|
||||
'id' => 's.id',
|
||||
'source_name' => 's.source_name',
|
||||
'provider_class' => 's.provider_class',
|
||||
'import_state' => 's.import_state',
|
||||
'last_error_message' => 's.last_error_message',
|
||||
);
|
||||
}
|
||||
|
||||
@ -44,25 +44,11 @@ class ImportsourceTable extends QuickTable
|
||||
|
||||
protected function getRowClasses($row)
|
||||
{
|
||||
if (! $this->revalidate) {
|
||||
return array();
|
||||
}
|
||||
try {
|
||||
$import = new Import(ImportSource::load($row->id, $this->connection()));
|
||||
if ($import->providesChanges()) {
|
||||
$row->source_name = sprintf(
|
||||
'%s (%s)',
|
||||
$row->source_name,
|
||||
$this->view()->translate('has changes')
|
||||
);
|
||||
return 'pending-changes';
|
||||
} else {
|
||||
return 'in-sync';
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$row->source_name = $row->source_name . ' (' . $e->getMessage() . ')';
|
||||
return 'failing';
|
||||
if ($row->import_state === 'failing' && $row->last_error_message) {
|
||||
$row->source_name .= ' (' . $row->last_error_message . ')';
|
||||
}
|
||||
|
||||
return $row->import_state;
|
||||
}
|
||||
|
||||
public function getBaseQuery()
|
||||
|
74
application/tables/JobTable.php
Normal file
74
application/tables/JobTable.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Tables;
|
||||
|
||||
use Icinga\Module\Director\Web\Table\QuickTable;
|
||||
use Icinga\Module\Director\Objects\Job;
|
||||
use Exception;
|
||||
|
||||
class JobTable extends QuickTable
|
||||
{
|
||||
public function getColumns()
|
||||
{
|
||||
return array(
|
||||
'id' => 'j.id',
|
||||
'job_name' => 'j.job_name',
|
||||
'job_class' => 'j.job_class',
|
||||
'disabled' => 'j.disabled',
|
||||
'run_interval' => 'j.run_interval',
|
||||
'last_attempt_succeeded' => 'j.last_attempt_succeeded',
|
||||
'ts_last_attempt' => 'j.ts_last_attempt',
|
||||
'unixts_last_attempt' => 'UNIX_TIMESTAMP(j.ts_last_attempt)',
|
||||
'ts_last_error' => 'j.ts_last_error',
|
||||
'last_error_message' => 'j.last_error_message',
|
||||
);
|
||||
}
|
||||
|
||||
protected function getActionUrl($row)
|
||||
{
|
||||
return $this->url('director/job', array('id' => $row->id));
|
||||
}
|
||||
|
||||
protected function listTableClasses()
|
||||
{
|
||||
return array_merge(array('jobs'), parent::listTableClasses());
|
||||
}
|
||||
|
||||
protected function getRowClasses($row)
|
||||
{
|
||||
if ($row->unixts_last_attempt === null) {
|
||||
return 'pending';
|
||||
}
|
||||
if ($row->unixts_last_attempt + $row->run_interval < time()) {
|
||||
return 'pending';
|
||||
}
|
||||
|
||||
if ($row->last_attempt_succeeded === 'y') {
|
||||
return 'ok';
|
||||
} elseif ($row->last_attempt_succeeded === 'n') {
|
||||
return 'critical';
|
||||
} else {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
public function getTitles()
|
||||
{
|
||||
$view = $this->view();
|
||||
return array(
|
||||
'job_name' => $view->translate('Job name'),
|
||||
);
|
||||
}
|
||||
|
||||
public function getBaseQuery()
|
||||
{
|
||||
$db = $this->connection()->getConnection();
|
||||
|
||||
$query = $db->select()->from(
|
||||
array('j' => 'director_job'),
|
||||
array()
|
||||
)->order('job_name');
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
78
application/tables/SyncRunTable.php
Normal file
78
application/tables/SyncRunTable.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Tables;
|
||||
|
||||
use Icinga\Module\Director\Web\Table\QuickTable;
|
||||
use Exception;
|
||||
|
||||
class SyncRunTable extends QuickTable
|
||||
{
|
||||
protected $revalidate = false;
|
||||
|
||||
public function getColumns()
|
||||
{
|
||||
return array(
|
||||
'id' => 'sr.id',
|
||||
'rule_id' => 'sr.rule_id',
|
||||
'rule_name' => 'sr.rule_name',
|
||||
'start_time' => 'sr.start_time',
|
||||
'duration_ms' => 'sr.duration_ms',
|
||||
'objects_deleted' => 'sr.objects_deleted',
|
||||
'objects_created' => 'sr.objects_created',
|
||||
'objects_modified' => 'sr.objects_modified',
|
||||
'last_former_activity' => 'sr.last_former_activity',
|
||||
'last_related_activity' => 'sr.last_related_activity',
|
||||
);
|
||||
}
|
||||
|
||||
protected function getActionUrl($row)
|
||||
{
|
||||
return $this->url(
|
||||
'director/syncrule/history',
|
||||
array(
|
||||
'id' => $row->rule_id,
|
||||
'run_id' => $row->id,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function getTitles()
|
||||
{
|
||||
$singleRule = false;
|
||||
|
||||
foreach ($this->enforcedFilters as $filter) {
|
||||
if (in_array('rule_id', $filter->listFilteredColumns())) {
|
||||
$singleRule = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$view = $this->view();
|
||||
|
||||
if ($singleRule) {
|
||||
return array(
|
||||
'start_time' => $view->translate('Start time'),
|
||||
'objects_created' => $view->translate('Created'),
|
||||
'objects_modified' => $view->translate('Modified'),
|
||||
'objects_deleted' => $view->translate('Deleted'),
|
||||
);
|
||||
} else {
|
||||
return array(
|
||||
'rule_name' => $view->translate('Rule name'),
|
||||
'start_time' => $view->translate('Start time'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBaseQuery()
|
||||
{
|
||||
$db = $this->connection()->getConnection();
|
||||
|
||||
$query = $db->select()->from(
|
||||
array('sr' => 'sync_run'),
|
||||
array()
|
||||
)->order('start_time DESC');
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
@ -9,13 +9,12 @@ use Exception;
|
||||
|
||||
class SyncruleTable extends QuickTable
|
||||
{
|
||||
protected $revalidate = false;
|
||||
|
||||
public function getColumns()
|
||||
{
|
||||
return array(
|
||||
'id' => 's.id',
|
||||
'rule_name' => 's.rule_name',
|
||||
'sync_state' => 's.sync_state',
|
||||
'object_type' => 's.object_type',
|
||||
'update_policy' => 's.update_policy',
|
||||
'purge_existing' => 's.purge_existing',
|
||||
@ -25,7 +24,7 @@ class SyncruleTable extends QuickTable
|
||||
|
||||
protected function getActionUrl($row)
|
||||
{
|
||||
return $this->url('director/syncrule/edit', array('id' => $row->id));
|
||||
return $this->url('director/syncrule', array('id' => $row->id));
|
||||
}
|
||||
|
||||
protected function listTableClasses()
|
||||
@ -45,25 +44,7 @@ class SyncruleTable extends QuickTable
|
||||
|
||||
protected function getRowClasses($row)
|
||||
{
|
||||
if (! $this->revalidate) {
|
||||
return array();
|
||||
}
|
||||
|
||||
try {
|
||||
// $mod = Sync::hasModifications(
|
||||
$sync = new Sync(SyncRule::load($row->id, $this->connection()));
|
||||
$mod = $sync->getExpectedModifications();
|
||||
|
||||
if (count($mod) > 0) {
|
||||
$row->rule_name = $row->rule_name . ' (' . count($mod) . ')';
|
||||
return 'pending-changes';
|
||||
} else {
|
||||
return 'in-sync';
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$row->rule_name = $row->rule_name . ' (' . $e->getMessage() . ')';
|
||||
return 'failing';
|
||||
}
|
||||
return $row->sync_state;
|
||||
}
|
||||
|
||||
public function getTitles()
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="controls">
|
||||
<?= $this->tabs ?>
|
||||
<h1><?= $this->escape($this->title) ?></h1>
|
||||
<span data-base-target="_next">
|
||||
<span class="action-links" data-base-target="_next">
|
||||
<?= $this->addLink ?>
|
||||
</div>
|
||||
|
||||
|
31
application/views/scripts/config/diff.phtml
Normal file
31
application/views/scripts/config/diff.phtml
Normal file
@ -0,0 +1,31 @@
|
||||
<div class="controls">
|
||||
<?= $this->tabs ?>
|
||||
<h1><?= $this->escape($this->title) ?></h1>
|
||||
<span class="action-links" data-base-target="_next">
|
||||
<?= $this->addLink ?>
|
||||
</span>
|
||||
<form action="<?= $this->url ?>" method="GET">
|
||||
<?= $this->formSelect(
|
||||
'left',
|
||||
$this->leftSum,
|
||||
array('class' => 'autosubmit'),
|
||||
array(null => $this->translate('- please choose -')) + $this->configs
|
||||
)
|
||||
?>
|
||||
<?= $this->formSelect(
|
||||
'right',
|
||||
$this->rightSum,
|
||||
array('class' => 'autosubmit'),
|
||||
array(null => $this->translate('- please choose -')) + $this->configs
|
||||
)
|
||||
?>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="content" data-base-target="_next">
|
||||
<?php if (count($this->table)): ?>
|
||||
<div>
|
||||
<?= $this->table->render() ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
@ -1,7 +1,7 @@
|
||||
<div class="controls">
|
||||
<?= $this->tabs ?>
|
||||
<h1><?= $title ?></h1>
|
||||
<span data-base-target="_self">
|
||||
<span class="action-links" data-base-target="_self">
|
||||
<?= $this->addLink ?>
|
||||
</span>
|
||||
</div>
|
||||
|
11
application/views/scripts/config/filediff.phtml
Normal file
11
application/views/scripts/config/filediff.phtml
Normal file
@ -0,0 +1,11 @@
|
||||
<div class="controls">
|
||||
<?= $this->tabs ?>
|
||||
<h1><?= $this->escape($this->title) ?></h1>
|
||||
<span class="action-links" data-base-target="_next">
|
||||
<?= $this->addLink ?>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="content" data-base-target="_next">
|
||||
<?= $this->output ?>
|
||||
</div>
|
@ -1,7 +1,7 @@
|
||||
<div class="controls">
|
||||
<?= $this->tabs ?>
|
||||
<h1><?= $this->escape($this->title) ?></h1>
|
||||
<span data-base-target="_next">
|
||||
<span class="action-links" data-base-target="_next">
|
||||
<?= $this->addLink ?>
|
||||
</span>
|
||||
<?php if (count($table) || ! $this->filterEditor->getFilter()->isEmpty()): ?>
|
||||
@ -24,6 +24,11 @@
|
||||
'director/show/activitylog',
|
||||
array('checksum' => $this->config->getLastActivityHexChecksum()),
|
||||
array('class' => 'icon-clock', 'data-base-target' => '_next')
|
||||
) ?><br /><?= $this->qlink(
|
||||
$this->translate('Diff with other config'),
|
||||
'director/config/diff',
|
||||
array('left' => $this->config->getHexChecksum()),
|
||||
array('class' => 'icon-flapping', 'data-base-target' => '_self')
|
||||
) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -8,7 +8,11 @@
|
||||
$cert = $this->escape($this->certname);
|
||||
$master = $this->escape($this->master);
|
||||
?>
|
||||
Please check the <a href="http://docs.icinga.org/icinga2/latest/doc/module/icinga2/chapter/icinga2-client">Icinga 2 Client documentation</a> for more related information. The Director-assisted setup corresponds to configuring the <a href="http://docs.icinga.org/icinga2/latest/doc/module/icinga2/chapter/icinga2-client#icinga2-client-configuration-command-bridge">Client as Command Execution Bridge</a>.
|
||||
<p>Please check the <a href="http://docs.icinga.org/icinga2/latest/doc/module/icinga2/chapter/icinga2-client">Icinga 2 Client documentation</a> for more related information. The Director-assisted setup corresponds to configuring the <a href="http://docs.icinga.org/icinga2/latest/doc/module/icinga2/chapter/icinga2-client#icinga2-client-configuration-command-bridge">Client as Command Execution Bridge</a>.</p>
|
||||
|
||||
<?php if ($this->error): ?>
|
||||
<p class="error"><?= $this->escape($this->error) ?></p>
|
||||
<?php endif ?>
|
||||
|
||||
<h2>When using the node wizard</h2>
|
||||
<p>Ticket : <code><?= $this->escape($ticket) ?></code></p>
|
||||
@ -49,7 +53,7 @@ icinga2 pki request --host <?= $master ?> \
|
||||
include "constants.conf"
|
||||
include <itl>
|
||||
include <plugins>
|
||||
include <plugins-contrib>
|
||||
// include <plugins-contrib>
|
||||
|
||||
object FileLogger "main-log" {
|
||||
severity = "information"
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="controls">
|
||||
<?= $this->tabs ?>
|
||||
<h1><?= $this->escape($this->title) ?></h1>
|
||||
<span>
|
||||
<span class="action-links">
|
||||
<?= $this->addLink ?>
|
||||
</span>
|
||||
<?php if (count($table) || ($this->filterEditor && ! $this->filterEditor->getFilter()->isEmpty())): ?>
|
||||
|
@ -84,6 +84,11 @@ if (!$this->hasDeploymentEndpoint) {
|
||||
echo $this->form;
|
||||
}
|
||||
|
||||
if ($this->migrationsForm) {
|
||||
echo '<h1>' . $this->translate('There are pending database schema migrations') . "</h2>\n";
|
||||
echo $this->migrationsForm;
|
||||
}
|
||||
|
||||
$all = array(
|
||||
$this->translate('Define whatever you want to be monitored') => array(
|
||||
array('host', $this->translate('Host objects'), 'director/hosts', statSummary($this, 'host')),
|
||||
@ -101,8 +106,9 @@ $all = array(
|
||||
array('globe', $this->translate('Zones'), 'director/zones', statSummary($this, 'zone')),
|
||||
),
|
||||
$this->translate('Do more with your data') => array(
|
||||
array('database', $this->translate('Import data sources'), 'director/list/importsource', $this->translate('Define and manage imports from various data sources')),
|
||||
array('flapping', $this->translate('Synchronize'), 'director/list/importsource', $this->translate('Define how imported data should be synchronized with Icinga')),
|
||||
array('database', $this->translate('Import data sources'), 'director/list/importsource', $this->translate('Define and manage imports from various data sources'), $this->importState),
|
||||
array('flapping', $this->translate('Synchronize'), 'director/list/syncrule', $this->translate('Define how imported data should be synchronized with Icinga'), $this->syncState),
|
||||
array('clock', $this->translate('Jobs'), 'director/jobs', $this->translate('Schedule and automate Import, Syncronization, Config Deployment, Housekeeping and more'), $this->jobState),
|
||||
array('sort-name-up', $this->translate('Provide data lists'), 'director/data/lists', $this->translate('Provide data lists to make life easier for your users')),
|
||||
array('edit', $this->translate('Define data fields'), 'director/data/fields', $this->translate('Data fields make sure that configuration fits your rules')),
|
||||
)
|
||||
|
@ -27,7 +27,7 @@ $pt = $loc['thousands_sep'];
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<span data-base-target="_next">
|
||||
<span class="action-links" data-base-target="_next">
|
||||
<?= $this->addLink ?>
|
||||
</span><br />
|
||||
<?= $this->table->getPaginator() ?>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="controls">
|
||||
<?= $this->tabs ?>
|
||||
<h1><?= $this->escape($this->title) ?></h1>
|
||||
<span<?php if (! $this->stayHere): ?> data-base-target="_next"<?php endif ?>>
|
||||
<span class="action-links"<?php if (! $this->stayHere): ?> data-base-target="_next"<?php endif ?>>
|
||||
<?= $this->addLink ?>
|
||||
</span>
|
||||
<?= $this->filterEditor ?>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="controls">
|
||||
<?= $this->tabs ?>
|
||||
<h1><?= $this->escape($this->title) ?></h1>
|
||||
<span>
|
||||
<span class="action-links">
|
||||
<?= $this->actionLinks ?>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="controls">
|
||||
<?= $this->tabs ?>
|
||||
<h1><?= $this->escape($this->title) ?></h1>
|
||||
<span>
|
||||
<span class="action-links">
|
||||
<?= $this->actionLinks ?>
|
||||
<?= $this->render('object/deploymentLink.phtml') ?>
|
||||
</span>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class="controls">
|
||||
<?= $this->tabs ?>
|
||||
<h1><?= $this->escape($this->title) ?></h1>
|
||||
<span>
|
||||
<span class="action-links">
|
||||
<?= $this->actionLinks ?>
|
||||
</span>
|
||||
</div>
|
||||
@ -10,7 +10,7 @@
|
||||
<?php if ($object->disabled === 'y'): ?>
|
||||
<p class="error"><?= $this->translate('This object will not be deployed as it has been disabled') ?></p>
|
||||
<?php endif ?>
|
||||
<?php if ($object->isExternal()): ?>
|
||||
<?php if ($this->isExternal): ?>
|
||||
<p><?= $this->translate(
|
||||
'This is an external object. It has been imported from Icinga 2 throught the'
|
||||
. ' Core API and cannot be managed with the Icinga Director. It is however'
|
||||
@ -19,7 +19,11 @@
|
||||
. ' object more enjoyable'
|
||||
) ?></p>
|
||||
<?php endif ?>
|
||||
<pre<?php if ($object->disabled === 'y'): ?> class="disabled"<?php endif ?>><?= $this->escape($object) ?><?php if ($this->extraObjects): ?>
|
||||
<?= implode('', $this->extraObjects) ?>
|
||||
<?php endif ?></pre>
|
||||
<?php foreach ($this->config->getFiles() as $filename => $file): ?>
|
||||
<?php if (! $this->isExternal): ?><h2><?= $this->escape($filename) ?></h2><?php endif ?>
|
||||
<pre<?php if ($this->isDisabled): ?> class="disabled"<?php elseif ($this->isExternal): ?> class="logfile"<?php endif ?>>
|
||||
<?= $this->escape($file->getContent()) ?>
|
||||
</pre>
|
||||
<?php endforeach ?>
|
||||
|
||||
</div>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<?= $this->tabs ?>
|
||||
|
||||
<h1><?= $this->escape($this->title) ?><?= $this->quickSearch ?></h1>
|
||||
<span<?php if (! $this->stayHere): ?> data-base-target="_next"<?php endif ?>>
|
||||
<span class="action-links"<?php if (! $this->stayHere): ?> data-base-target="_next"<?php endif ?>>
|
||||
<?= $this->addLink ?>
|
||||
</span>
|
||||
<?php if ($this->filterEditor): ?>
|
||||
|
84
application/views/scripts/syncrule/history.phtml
Normal file
84
application/views/scripts/syncrule/history.phtml
Normal file
@ -0,0 +1,84 @@
|
||||
<div class="controls">
|
||||
<?= $this->tabs ?>
|
||||
<h1><?= $this->escape($this->title) ?></h1>
|
||||
<span class="action-links"<?php if (! $this->stayHere): ?> data-base-target="_next"<?php endif ?>>
|
||||
<?= $this->addLink ?>
|
||||
</span>
|
||||
<?= $this->filterEditor ?>
|
||||
<?= $this->table->getPaginator() ?>
|
||||
</div>
|
||||
|
||||
<div class="content"<?php if (! $this->stayHere): ?> data-base-target="_next"<?php endif ?>>
|
||||
<?php if ($this->run): ?>
|
||||
<h3><?= $this->translate('Sync run details') ?></h3>
|
||||
<table class="key-value-table">
|
||||
<tr>
|
||||
<th><?= $this->translate('Start time') ?></th>
|
||||
<td><?= $this->escape($run->start_time) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?= $this->translate('Duration') ?></th>
|
||||
<td><?= sprintf('%.2fs', $run->duration_ms / 1000) ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?= $this->translate('Activity') ?></th>
|
||||
<td data-base-target="_next"><?php
|
||||
$total = $run->objects_deleted + $run->objects_created + $run->objects_modified;
|
||||
if ($total === 0) {
|
||||
echo $this->translate('No changes have been made');
|
||||
} else {
|
||||
if ($total === 1) {
|
||||
echo $this->translate('One object has been modified');
|
||||
} else {
|
||||
printf(
|
||||
$this->translate('%s objects have been modified'),
|
||||
$total
|
||||
);
|
||||
}
|
||||
|
||||
$activityUrl = sprintf(
|
||||
'director/config/activities?id>%d&id<=%d',
|
||||
$formerId,
|
||||
$lastId
|
||||
);
|
||||
|
||||
$links = array();
|
||||
if ($run->objects_created > 0) {
|
||||
$links[] = $this->qlink(
|
||||
sprintf('%d created', $run->objects_created),
|
||||
$activityUrl,
|
||||
array('action_name' => 'create')
|
||||
);
|
||||
}
|
||||
if ($run->objects_modified > 0) {
|
||||
$links[] = $this->qlink(
|
||||
sprintf('%d modified', $run->objects_modified),
|
||||
$activityUrl,
|
||||
array('action_name' => 'modify')
|
||||
);
|
||||
}
|
||||
if ($run->objects_deleted > 0) {
|
||||
$links[] = $this->qlink(
|
||||
sprintf('%d deleted', $run->objects_deleted),
|
||||
$activityUrl,
|
||||
array('action_name' => 'delete')
|
||||
);
|
||||
}
|
||||
|
||||
if (count($links) > 1) {
|
||||
$links[] = $this->qlink(
|
||||
'Show all actions',
|
||||
$activityUrl
|
||||
);
|
||||
}
|
||||
|
||||
if (! empty($links)) {
|
||||
echo ': ' . implode(', ', $links);
|
||||
}
|
||||
}
|
||||
?></td>
|
||||
</tr>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
<?= $this->table->render() ?>
|
||||
</div>
|
@ -37,10 +37,10 @@ $section->add($this->translate('Hosts'))->setUrl('director/hosts')->setPriority(
|
||||
$section->add($this->translate('Services'))->setUrl('director/services')->setPriority(40);
|
||||
$section->add($this->translate('Commands'))->setUrl('director/commands')->setPriority(50);
|
||||
$section->add($this->translate('Users'))->setUrl('director/users')->setPriority(70);
|
||||
$section->add($this->translate('Import / Sync'))
|
||||
$section->add($this->translate('Automation'))
|
||||
->setUrl('director/list/importsource')
|
||||
->setPriority(901);
|
||||
$section->add($this->translate('Deployments / History'))
|
||||
$section->add($this->translate('Config history'))
|
||||
->setUrl('director/config/deployments')
|
||||
->setPriority(902)
|
||||
->setRenderer('ConfigHealthItemRenderer');
|
||||
|
@ -42,7 +42,7 @@ Web-based Configuration
|
||||
|
||||
The following steps should guide you through the web-based Kickstart wizard.
|
||||
In case you prefer automated configuration, you should check the dedicated
|
||||
[documentation section](doc/03-Automation.md).
|
||||
[documentation section](03-Automation.md).
|
||||
|
||||
### Create a Database resource
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
Automation - Configuration management
|
||||
<a id="Automation"></a>Automation - Configuration management
|
||||
=====================================
|
||||
|
||||
Director has been designed to work in distributed environments. In case
|
||||
|
@ -1,5 +1,13 @@
|
||||
Preparing your Icinga 2 environment for the Director
|
||||
====================================================
|
||||
Getting started
|
||||
===============
|
||||
|
||||
When new to the Director please make your first steps with a naked Icinga
|
||||
environment. Director is not allowed to modify existing configuration in
|
||||
`/etc/icinga2`. And while importing existing config is possible (happens for
|
||||
example automagically at kickstart time), it is a pretty advanced task you
|
||||
should not tackle at the early beginning.
|
||||
|
||||
|
||||
|
||||
Create an API user
|
||||
------------------
|
||||
@ -21,14 +29,22 @@ checking your clients, you will have to create them.
|
||||
The easiest way to set up Icinga 2 with a `zone` and `endpoint` is by
|
||||
running the [Icinga 2 Setup Wizard](http://docs.icinga.org/icinga2/latest/doc/module/icinga2/chapter/icinga2-client#icinga2-client-installation-master-setup).
|
||||
|
||||
Start with a new, empty Icinga setup. Director is not allowed to modify
|
||||
existing configuration in `/etc/icinga2`, and while importing existing
|
||||
config is possible (happens for example automagically at kickstart time)
|
||||
this is an advanced task you should not tackle at the early beginning.
|
||||
|
||||
Take some time to really understand how to work with Icinga Director first.
|
||||
|
||||
Working with Agents and Config Zones
|
||||
====================================
|
||||
|
||||
Hint: Large: max packet size
|
||||
Other topics that might interest you
|
||||
------------------------------------
|
||||
|
||||
* [Working with agents](24-Working-with-agents.md)
|
||||
* [Undstanding how Icinga Director works](10-How-it-works.md)
|
||||
|
||||
What you should not try to start with
|
||||
-------------------------------------
|
||||
|
||||
Director has not been built to help you with managing existing hand-crafted
|
||||
configuration in /etc/icinga2. There are cases where it absolutely would
|
||||
make sense to combine the Director with manual configuration. You can also
|
||||
use multiple tools owning separare config packages. But these are pretty
|
||||
advanced topics.
|
||||
|
||||
|
||||
|
@ -79,12 +79,12 @@ PASSWORD="***"
|
||||
test -z "$PASSWORD" || USERNAME="$USERNAME:$PASSWORD"
|
||||
|
||||
test -z "$BODY" && curl -u "$USERNAME" \
|
||||
-i http://icingaweb/icingaweb/$URL \
|
||||
-i https://icingaweb/icingaweb/$URL \
|
||||
-H 'Accept: application/json' \
|
||||
-X $METHOD
|
||||
|
||||
test -z "$BODY" || curl -u "$USERNAME" \
|
||||
-i http://icingaweb/icingaweb/$URL \
|
||||
-i https://icingaweb/icingaweb/$URL \
|
||||
-H 'Accept: application/json' \
|
||||
-X $METHOD \
|
||||
-d "$BODY"
|
||||
|
@ -1062,6 +1062,18 @@ abstract class DbObject
|
||||
return self::$prefetched[$class];
|
||||
}
|
||||
|
||||
public static function clearPrefetchCache()
|
||||
{
|
||||
$class = get_called_class();
|
||||
if (! array_key_exists($class, self::$prefetched)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset(self::$prefetched[$class]);
|
||||
unset(self::$prefetchedNames[$class]);
|
||||
unset(self::$prefetchStats[$class]);
|
||||
}
|
||||
|
||||
public static function exists($id, DbConnection $connection)
|
||||
{
|
||||
if (static::getPrefetched($id)) {
|
||||
|
@ -789,6 +789,32 @@ class Db extends DbConnection
|
||||
return $binary;
|
||||
}
|
||||
|
||||
public function enumDeployedConfigs()
|
||||
{
|
||||
$db = $this->db();
|
||||
|
||||
$columns = array(
|
||||
'checksum' => $this->dbHexFunc('c.checksum'),
|
||||
);
|
||||
|
||||
if ($this->isPgsql()) {
|
||||
$columns['caption'] = 'SUBSTRING(' . $columns['checksum'] . ' FROM 1 FOR 7)';
|
||||
} else {
|
||||
$columns['caption'] = 'SUBSTRING(' . $columns['checksum'] . ', 1, 7)';
|
||||
}
|
||||
|
||||
$query = $db->select()->from(
|
||||
array('l' => 'director_deployment_log'),
|
||||
$columns
|
||||
)->joinLeft(
|
||||
array('c' => 'director_generated_config'),
|
||||
'c.checksum = l.config_checksum',
|
||||
array()
|
||||
)->order('l.start_time DESC');
|
||||
|
||||
return $db->fetchPairs($query);
|
||||
}
|
||||
|
||||
public function getUncollectedDeployments()
|
||||
{
|
||||
$db = $this->db();
|
||||
|
@ -64,6 +64,11 @@ class Housekeeping
|
||||
);
|
||||
}
|
||||
|
||||
public function hasPendingTasks()
|
||||
{
|
||||
return count($this->getPendingTaskSummary()) > 0;
|
||||
}
|
||||
|
||||
public function runAllTasks()
|
||||
{
|
||||
$result = array();
|
||||
|
64
library/Director/Hook/JobHook.php
Normal file
64
library/Director/Hook/JobHook.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Hook;
|
||||
|
||||
use Icinga\Module\Director\Db;
|
||||
use Icinga\Module\Director\Web\Form\QuickForm;
|
||||
|
||||
abstract class JobHook
|
||||
{
|
||||
private $db;
|
||||
|
||||
public static function getDescription(QuickForm $form)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
abstract public function run();
|
||||
|
||||
abstract public function isPending();
|
||||
|
||||
public function getName()
|
||||
{
|
||||
$parts = explode('\\', get_class($this));
|
||||
$class = preg_replace('/Job$/', '', array_pop($parts));
|
||||
|
||||
if (array_shift($parts) === 'Icinga' && array_shift($parts) === 'Module') {
|
||||
$module = array_shift($parts);
|
||||
if ($module !== 'Director') {
|
||||
return sprintf('%s (%s)', $class, $module);
|
||||
}
|
||||
}
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
public static function getSuggestedRunInterval(QuickForm $form)
|
||||
{
|
||||
return 900;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method if you want to extend the settings form
|
||||
*
|
||||
* @param QuickForm $form QuickForm that should be extended
|
||||
* @return QuickForm
|
||||
*/
|
||||
public static function addSettingsFormFields(QuickForm $form)
|
||||
{
|
||||
return $form;
|
||||
}
|
||||
|
||||
public function setDb(Db $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function db()
|
||||
{
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -233,8 +233,14 @@ class IcingaConfig
|
||||
return $checksums;
|
||||
}
|
||||
|
||||
protected function getZoneName($id)
|
||||
// TODO: prepare lookup cache if empty?
|
||||
public function getZoneName($id)
|
||||
{
|
||||
if (! array_key_exists($id, $this->zoneMap)) {
|
||||
$zone = IcingaZone::loadWithAutoIncId($id, $this->connection);
|
||||
$this->zoneMap[$id] = $zone->object_name;
|
||||
}
|
||||
|
||||
return $this->zoneMap[$id];
|
||||
}
|
||||
|
||||
@ -546,7 +552,7 @@ class IcingaConfig
|
||||
return in_array($type, $types);
|
||||
}
|
||||
|
||||
protected function configFile($name, $suffix = '.conf')
|
||||
public function configFile($name, $suffix = '.conf')
|
||||
{
|
||||
$filename = $name . $suffix;
|
||||
if (! array_key_exists($filename, $this->files)) {
|
||||
|
@ -105,6 +105,8 @@ class Sync
|
||||
foreach ($objects as $object) {
|
||||
if ($object->hasBeenModified()) {
|
||||
$modified[] = $object;
|
||||
} elseif ($object instanceof IcingaObject && $object->shouldBeRemoved()) {
|
||||
$modified[] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
@ -671,9 +673,6 @@ class Sync
|
||||
/**
|
||||
* Runs a SyncRule and applies all resulting changes
|
||||
*
|
||||
* TODO: Should return the id of the related sync_history table entry.
|
||||
* Such a table does not yet exist, so 42 is the answer right now.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function apply()
|
||||
@ -739,7 +738,6 @@ class Sync
|
||||
(microtime(true) - $this->runStartTime) * 1000
|
||||
))->store();
|
||||
|
||||
|
||||
return $this->run->id;
|
||||
}
|
||||
}
|
||||
|
107
library/Director/Job/ConfigJob.php
Normal file
107
library/Director/Job/ConfigJob.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Job;
|
||||
|
||||
use Icinga\Application\Benchmark;
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
|
||||
use Icinga\Module\Director\Hook\JobHook;
|
||||
use Icinga\Module\Director\Web\Form\QuickForm;
|
||||
use Icinga\Module\Director\Util;
|
||||
|
||||
class ConfigJob extends JobHook
|
||||
{
|
||||
protected $housekeeping;
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->housekeeping()->runAllTasks();
|
||||
}
|
||||
|
||||
public function isPending()
|
||||
{
|
||||
return $this->housekeeping()->hasPendingTasks();
|
||||
}
|
||||
|
||||
public static function getDescription(QuickForm $form)
|
||||
{
|
||||
return $form->translate(
|
||||
'The Housekeeping job provides various task that keep your Director'
|
||||
. ' database fast and clean'
|
||||
);
|
||||
}
|
||||
|
||||
protected function housekeeping()
|
||||
{
|
||||
if ($this->housekeeping === null) {
|
||||
$this->housekeeping = new Housekeeping($this->db());
|
||||
}
|
||||
|
||||
return $this->housekeeping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-render the current configuration
|
||||
*/
|
||||
public function renderAction()
|
||||
{
|
||||
$config = new IcingaConfig($this->db());
|
||||
Benchmark::measure('Rendering config');
|
||||
if ($config->hasBeenModified()) {
|
||||
Benchmark::measure('Config rendered, storing to db');
|
||||
$config->store();
|
||||
Benchmark::measure('All done');
|
||||
$checksum = $config->getHexChecksum();
|
||||
$this->printf(
|
||||
"New config with checksum %s has been generated\n",
|
||||
$checksum
|
||||
);
|
||||
} else {
|
||||
$checksum = $config->getHexChecksum();
|
||||
$this->printf(
|
||||
"Config with checksum %s already exists\n",
|
||||
$checksum
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy the current configuration
|
||||
*
|
||||
* Does nothing if config didn't change unless you provide
|
||||
* the --force parameter
|
||||
*/
|
||||
public function deployAction()
|
||||
{
|
||||
$api = $this->api();
|
||||
$db = $this->db();
|
||||
|
||||
$checksum = $this->params->get('checksum');
|
||||
if ($checksum) {
|
||||
$config = IcingaConfig::load(Util::hex2binary($checksum), $db);
|
||||
} else {
|
||||
$config = IcingaConfig::generate($db);
|
||||
$checksum = $config->getHexChecksum();
|
||||
}
|
||||
|
||||
$api->wipeInactiveStages($db);
|
||||
$current = $api->getActiveChecksum($db);
|
||||
if ($current === $checksum) {
|
||||
if ($this->params->get('force')) {
|
||||
echo "Config matches active stage, deploying anyway\n";
|
||||
} else {
|
||||
echo "Config matches active stage, nothing to do\n";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
if ($api->dumpConfig($config, $db)) {
|
||||
$this->printf("Config '%s' has been deployed\n", $checksum);
|
||||
} else {
|
||||
$this->fail(
|
||||
sprintf("Failed to deploy config '%s'\n", $checksum)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
library/Director/Job/HousekeepingJob.php
Normal file
39
library/Director/Job/HousekeepingJob.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Job;
|
||||
|
||||
use Icinga\Module\Director\Db\Housekeeping;
|
||||
use Icinga\Module\Director\Hook\JobHook;
|
||||
use Icinga\Module\Director\Web\Form\QuickForm;
|
||||
|
||||
class HousekeepingJob extends JobHook
|
||||
{
|
||||
protected $housekeeping;
|
||||
|
||||
public function run()
|
||||
{
|
||||
$this->housekeeping()->runAllTasks();
|
||||
}
|
||||
|
||||
public static function getDescription(QuickForm $form)
|
||||
{
|
||||
return $form->translate(
|
||||
'The Housekeeping job provides various task that keep your Director'
|
||||
. ' database fast and clean'
|
||||
);
|
||||
}
|
||||
|
||||
public function isPending()
|
||||
{
|
||||
return $this->housekeeping()->hasPendingTasks();
|
||||
}
|
||||
|
||||
protected function housekeeping()
|
||||
{
|
||||
if ($this->housekeeping === null) {
|
||||
$this->housekeeping = new Housekeeping($this->db());
|
||||
}
|
||||
|
||||
return $this->housekeeping;
|
||||
}
|
||||
}
|
24
library/Director/Job/ImportJob.php
Normal file
24
library/Director/Job/ImportJob.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Job;
|
||||
|
||||
use Icinga\Module\Director\Hook\JobHook;
|
||||
use Icinga\Module\Director\Web\Form\QuickForm;
|
||||
|
||||
class ImportJob extends JobHook
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
|
||||
public static function getDescription(QuickForm $form)
|
||||
{
|
||||
return $form->translate(
|
||||
'The "Import" job allows to run import actions at regular intervals'
|
||||
);
|
||||
}
|
||||
|
||||
public function isPending()
|
||||
{
|
||||
}
|
||||
}
|
46
library/Director/Job/JobRunner.php
Normal file
46
library/Director/Job/JobRunner.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Job;
|
||||
|
||||
use Icinga\Module\Director\Db;
|
||||
|
||||
class JobRunner
|
||||
{
|
||||
public function __construct(Db $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function runPendingJobs()
|
||||
{
|
||||
foreach ($this->getConfiguredJobs() as $job) {
|
||||
if ($job->isPending()) {
|
||||
$this->run($job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function run(Job $job)
|
||||
{
|
||||
if ($this->shouldFork()) {
|
||||
$this->fork($job);
|
||||
} else {
|
||||
$job->run();
|
||||
}
|
||||
}
|
||||
|
||||
protected function fork(Job $job)
|
||||
{
|
||||
$cmd = 'icingacli director job run ' . $job->id;
|
||||
$output = `$cmd`;
|
||||
}
|
||||
|
||||
protected function shouldFork()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getRegisteredJobs()
|
||||
{
|
||||
}
|
||||
}
|
83
library/Director/Job/SyncJob.php
Normal file
83
library/Director/Job/SyncJob.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Job;
|
||||
|
||||
use Icinga\Module\Director\Db;
|
||||
use Icinga\Module\Director\Hook\JobHook;
|
||||
use Icinga\Module\Director\Web\Form\QuickForm;
|
||||
|
||||
class SyncJob extends JobHook
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
if ($this->getSetting('apply_changes') === 'y') {
|
||||
$this->syncRule()->applyChanges();
|
||||
} else{
|
||||
$this->syncRule()->checkForChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public static function getDescription(QuickForm $form)
|
||||
{
|
||||
return $form->translate(
|
||||
'The "Sync" job allows to run sync actions at regular intervals'
|
||||
);
|
||||
}
|
||||
|
||||
public static function addSettingsFormFields(QuickForm $form)
|
||||
{
|
||||
$rules = self::enumSyncRules($form);
|
||||
|
||||
$form->addElement('select', 'rule_id', array(
|
||||
'label' => $form->translate('Synchronization rule'),
|
||||
'description' => $form->translate(
|
||||
'Please choose your synchronization rule that should be executed.'
|
||||
. ' You could create different schedules for different rules or also'
|
||||
. ' opt for running all of them at once.'
|
||||
),
|
||||
'required' => true,
|
||||
'class' => 'autosubmit',
|
||||
'multiOptions' => $rules
|
||||
));
|
||||
|
||||
$form->addElement('select', 'apply_changes', array(
|
||||
'label' => $form->translate('Apply changes'),
|
||||
'description' => $form->translate(
|
||||
'You could immediately apply eventual changes or just learn about them.'
|
||||
. ' In case you do not want them to be applied immediately, defining a'
|
||||
. ' job still makes sense. You will be made aware of available changes'
|
||||
. ' in your Director GUI.'
|
||||
),
|
||||
'value' => 'n',
|
||||
'multiOptions' => array(
|
||||
'y' => $form->translate('Yes'),
|
||||
'n' => $form->translate('No'),
|
||||
)
|
||||
));
|
||||
|
||||
if (! strlen($form->getSentOrObjectValue('job_name'))) {
|
||||
if (($ruleId = $form->getSentValue('rule_id')) && array_key_exists($ruleId, $rules)) {
|
||||
$name = sprintf('Sync job: %s', $rules[$ruleId]);
|
||||
$form->getElement('job_name')->setValue($name);
|
||||
///$form->getObject()->set('job_name', $name);
|
||||
}
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
protected static function enumSyncRules(QuickForm $form)
|
||||
{
|
||||
$db = $form->getDb();
|
||||
$query = $db->select()->from('sync_rule', array('id', 'rule_name'))->order('rule_name');
|
||||
$res = $db->fetchPairs($query);
|
||||
return array(
|
||||
null => $form->translate('- please choose -'),
|
||||
'__ALL__' => $form->translate('Run all rules at once')
|
||||
) + $res;
|
||||
}
|
||||
|
||||
public function isPending()
|
||||
{
|
||||
}
|
||||
}
|
48
library/Director/Objects/DirectorJob.php
Normal file
48
library/Director/Objects/DirectorJob.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Icinga\Module\Director\Data\Db\DbObjectWithSettings;
|
||||
|
||||
class DirectorJob extends DbObjectWithSettings
|
||||
{
|
||||
protected $table = 'director_job';
|
||||
|
||||
protected $keyName = 'id';
|
||||
|
||||
protected $autoincKeyName = 'id';
|
||||
|
||||
protected $defaultProperties = array(
|
||||
'id' => null,
|
||||
'job_name' => null,
|
||||
'job_class' => null,
|
||||
'disabled' => null,
|
||||
'run_interval' => null,
|
||||
'last_attempt_succeeded' => null,
|
||||
'ts_last_attempt' => null,
|
||||
'ts_last_error' => null,
|
||||
'last_error_message' => null,
|
||||
);
|
||||
|
||||
protected $settingsTable = 'director_job_setting';
|
||||
|
||||
protected $settingsRemoteId = 'job_id';
|
||||
|
||||
public function isPending()
|
||||
{
|
||||
if ($this->ts_last_attempt === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strtotime($this->unixts_last_attempt) + $this->run_interval < time()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function lastAttemptSucceeded()
|
||||
{
|
||||
return $this->last_attempt_succeeded === 'y';
|
||||
}
|
||||
}
|
@ -82,7 +82,9 @@ class IcingaArguments implements Iterator, Countable, IcingaConfigRenderer
|
||||
|
||||
public function set($key, $value)
|
||||
{
|
||||
$argument = IcingaCommandArgument::create($this->mungeCommandArgument($key, $value));
|
||||
$argument = IcingaCommandArgument::create(
|
||||
$this->mungeCommandArgument($key, $value)
|
||||
)->set('command_id', $this->object->id);
|
||||
$key = $argument->argument_name;
|
||||
if (array_key_exists($key, $this->arguments)) {
|
||||
$this->arguments[$key]->replaceWith($argument);
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
|
||||
|
||||
class IcingaCommand extends IcingaObject
|
||||
@ -74,6 +75,11 @@ class IcingaCommand extends IcingaObject
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function getRenderingZone(IcingaConfig $config = null)
|
||||
{
|
||||
return $this->connection->getDefaultGlobalZoneName();
|
||||
}
|
||||
|
||||
protected function renderCommand()
|
||||
{
|
||||
$command = $this->command;
|
||||
|
@ -122,7 +122,6 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
private function loadMultiRelation($property)
|
||||
{
|
||||
if ($this->hasBeenLoadedFromDb()) {
|
||||
@ -155,6 +154,7 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
||||
}
|
||||
}
|
||||
|
||||
ksort($this->loadedMultiRelations);
|
||||
return $this->loadedMultiRelations;
|
||||
}
|
||||
|
||||
@ -1128,15 +1128,15 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
||||
}
|
||||
|
||||
$config->configFile(
|
||||
'zones.d/' . $this->getRenderingZone($config)
|
||||
'zones.d/' . $this->getRenderingZone($config) . '/' . $filename
|
||||
)->addObject($this);
|
||||
}
|
||||
|
||||
public function getRenderingZone(IcingaConfig $config = null)
|
||||
{
|
||||
if ($this->zone_id) {
|
||||
if ($zoneId = $this->getResolvedProperty('zone_id')) {
|
||||
// Config has a lookup cache, is faster:
|
||||
return $config->getZoneName($this->zone_id);
|
||||
return $config->getZoneName($zoneId);
|
||||
}
|
||||
|
||||
if ($this->isTemplate() || $this->isApplyRule()) {
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
|
||||
|
||||
abstract class IcingaObjectGroup extends IcingaObject
|
||||
{
|
||||
protected $supportsImports = true;
|
||||
@ -13,4 +15,9 @@ abstract class IcingaObjectGroup extends IcingaObject
|
||||
'disabled' => 'n',
|
||||
'display_name' => null,
|
||||
);
|
||||
|
||||
public function getRenderingZone(IcingaConfig $config = null)
|
||||
{
|
||||
return $this->connection->getDefaultGlobalZoneName();
|
||||
}
|
||||
}
|
||||
|
@ -162,13 +162,18 @@ class IcingaService extends IcingaObject
|
||||
|
||||
protected function renderCustomExtensions()
|
||||
{
|
||||
if ($this->command_endpoint_id !== null
|
||||
|| $this->object_type !== 'object'
|
||||
|| $this->getResolvedProperty('use_agent') !== 'y') {
|
||||
// A hand-crafted command endpoint overrides use_agent
|
||||
if ($this->command_endpoint_id !== null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($this->hasBeenAssignedToHostTemplate()) {
|
||||
// In case use_agent isn't defined, do nothing
|
||||
// TODO: what if we inherit use_agent and override it with 'n'?
|
||||
if ($this->use_agent !== 'y') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($this->hasBeenAssignedToHostTemplate() || $this->object_type !== 'object') {
|
||||
return c::renderKeyValue('command_endpoint', 'host.name');
|
||||
} else {
|
||||
return $this->renderRelationProperty('host', $this->host_id, 'command_endpoint');
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
|
||||
|
||||
class IcingaUser extends IcingaObject
|
||||
{
|
||||
protected $table = 'icinga_user';
|
||||
@ -40,4 +42,9 @@ class IcingaUser extends IcingaObject
|
||||
'period' => 'IcingaTimePeriod',
|
||||
'zone' => 'IcingaZone',
|
||||
);
|
||||
|
||||
public function getRenderingZone(IcingaConfig $config = null)
|
||||
{
|
||||
return $this->connection->getMasterZoneName();
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,14 @@
|
||||
|
||||
namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
|
||||
|
||||
class IcingaUserGroup extends IcingaObjectGroup
|
||||
{
|
||||
protected $table = 'icinga_usergroup';
|
||||
|
||||
public function getRenderingZone(IcingaConfig $config = null)
|
||||
{
|
||||
return $this->connection->getMasterZoneName();
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
|
||||
|
||||
class IcingaZone extends IcingaObject
|
||||
@ -40,6 +41,21 @@ class IcingaZone extends IcingaObject
|
||||
return c::renderKeyValue('endpoints', c::renderArray($endpoints));
|
||||
}
|
||||
|
||||
public function getRenderingZone(IcingaConfig $config = null)
|
||||
{
|
||||
// If the zone has a parent zone...
|
||||
if ($this->get('parent_id')) {
|
||||
// ...we render the zone object to the parent zone
|
||||
return $this->parent;
|
||||
} elseif ($this->is_global === 'y') {
|
||||
// ...additional global zones are rendered to our global zone...
|
||||
return $this->connection->getDefaultGlobalZoneName();
|
||||
} else {
|
||||
// ...and all the other zones are rendered to our master zone
|
||||
return $this->connection->getMasterZoneName();
|
||||
}
|
||||
}
|
||||
|
||||
public function setEndpointList($list)
|
||||
{
|
||||
$this->endpointList = $list;
|
||||
|
@ -2,7 +2,10 @@
|
||||
|
||||
namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Icinga\Application\Benchmark;
|
||||
use Icinga\Module\Director\Data\Db\DbObjectWithSettings;
|
||||
use Icinga\Module\Director\Import\Import;
|
||||
use Exception;
|
||||
|
||||
class ImportSource extends DbObjectWithSettings
|
||||
{
|
||||
@ -13,10 +16,13 @@ class ImportSource extends DbObjectWithSettings
|
||||
protected $autoincKeyName = 'id';
|
||||
|
||||
protected $defaultProperties = array(
|
||||
'id' => null,
|
||||
'source_name' => null,
|
||||
'provider_class' => null,
|
||||
'key_column' => null
|
||||
'id' => null,
|
||||
'source_name' => null,
|
||||
'provider_class' => null,
|
||||
'key_column' => null,
|
||||
'import_state' => 'unknown',
|
||||
'last_error_message' => null,
|
||||
'last_attempt' => null,
|
||||
);
|
||||
|
||||
protected $settingsTable = 'import_source_setting';
|
||||
@ -34,4 +40,44 @@ class ImportSource extends DbObjectWithSettings
|
||||
->order('priority DESC')
|
||||
);
|
||||
}
|
||||
|
||||
public function checkForChanges($runImport = false)
|
||||
{
|
||||
$hadChanges = false;
|
||||
|
||||
Benchmark::measure('Starting with import ' . $this->source_name);
|
||||
try {
|
||||
$import = new Import($this);
|
||||
if ($import->providesChanges()) {
|
||||
Benchmark::measure('Found changes for ' . $this->source_name);
|
||||
$this->hadChanges = true;
|
||||
$this->import_state = 'pending-changes';
|
||||
|
||||
if ($runImport && $import->run()) {
|
||||
Benchmark::measure('Import succeeded for ' . $this->source_name);
|
||||
$this->import_state = 'in-sync';
|
||||
}
|
||||
} else {
|
||||
$this->import_state = 'in-sync';
|
||||
}
|
||||
|
||||
$this->last_error_message = null;
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->import_state = 'failing';
|
||||
Benchmark::measure('Import failed for ' . $this->source_name);
|
||||
$this->last_error_message = 'ERR: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
if ($this->hasBeenModified()) {
|
||||
$this->store();
|
||||
}
|
||||
|
||||
return $hadChanges;
|
||||
}
|
||||
|
||||
public function runImport()
|
||||
{
|
||||
return $this->checkForChanges(true);
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,11 @@
|
||||
|
||||
namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Icinga\Application\Benchmark;
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Module\Director\Data\Db\DbObject;
|
||||
use Icinga\Module\Director\Import\Sync;
|
||||
use Exception;
|
||||
|
||||
class SyncRule extends DbObject
|
||||
{
|
||||
@ -14,14 +17,19 @@ class SyncRule extends DbObject
|
||||
protected $autoincKeyName = 'id';
|
||||
|
||||
protected $defaultProperties = array(
|
||||
'id' => null,
|
||||
'rule_name' => null,
|
||||
'object_type' => null,
|
||||
'update_policy' => null,
|
||||
'purge_existing' => null,
|
||||
'filter_expression' => null,
|
||||
'id' => null,
|
||||
'rule_name' => null,
|
||||
'object_type' => null,
|
||||
'update_policy' => null,
|
||||
'purge_existing' => null,
|
||||
'filter_expression' => null,
|
||||
'sync_state' => 'unknown',
|
||||
'last_error_message' => null,
|
||||
'last_attempt' => null,
|
||||
);
|
||||
|
||||
private $sync;
|
||||
|
||||
private $filter;
|
||||
|
||||
public function listInvolvedSourceIds()
|
||||
@ -67,6 +75,55 @@ class SyncRule extends DbObject
|
||||
return $this->filter()->matches($row);
|
||||
}
|
||||
|
||||
public function checkForChanges($apply = false)
|
||||
{
|
||||
$hadChanges = false;
|
||||
|
||||
Benchmark::measure('Checking sync rule ' . $this->rule_name);
|
||||
try {
|
||||
$sync = $this->sync();
|
||||
if ($sync->hasModifications()) {
|
||||
Benchmark::measure('Got modifications for sync rule ' . $this->rule_name);
|
||||
$this->sync_state = 'pending-changes';
|
||||
if ($apply && $sync->apply()) {
|
||||
Benchmark::measure('Successfully synced rule ' . $rule->rule_name);
|
||||
$this->sync_state = 'in-sync';
|
||||
}
|
||||
|
||||
$hadChanges = true;
|
||||
|
||||
} else {
|
||||
Benchmark::measure('No modifications for sync rule ' . $this->rule_name);
|
||||
$this->sync_state = 'in-sync';
|
||||
}
|
||||
|
||||
$this->last_error_message = null;
|
||||
} catch (Exception $e) {
|
||||
$this->sync_state = 'failing';
|
||||
$this->last_error_message = $e->getMessage();
|
||||
}
|
||||
|
||||
if ($this->hasBeenModified()) {
|
||||
$this->store();
|
||||
}
|
||||
|
||||
return $hadChanges;
|
||||
}
|
||||
|
||||
public function applyChanges()
|
||||
{
|
||||
return $this->checkForChanges(true);
|
||||
}
|
||||
|
||||
protected function sync()
|
||||
{
|
||||
if ($this->sync === null) {
|
||||
$this->sync = new Sync($this);
|
||||
}
|
||||
|
||||
return $this->sync;
|
||||
}
|
||||
|
||||
protected function filter()
|
||||
{
|
||||
if ($this->filter === null) {
|
||||
|
@ -152,6 +152,12 @@ abstract class ActionController extends Controller
|
||||
'label' => $this->translate('Sync rule'),
|
||||
'url' => 'director/list/syncrule'
|
||||
)
|
||||
)->add(
|
||||
'jobs',
|
||||
array(
|
||||
'label' => $this->translate('Jobs'),
|
||||
'url' => 'director/jobs'
|
||||
)
|
||||
);
|
||||
return $this->view->tabs;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use Exception;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Exception\InvalidPropertyException;
|
||||
use Icinga\Exception\NotFoundError;
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
|
||||
use Icinga\Module\Director\Objects\IcingaObject;
|
||||
use Icinga\Web\Url;
|
||||
|
||||
@ -103,23 +104,22 @@ abstract class ObjectController extends ActionController
|
||||
$type = $this->getType();
|
||||
$this->getTabs()->activate('render');
|
||||
$object = $this->object;
|
||||
$this->view->isDisabled = $object->disabled === 'y';
|
||||
$this->view->isExternal = $object->isExternal();
|
||||
|
||||
if ($this->params->shift('resolved')) {
|
||||
$this->view->object = $object::fromPlainObject(
|
||||
$object = $object::fromPlainObject(
|
||||
$object->toPlainObject(true),
|
||||
$object->getConnection()
|
||||
);
|
||||
|
||||
if ($object->imports()->count() > 0) {
|
||||
$this->view->actionLinks = $this->view->qlink(
|
||||
$this->translate('Show normal'),
|
||||
$this->getRequest()->getUrl()->without('resolved'),
|
||||
null,
|
||||
array('class' => 'icon-resize-small state-warning')
|
||||
);
|
||||
}
|
||||
$this->view->actionLinks = $this->view->qlink(
|
||||
$this->translate('Show normal'),
|
||||
$this->getRequest()->getUrl()->without('resolved'),
|
||||
null,
|
||||
array('class' => 'icon-resize-small state-warning')
|
||||
);
|
||||
} else {
|
||||
$this->view->object = $object;
|
||||
|
||||
if ($object->supportsImports() && $object->imports()->count() > 0) {
|
||||
$this->view->actionLinks = $this->view->qlink(
|
||||
@ -131,6 +131,18 @@ abstract class ObjectController extends ActionController
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->view->isExternal) {
|
||||
$object->object_type = 'object';
|
||||
}
|
||||
|
||||
if ($this->view->isDisabledd) {
|
||||
$object->disabled = 'n';
|
||||
}
|
||||
|
||||
$this->view->object = $object;
|
||||
$this->view->config = new IcingaConfig($this->db());
|
||||
$object->renderToConfig($this->view->config);
|
||||
|
||||
$this->view->title = sprintf(
|
||||
$this->translate('Config preview: %s'),
|
||||
$object->object_name
|
||||
@ -227,10 +239,9 @@ abstract class ObjectController extends ActionController
|
||||
$type = $this->getType();
|
||||
|
||||
$this->getTabs()->activate('fields');
|
||||
$title = $this->translate('%s template "%s": custom fields');
|
||||
|
||||
$this->view->title = sprintf(
|
||||
$title,
|
||||
$this->translate(ucfirst($type)),
|
||||
$this->translate('Custom fields: %s'),
|
||||
$object->object_name
|
||||
);
|
||||
|
||||
|
@ -34,9 +34,11 @@ span.disabled {
|
||||
color: @gray-light;
|
||||
}
|
||||
|
||||
.controls span a {
|
||||
color: @icinga-blue;
|
||||
margin-right: 1em;
|
||||
.controls span.action-links {
|
||||
a {
|
||||
color: @icinga-blue;
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
pre.disabled {
|
||||
@ -404,9 +406,15 @@ a:hover::before {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h1 {
|
||||
min-width: 27em;
|
||||
}
|
||||
|
||||
ul.main-actions {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-width: 36em;
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
|
||||
@ -415,7 +423,6 @@ ul.main-actions {
|
||||
padding: 0;
|
||||
clear: both;
|
||||
width: 19em;
|
||||
min-width: 16em;
|
||||
vertical-align: top;
|
||||
|
||||
a {
|
||||
@ -450,7 +457,6 @@ ul.main-actions {
|
||||
}
|
||||
|
||||
padding: 1em;
|
||||
font-size: 1.1em;
|
||||
color: #666;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
@ -477,8 +483,9 @@ ul.main-actions {
|
||||
#layout.poor-layout ul.main-actions {
|
||||
li {
|
||||
a { height: 12em; }
|
||||
width: 18em;
|
||||
> a > i {
|
||||
font-size: 2.4em;
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -487,7 +494,7 @@ ul.main-actions {
|
||||
#layout.twocols ul.main-actions {
|
||||
li {
|
||||
a { height: 12em; }
|
||||
width: 16em;
|
||||
width: 17em;
|
||||
> a > i {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
@ -722,7 +729,6 @@ table.tinystats {
|
||||
}
|
||||
|
||||
/* Simple table, test */
|
||||
|
||||
table.syncstate {
|
||||
tr td:first-child {
|
||||
padding-left: 2em;
|
||||
@ -753,6 +759,41 @@ table.syncstate {
|
||||
}
|
||||
}
|
||||
|
||||
table.jobs {
|
||||
tr td:first-child {
|
||||
padding-left: 2em;
|
||||
&::before {
|
||||
font-family: 'ifont';
|
||||
// icon-help:
|
||||
content: '\e85b';
|
||||
float: left;
|
||||
font-weight: bold;
|
||||
margin-left: -1.5em;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
tr.ok td:first-child::before {
|
||||
content: '\e803';
|
||||
color: @color-ok;
|
||||
}
|
||||
|
||||
tr.warning td:first-child::before {
|
||||
content: '\e864';
|
||||
color: @color-warning;
|
||||
}
|
||||
|
||||
tr.pending td:first-child::before {
|
||||
content: '\e864';
|
||||
color: @color-pending;
|
||||
}
|
||||
|
||||
tr.critical td:first-child::before {
|
||||
content: '\e804';
|
||||
color: @color-critical;
|
||||
}
|
||||
}
|
||||
|
||||
table.icinga-objects {
|
||||
tr td:first-child {
|
||||
padding-left: 2em;
|
||||
@ -926,7 +967,8 @@ table.activity-log {
|
||||
}
|
||||
|
||||
tr.undeployed td, tr.undeployed a {
|
||||
font-weight: bold;
|
||||
color: @gray;
|
||||
background-color: @gray-lightest;
|
||||
}
|
||||
|
||||
tr.undeployed td:first-child::before {
|
||||
@ -934,6 +976,50 @@ table.activity-log {
|
||||
}
|
||||
}
|
||||
|
||||
table.config-diff {
|
||||
|
||||
tr th:first-child {
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
tr td:first-child {
|
||||
padding-left: 2em;
|
||||
&::before {
|
||||
font-family: 'ifont';
|
||||
// icon-help:
|
||||
content: '\e85b';
|
||||
float: left;
|
||||
font-weight: bold;
|
||||
margin-left: -1.5em;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
tr.file-unmodified td:first-child::before {
|
||||
// icon-ok
|
||||
color: @color-ok;
|
||||
content: '\e803';
|
||||
}
|
||||
|
||||
tr.file-created td:first-child::before {
|
||||
// icon-plus
|
||||
color: @color-pending;
|
||||
content: '\e805';
|
||||
}
|
||||
|
||||
tr.file-removed td:first-child::before {
|
||||
// icon-cancel
|
||||
color: @color-critical;
|
||||
content: '\e804';
|
||||
}
|
||||
|
||||
tr.file-modified td:first-child::before {
|
||||
// icon-flapping
|
||||
color: @color-warning;
|
||||
content: '\e85d';
|
||||
}
|
||||
}
|
||||
|
||||
.tree li a {
|
||||
display: inline-block;
|
||||
padding-left: 2.4em;
|
||||
|
5
run.php
5
run.php
@ -32,6 +32,11 @@ $this->provideHook('director/PropertyModifier', $prefix . 'PropertyModifier\\Pro
|
||||
$this->provideHook('director/PropertyModifier', $prefix . 'PropertyModifier\\PropertyModifierFromAdSid');
|
||||
$this->provideHook('director/PropertyModifier', $prefix . 'PropertyModifier\\PropertyModifierFromLatin1');
|
||||
|
||||
$this->provideHook('director/Job', $prefix . 'Job\\HousekeepingJob');
|
||||
$this->provideHook('director/Job', $prefix . 'Job\\ConfigJob');
|
||||
$this->provideHook('director/Job', $prefix . 'Job\\ImportJob');
|
||||
$this->provideHook('director/Job', $prefix . 'Job\\SyncJob');
|
||||
|
||||
if (Icinga::app()->isCli()) {
|
||||
return;
|
||||
}
|
||||
|
22
schema/mysql-migrations/upgrade_93.sql
Normal file
22
schema/mysql-migrations/upgrade_93.sql
Normal file
@ -0,0 +1,22 @@
|
||||
ALTER TABLE sync_rule
|
||||
ADD COLUMN sync_state ENUM(
|
||||
'unknown',
|
||||
'in-sync',
|
||||
'pending-changes',
|
||||
'failing'
|
||||
) NOT NULL DEFAULT 'unknown',
|
||||
ADD COLUMN last_error_message VARCHAR(255) DEFAULT NULL,
|
||||
ADD COLUMN last_attempt DATETIME DEFAULT NULL
|
||||
;
|
||||
|
||||
UPDATE sync_rule r
|
||||
JOIN (
|
||||
SELECT rule_id, MAX(start_time) AS start_time
|
||||
FROM sync_run
|
||||
GROUP BY rule_id
|
||||
) lr ON r.id = lr.rule_id
|
||||
SET r.last_attempt = lr.start_time;
|
||||
|
||||
INSERT INTO director_schema_migration
|
||||
(schema_version, migration_time)
|
||||
VALUES (93, NOW());
|
29
schema/mysql-migrations/upgrade_94.sql
Normal file
29
schema/mysql-migrations/upgrade_94.sql
Normal file
@ -0,0 +1,29 @@
|
||||
CREATE TABLE director_job (
|
||||
id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
|
||||
job_name VARCHAR(64) NOT NULL,
|
||||
job_class VARCHAR(72) NOT NULL,
|
||||
disabled ENUM('y', 'n') NOT NULL DEFAULT 'n',
|
||||
run_interval INT(10) UNSIGNED NOT NULL, -- seconds
|
||||
last_attempt_succeeded ENUM('y', 'n') DEFAULT NULL,
|
||||
ts_last_attempt DATETIME DEFAULT NULL,
|
||||
ts_last_error DATETIME DEFAULT NULL,
|
||||
last_error_message TEXT,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (job_name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE director_job_setting (
|
||||
job_id INT UNSIGNED NOT NULL,
|
||||
setting_name VARCHAR(64) NOT NULL,
|
||||
setting_value TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (job_id, setting_name),
|
||||
CONSTRAINT job_settings
|
||||
FOREIGN KEY director_job (job_id)
|
||||
REFERENCES director_job (id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
INSERT INTO director_schema_migration
|
||||
(schema_version, migration_time)
|
||||
VALUES (94, NOW());
|
22
schema/mysql-migrations/upgrade_95.sql
Normal file
22
schema/mysql-migrations/upgrade_95.sql
Normal file
@ -0,0 +1,22 @@
|
||||
ALTER TABLE import_source
|
||||
ADD COLUMN import_state ENUM(
|
||||
'unknown',
|
||||
'in-sync',
|
||||
'pending-changes',
|
||||
'failing'
|
||||
) NOT NULL DEFAULT 'unknown',
|
||||
ADD COLUMN last_error_message TEXT DEFAULT NULL,
|
||||
ADD COLUMN last_attempt DATETIME DEFAULT NULL
|
||||
;
|
||||
|
||||
UPDATE import_source s
|
||||
JOIN (
|
||||
SELECT source_id, MAX(start_time) AS start_time
|
||||
FROM import_run
|
||||
GROUP BY source_id
|
||||
) ir ON s.id = ir.source_id
|
||||
SET s.last_attempt = ir.start_time;
|
||||
|
||||
INSERT INTO director_schema_migration
|
||||
(schema_version, migration_time)
|
||||
VALUES (95, NOW());
|
@ -140,6 +140,32 @@ CREATE TABLE director_datafield_setting (
|
||||
ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE director_job (
|
||||
id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
|
||||
job_name VARCHAR(64) NOT NULL,
|
||||
job_class VARCHAR(72) NOT NULL,
|
||||
disabled ENUM('y', 'n') NOT NULL DEFAULT 'n',
|
||||
run_interval INT(10) UNSIGNED NOT NULL, -- seconds
|
||||
last_attempt_succeeded ENUM('y', 'n') DEFAULT NULL,
|
||||
ts_last_attempt DATETIME DEFAULT NULL,
|
||||
ts_last_error DATETIME DEFAULT NULL,
|
||||
last_error_message TEXT,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (job_name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE director_job_setting (
|
||||
job_id INT UNSIGNED NOT NULL,
|
||||
setting_name VARCHAR(64) NOT NULL,
|
||||
setting_value TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (job_id, setting_name),
|
||||
CONSTRAINT job_settings
|
||||
FOREIGN KEY director_job (job_id)
|
||||
REFERENCES director_job (id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE director_schema_migration (
|
||||
schema_version SMALLINT UNSIGNED NOT NULL,
|
||||
migration_time DATETIME NOT NULL,
|
||||
@ -1081,6 +1107,14 @@ CREATE TABLE import_source (
|
||||
source_name VARCHAR(64) NOT NULL,
|
||||
key_column VARCHAR(64) NOT NULL,
|
||||
provider_class VARCHAR(72) NOT NULL,
|
||||
import_state ENUM(
|
||||
'unknown',
|
||||
'in-sync',
|
||||
'pending-changes',
|
||||
'failing'
|
||||
) NOT NULL DEFAULT 'unknown',
|
||||
last_error_message TEXT DEFAULT NULL,
|
||||
last_attempt DATETIME DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
INDEX search_idx (key_column)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
@ -1214,6 +1248,14 @@ CREATE TABLE sync_rule (
|
||||
update_policy ENUM('merge', 'override', 'ignore') NOT NULL,
|
||||
purge_existing ENUM('y', 'n') NOT NULL DEFAULT 'n',
|
||||
filter_expression TEXT DEFAULT NULL,
|
||||
sync_state ENUM(
|
||||
'unknown',
|
||||
'in-sync',
|
||||
'pending-changes',
|
||||
'failing'
|
||||
) NOT NULL DEFAULT 'unknown',
|
||||
last_error_message VARCHAR(255) DEFAULT NULL,
|
||||
last_attempt DATETIME DEFAULT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
@ -1260,4 +1302,4 @@ CREATE TABLE sync_run (
|
||||
|
||||
INSERT INTO director_schema_migration
|
||||
SET migration_time = NOW(),
|
||||
schema_version = 92;
|
||||
schema_version = 95;
|
||||
|
@ -284,7 +284,7 @@ class IcingaHostTest extends BaseTestCase
|
||||
$config = new IcingaConfig($db);
|
||||
$host->renderToConfig($config);
|
||||
$this->assertEquals(
|
||||
array('zones.d/master.conf'),
|
||||
array('zones.d/master/hosts.conf'),
|
||||
$config->getFileNames()
|
||||
);
|
||||
|
||||
@ -295,7 +295,7 @@ class IcingaHostTest extends BaseTestCase
|
||||
$host->zone = '___TEST___zone';
|
||||
$host->renderToConfig($config);
|
||||
$this->assertEquals(
|
||||
array('zones.d/___TEST___zone.conf'),
|
||||
array('zones.d/___TEST___zone/hosts.conf'),
|
||||
$config->getFileNames()
|
||||
);
|
||||
|
||||
@ -306,7 +306,7 @@ class IcingaHostTest extends BaseTestCase
|
||||
$config = new IcingaConfig($db);
|
||||
$host->renderToConfig($config);
|
||||
$this->assertEquals(
|
||||
array('zones.d/___TEST___zone.conf'),
|
||||
array('zones.d/___TEST___zone/hosts.conf'),
|
||||
$config->getFileNames()
|
||||
);
|
||||
|
||||
@ -316,7 +316,7 @@ class IcingaHostTest extends BaseTestCase
|
||||
$config = new IcingaConfig($db);
|
||||
$host->renderToConfig($config);
|
||||
$this->assertEquals(
|
||||
array('zones.d/director-global.conf'),
|
||||
array('zones.d/director-global/host_templates.conf'),
|
||||
$config->getFileNames()
|
||||
);
|
||||
|
||||
|
@ -204,7 +204,7 @@ class IcingaServiceTest extends BaseTestCase
|
||||
$config = new IcingaConfig($db);
|
||||
$service->renderToConfig($config);
|
||||
$this->assertEquals(
|
||||
array('zones.d/master.conf'),
|
||||
array('zones.d/master/services.conf'),
|
||||
$config->getFileNames()
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user