diff --git a/application/controllers/DatalistentryController.php b/application/controllers/DatalistentryController.php index 5d040f8f..5916dccc 100644 --- a/application/controllers/DatalistentryController.php +++ b/application/controllers/DatalistentryController.php @@ -11,19 +11,13 @@ class Director_DatalistentryController extends ActionController public function editAction() { - $this->indexAction(); + $this->indexAction(true); } public function indexAction($edit = false) { $request = $this->getRequest(); - if ($request->getParam('edit')) { - $edit = true; - } else { - $edit = false; - } - $listId = $this->params->get('list_id'); $this->view->lastId = $listId; @@ -46,6 +40,7 @@ class Director_DatalistentryController extends ActionController } $form = $this->view->form = $this->loadForm('directorDatalistentry') + ->setListId($listId) ->setSuccessUrl('director/datalistentry' . '?list_id=' . $listId) ->setDb($this->db()); diff --git a/application/controllers/HosttemplatesController.php b/application/controllers/HosttemplatesController.php new file mode 100644 index 00000000..49b15307 --- /dev/null +++ b/application/controllers/HosttemplatesController.php @@ -0,0 +1,7 @@ +schemas = array( + 'mysql' => $this->translate('MySQL schema'), + 'pgsql' => $this->translate('PostgreSQL schema'), + ); + } + + protected function tabs() + { + $tabs = $this->getTabs(); + foreach ($this->schemas as $type => $title) { + $tabs->add($type, array( + 'url' => 'director/schema/' . $type, + 'label' => $title, + )); + } + return $tabs; + } + + public function mysqlAction() + { + $this->serveSchema('mysql'); + } + + public function pgsqlAction() + { + $this->serveSchema('pgsql'); + } + + protected function serveSchema($type) + { + $schema = file_get_contents( + sprintf( + '%s/schema/%s.sql', + $this->Module()->getBasedir(), + $type + ) + ); + + if ($this->params->get('format') === 'sql') { + header('Content-type: application/octet-stream'); + header('Content-Disposition: attachment; filename=' . $type . '.sql'); + echo $schema; + exit; + // TODO: Shutdown + } else { + $this->tabs()->activate($type); + $this->view->title = $this->schemas[$type]; + $this->view->schema = $schema; + $this->render('schema'); + } + } +} diff --git a/application/controllers/ServicetemplatesController.php b/application/controllers/ServicetemplatesController.php new file mode 100644 index 00000000..0434ecaa --- /dev/null +++ b/application/controllers/ServicetemplatesController.php @@ -0,0 +1,7 @@ +view->tabs = $this->Module()->getConfigTabs()->activate('config'); + $this->view->tabs = $this->Module() + ->getConfigTabs() + ->activate('config'); - $resource = $this->Config()->get('db', 'resource'); - - $form = new ConfigForm(); - - $form->setIniConfig($this->Config('config')); - $form->addElement('select', 'resource', array( - 'required' => true, - 'label' => $this->translate('DB Resource'), - 'multiOptions' => array(null => $this->translate('- please choose -')) + $this->getResources(), - 'value' => $resource - )); - $form->setSubmitLabel($this->translate('Save')); - - $form->setOnSuccess(function($form) { - /** @var $form ConfigForm */ - $this->Config('config')->setSection('db', array( - 'resource' => $form->getValue('resource') - )); - $form->save(); - }); - - $form->handleRequest(); - - $this->view->form = $form; - } - - public function getResources() - { - $resources = array(); - foreach (ResourceFactory::getResourceConfigs() as $name => $resource) { - if ($resource->type === 'db') { - $resources['ido'][$name] = $name; - } - } - return $resources; + $this->view->form = $this->loadForm('config') + ->setModuleConfig($this->Config()) + ->handleRequest(); } } diff --git a/application/forms/ConfigForm.php b/application/forms/ConfigForm.php new file mode 100644 index 00000000..4c9be27c --- /dev/null +++ b/application/forms/ConfigForm.php @@ -0,0 +1,152 @@ +enumResources(); + + $this->addElement('select', 'resource', array( + 'required' => true, + 'label' => $this->translate('DB Resource'), + 'multiOptions' => $this->optionalEnum($resources), + 'class' => 'autosubmit', + 'value' => $this->config()->get('db', 'resource') + )); + + if (empty($resources)) { + $this->getElement('resource')->addError( + $this->translate('This has to be a MySQL or PostgreSQL database') + ); + + $hint = $this->translate('Please click %s to create new DB resources'); + $link = $this->getView()->qlink( + $this->translate('here'), + 'config/resource', + null, + array('data-base-target' => '_main') + ); + $this->addHtmlHint(sprintf($hint, $link)); + } + + $this->setSubmitLabel($this->translate('Store configuration')); + } + + protected function onSetup() + { + if ($this->hasBeenSubmitted()) { + // Do not hinder the form from being stored + return; + } + + if ($this->hasBeenSent() && $this->isValidPartial($this->getRequest()->getPost())) { + $resourceName = $this->getValue('resource'); + } else { + $resourceName = $this->config()->get('db', 'resource'); + } + + if ($resourceName) { + $resource = ResourceFactory::create($resourceName); + $db = $resource->getDbAdapter(); + + try { + $query = $db->select()->from('director_dbversion', 'COUNT(*)'); + $db->fetchOne($query); + + if (! $this->hasBeenSent()) { + $hint = $this->translate( + 'Configuration looks good, you should be ready to %s' + . ' Icinga Director' + ); + $link = $this->getView()->qlink( + $this->translate('start using'), + 'director/welcome', + null, + array('data-base-target' => '_main') + ); + $this->addHtmlHint(sprintf($hint, $link)); + $this->moveSubmitToBottom(); + } + + } catch (Exception $e) { + $this->getElement('resource') + ->addError('Could not fetch: ' . $e->getMessage()) + ->removeDecorator('description'); + + $hint = $this->translate( + 'Please make sure that your database grants enough permissions' + . ' and that you deployed the correct %s.' + ); + $link = $this->getView()->qlink( + $this->translate('database schema'), + 'director/schema/' . $resource->getDbType(), + null, + array('data-base-target' => '_next') + ); + $this->addHtmlHint(sprintf($hint, $link)); + $this->moveSubmitToBottom(); + } + } + } + + public function setModuleConfig(Config $config) + { + $this->config = $config; + return $this; + } + + public function onSuccess() + { + $config = $this->config(); + $value = $this->getValue('resource'); + + $config->setSection('db', array('resource' => $value)); + + try { + $config->saveIni(); + $this->redirectOnSuccess($this->translate('Configuration has been stored')); + } catch (Exception $e) { + $this->getElement('resource')->addError( + sprintf( + $this->translate('Unable to store the configuration to "%s"'), + $config->getConfigFile() + ) + )->removeDecorator('description'); + $this->addHtmlHint( + '
' . $config . '
' + ); + } + } + + protected function config() + { + if ($this->config === null) { + $this->config = Config::module('director'); + } + + return $this->config; + } + + protected function enumResources() + { + $resources = array(); + $allowed = array('mysql', 'pgsql'); + + foreach (ResourceFactory::getResourceConfigs() as $name => $resource) { + if ($resource->type === 'db' && in_array($resource->db, $allowed)) { + $resources[$name] = $name; + } + } + + return $resources; + } +} diff --git a/application/forms/CustomvarForm.php b/application/forms/CustomvarForm.php new file mode 100644 index 00000000..759464c9 --- /dev/null +++ b/application/forms/CustomvarForm.php @@ -0,0 +1,26 @@ +removeCsrfToken(); + $this->removeElement(self::ID); + $this->addElement('text', 'varname', array( + 'label' => $this->translate('Variable name'), + 'required' => true, + )); + + $this->addElement('text', 'varvalue', array( + 'label' => $this->translate('Value'), + )); + + // $this->addHidden('format', 'string'); // expression, json? + } +} diff --git a/application/forms/DirectorDatafieldForm.php b/application/forms/DirectorDatafieldForm.php index 71feb9a0..e6298191 100644 --- a/application/forms/DirectorDatafieldForm.php +++ b/application/forms/DirectorDatafieldForm.php @@ -7,22 +7,30 @@ use Icinga\Web\Hook; class DirectorDatafieldForm extends DirectorObjectForm { + protected $objectName = 'Data field'; + public function setup() { + $this->addHtmlHint( + $this->translate('Data fields allow you to customize input controls your custom variables.') + ); + $this->addElement('text', 'varname', array( - 'required' => true, - 'label' => $this->translate('Field name'), - 'description' => $this->translate('The unique name of the field') + 'label' => $this->translate('Field name'), + 'description' => $this->translate('The unique name of the field'), + 'required' => true, )); $this->addElement('text', 'caption', array( - 'label' => $this->translate('Caption'), + 'label' => $this->translate('Caption'), + 'required' => true, 'description' => $this->translate('The caption which should be displayed') )); $this->addElement('textarea', 'description', array( - 'label' => $this->translate('Description'), - 'description' => $this->translate('A description about the field') + 'label' => $this->translate('Description'), + 'description' => $this->translate('A description about the field'), + 'rows' => '3', )); $this->addElement('select', 'datatype', array( @@ -30,15 +38,23 @@ class DirectorDatafieldForm extends DirectorObjectForm 'description' => $this->translate('Field type'), 'required' => true, 'multiOptions' => $this->enumDataTypes(), - 'class' => 'autosubmit' + 'class' => 'autosubmit', )); - if ($class = $this->object()->datatype) { - $this->addSettings($class); - } elseif ($class = $this->getSentValue('datatype')) { + + if ($class = $this->getSentValue('datatype')) { if ($class && array_key_exists($class, $this->enumDataTypes())) { $this->addSettings($class); } + } elseif ($class = $this->object()->datatype) { + $this->addSettings($class); + } + + $this->addSettings(); + foreach ($this->object()->getSettings() as $key => $val) { + if ($el = $this->getElement($key)) { + $el->setValue($val); + } } } @@ -53,8 +69,31 @@ class DirectorDatafieldForm extends DirectorObjectForm } } + protected function clearOutdatedSettings() + { + $names = array(); + $object = $this->object(); + $global = array('varname', 'description', 'caption', 'datatype'); + + foreach ($this->getElements() as $el) { + if ($el->getIgnore()) continue; + $name = $el->getName(); + if (in_array($name, $global)) continue; + $names[$name] = $name; + } + + + foreach ($object->getSettings() as $setting => $value) { + if (! array_key_exists($setting, $names)) { + unset($object->$setting); + } + } + } + public function onSuccess() { + $this->clearOutdatedSettings(); + if ($class = $this->getValue('datatype')) { if (array_key_exists($class, $this->enumDataTypes())) { $this->addHidden('format', $class::getFormat()); @@ -64,21 +103,6 @@ class DirectorDatafieldForm extends DirectorObjectForm parent::onSuccess(); } - public function loadObject($id) - { - parent::loadObject($id); - - $this->addSettings(); - foreach ($this->object()->getSettings() as $key => $val) { - if ($el = $this->getElement($key)) { - $el->setValue($val); - } - } - $this->moveSubmitToBottom(); - - return $this; - } - protected function enumDataTypes() { $hooks = Hook::all('Director\\DataType'); diff --git a/application/forms/DirectorDatalistForm.php b/application/forms/DirectorDatalistForm.php index 475e23bd..80da505d 100644 --- a/application/forms/DirectorDatalistForm.php +++ b/application/forms/DirectorDatalistForm.php @@ -3,22 +3,21 @@ namespace Icinga\Module\Director\Forms; use Icinga\Module\Director\Web\Form\DirectorObjectForm; -use Icinga\Authentication\Manager as Auth; +use Icinga\Authentication\Auth; class DirectorDatalistForm extends DirectorObjectForm { public function setup() { $this->addElement('text', 'list_name', array( - 'label' => $this->translate('List name') + 'label' => $this->translate('List name'), + 'required' => true, )); - - $this->addElement('hidden', 'owner'); } public function onSuccess() { - $this->addHidden('owner', self::username()); + $this->object()->owner = self::username(); parent::onSuccess(); } diff --git a/application/forms/DirectorDatalistentryForm.php b/application/forms/DirectorDatalistentryForm.php index 63ee5506..2e411a4a 100644 --- a/application/forms/DirectorDatalistentryForm.php +++ b/application/forms/DirectorDatalistentryForm.php @@ -6,6 +6,8 @@ use Icinga\Module\Director\Web\Form\DirectorObjectForm; class DirectorDatalistEntryForm extends DirectorObjectForm { + protected $listId; + public function setup() { $this->addElement('text', 'entry_name', array( @@ -18,9 +20,17 @@ class DirectorDatalistEntryForm extends DirectorObjectForm 'label' => 'Type', 'multiOptions' => array('string' => $this->translate('String')) )); + } - $this->addElement('hidden', 'list_id', array( - 'value' => $this->getRequest()->getParam('list_id'), - )); + public function onSuccess() + { + $this->object()->list_id = $this->listId; + parent::onSuccess(); + } + + public function setListId($id) + { + $this->listId = $id; + return $this; } } diff --git a/application/forms/IcingaCommandArgumentForm.php b/application/forms/IcingaCommandArgumentForm.php index 19eec1bf..c4f51b93 100644 --- a/application/forms/IcingaCommandArgumentForm.php +++ b/application/forms/IcingaCommandArgumentForm.php @@ -8,10 +8,10 @@ class IcingaCommandArgumentForm extends DirectorObjectForm { public function setup() { - $this->addElement('select', 'command_id', array( - 'label' => $this->translate('Check command'), - 'description' => $this->translate('Check command definition') + 'label' => $this->translate('Check command'), + 'description' => $this->translate('Check command definition'), + 'multiOptions' => $this->optionalEnum($this->db->enumCommands()) )); $this->addElement('text', 'argument_name', array( @@ -26,7 +26,6 @@ class IcingaCommandArgumentForm extends DirectorObjectForm )); $this->addHidden('value_format', 'string'); // expression, json? - } diff --git a/application/forms/IcingaEndpointForm.php b/application/forms/IcingaEndpointForm.php index f792eac3..718116fb 100644 --- a/application/forms/IcingaEndpointForm.php +++ b/application/forms/IcingaEndpointForm.php @@ -8,7 +8,8 @@ class IcingaEndpointForm extends DirectorObjectForm { public function setup() { - $isTemplate = isset($_POST['object_type']) && $_POST['object_type'] === 'template'; + $isTemplate = $this->getSentValue('object_type') === 'template'; + $this->addElement('select', 'object_type', array( 'label' => $this->translate('Object type'), 'description' => $this->translate('Whether this should be a template'), @@ -48,15 +49,7 @@ class IcingaEndpointForm extends DirectorObjectForm 'description' => $this->translate('The log duration time.') )); - $this->addElement('select', 'zone_id', array( - 'label' => $this->translate('Cluster Zone'), - 'description' => $this->translate('Check this host in this specific Icinga cluster zone'), - 'required' => true - )); - - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited endpoint template names') - )); + $this->addZoneElement() + ->addImportsElement(); } } diff --git a/application/forms/IcingaHostFieldForm.php b/application/forms/IcingaHostFieldForm.php index fb2f9713..5c7b8f08 100644 --- a/application/forms/IcingaHostFieldForm.php +++ b/application/forms/IcingaHostFieldForm.php @@ -9,21 +9,21 @@ class IcingaHostFieldForm extends DirectorObjectForm public function setup() { $this->addElement('select', 'host_id', array( - 'label' => 'Host Tpl', - 'description' => 'Host Template', - 'multiOptions' => $this->optionalEnum($this->getDb()->enumHostTemplates()) + 'label' => 'Host Tpl', + 'description' => 'Host Template', + 'multiOptions' => $this->optionalEnum($this->db->enumHostTemplates()) )); $this->addElement('select', 'datafield_id', array( - 'label' => 'Field', - 'description' => 'Field to assign', - 'multiOptions' => $this->optionalEnum($this->getDb()->enumDatafields()) + 'label' => 'Field', + 'description' => 'Field to assign', + 'multiOptions' => $this->optionalEnum($this->db->enumDatafields()) )); $this->optionalBoolean( 'is_required', $this->translate('Required'), - $this->translate('Whether this filed is required or not.') + $this->translate('Whether this field should be required or not') ); } } diff --git a/application/forms/IcingaHostForm.php b/application/forms/IcingaHostForm.php index d6f3cda2..5d7dc6be 100644 --- a/application/forms/IcingaHostForm.php +++ b/application/forms/IcingaHostForm.php @@ -9,32 +9,12 @@ class IcingaHostForm extends DirectorObjectForm { public function setup() { - $isTemplate = isset($_POST['object_type']) && $_POST['object_type'] === 'template'; - $this->addElement('select', 'object_type', array( - 'label' => $this->translate('Object type'), - 'description' => $this->translate('Whether this should be a template'), - 'multiOptions' => array( - null => '- please choose -', - 'object' => 'Host object', - 'template' => 'Host template', - ), - 'class' => 'autosubmit' + $this->addElement('text', 'object_name', array( + 'label' => $this->translate('Hostname'), + 'required' => true, + 'description' => $this->translate('Icinga object name for this host') )); - if ($isTemplate) { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Host template name'), - 'required' => true, - 'description' => $this->translate('Name for the Icinga host template you are going to create') - )); - } else { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Hostname'), - 'required' => true, - 'description' => $this->translate('Hostname for the Icinga host you are going to create') - )); - } - $this->addElement('text', 'address', array( 'label' => $this->translate('Host address'), 'description' => $this->translate('Host address. Usually an IPv4 address, but may be any kind of address your check plugin is able to deal with') @@ -45,95 +25,31 @@ class IcingaHostForm extends DirectorObjectForm 'description' => $this->translate('Usually your hosts main IPv6 address') )); - $this->addElement('select', 'check_command_id', array( - 'label' => $this->translate('Check command'), - 'description' => $this->translate('Check command definition') - )); - - $this->optionalBoolean( - 'enable_notifications', - $this->translate('Send notifications'), - $this->translate('Whether to send notifications for this host') - ); - - $this->optionalBoolean( - 'enable_active_checks', - $this->translate('Execute active checks'), - $this->translate('Whether to actively check this host') - ); - - $this->optionalBoolean( - 'enable_passive_checks', - $this->translate('Accept passive checks'), - $this->translate('Whether to accept passive check results for this host') - ); - - $this->optionalBoolean( - 'enable_event_handler', - $this->translate('Enable event handler'), - $this->translate('Whether to enable event handlers this host') - ); - - $this->optionalBoolean( - 'enable_perfdata', - $this->translate('Process performance data'), - $this->translate('Whether to process performance data provided by this host') - ); - - $this->optionalBoolean( - 'volatile', - $this->translate('Volatile'), - $this->translate('Whether this check is volatile.') - ); + $this->addImportsElement(); + /* $this->addElement('text', 'groups', array( 'label' => $this->translate('Hostgroups'), 'description' => $this->translate('One or more comma separated hostgroup names') )); + */ - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited host template names') - )); + if ($this->isTemplate()) { + $this->addElement('text', 'address', array( + 'label' => $this->translate('Host address'), + 'description' => $this->translate('Host address. Usually an IPv4 address, but may be any kind of address your check plugin is able to deal with') + )); - $this->addElement('select', 'zone_id', array( - 'label' => $this->translate('Cluster Zone'), - 'description' => $this->translate('Check this host in this specific Icinga cluster zone') - )); - } + $this->addElement('text', 'address6', array( + 'label' => $this->translate('IPv6 address'), + 'description' => $this->translate('Usually your hosts main IPv6 address') + )); - public function loadObject($id) - { - parent::loadObject($id); - - $this->addFields(); - - $this->moveSubmitToBottom(); - } - - public function addFields() - { - $fields = $this->getObject()->getFields($this); - $vars = $this->getObject()->vars(); - - foreach ($fields as $field) { - $datatype = new $field->datatype; - $datafield = DirectorDatafield::load($field->datafield_id, $this->getDb()); - $datatype->setSettings($datafield->getSettings()); - $varname = $datafield->varname; - $el = $datatype->getFormElement('var_' . $varname, $this); - $el->setLabel($field->caption); - $el->setDescription($field->description); - if (isset($vars->$varname)) { - $el->setValue($vars->{$varname}->getValue()); - } - - if ($field->is_required === 'y') - { - $el->setRequired(true); - } - - $this->addElement($el); + $this->addCheckExecutionElements(); + } else { + $this->getElement('imports')->setRequired(); } + + $this->addZoneElement(); } } diff --git a/application/forms/IcingaHostGroupForm.php b/application/forms/IcingaHostGroupForm.php index 59acf50c..31c309c9 100644 --- a/application/forms/IcingaHostGroupForm.php +++ b/application/forms/IcingaHostGroupForm.php @@ -8,39 +8,15 @@ class IcingaHostGroupForm extends DirectorObjectForm { public function setup() { - $isTemplate = isset($_POST['object_type']) && $_POST['object_type'] === 'template'; - $this->addElement('select', 'object_type', array( - 'label' => $this->translate('Object type'), - 'description' => $this->translate('Whether this should be a template'), - 'multiOptions' => array( - null => '- please choose -', - 'object' => 'Hostgroup object', - 'template' => 'Hostgroup template', - ) + $this->addElement('text', 'object_name', array( + 'label' => $this->translate('Hostgroup'), + 'required' => true, + 'description' => $this->translate('Icinga object name for this hostgroup') )); - if ($isTemplate) { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Hostgroup template name'), - 'required' => true, - 'description' => $this->translate('Hostgroup for the Icinga hostgroup template you are going to create') - )); - } else { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Hostgroup'), - 'required' => true, - 'description' => $this->translate('Hostgroup for the Icinga hostgroup you are going to create') - )); - } - $this->addElement('text', 'display_name', array( 'label' => $this->translate('Display Name'), 'description' => $this->translate('The name which should displayed.') )); - - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited hostgroup template names') - )); } } diff --git a/application/forms/IcingaHostVarForm.php b/application/forms/IcingaHostVarForm.php index 3fe0a70d..cb15bcb0 100644 --- a/application/forms/IcingaHostVarForm.php +++ b/application/forms/IcingaHostVarForm.php @@ -12,23 +12,24 @@ class IcingaHostVarForm extends DirectorObjectForm public function setup() { $this->addElement('select', 'host_id', array( - 'label' => $this->translate('Host'), - 'description' => $this->translate('The name of the host'), - 'required' => true + 'label' => $this->translate('Host'), + 'description' => $this->translate('The name of the host'), + 'multiOptions' => $this->optionalEnum($this->db->enumHosts()), + 'required' => true )); $this->addElement('text', 'varname', array( - 'label' => $this->translate('Name'), + 'label' => $this->translate('Name'), 'description' => $this->translate('host var name') )); $this->addElement('textarea', 'varvalue', array( - 'label' => $this->translate('Value'), + 'label' => $this->translate('Value'), 'description' => $this->translate('host var value') )); $this->addElement('text', 'format', array( - 'label' => $this->translate('Format'), + 'label' => $this->translate('Format'), 'description' => $this->translate('value format') )); } diff --git a/application/forms/IcingaServiceFieldForm.php b/application/forms/IcingaServiceFieldForm.php index 49c506a9..202b4fd7 100644 --- a/application/forms/IcingaServiceFieldForm.php +++ b/application/forms/IcingaServiceFieldForm.php @@ -9,21 +9,20 @@ class IcingaServiceFieldForm extends DirectorObjectForm public function setup() { $this->addElement('select', 'service_id', array( - 'label' => 'Service Tpl', - 'description' => 'Service Template', - 'multiOptions' => $this->optionalEnum($this->getDb()->enumServiceTemplates()) + 'label' => 'Service template', + 'multiOptions' => $this->optionalEnum($this->db->enumServiceTemplates()) )); $this->addElement('select', 'datafield_id', array( - 'label' => 'Field', - 'description' => 'Field to assign', - 'multiOptions' => $this->optionalEnum($this->getDb()->enumDatafields()) + 'label' => 'Field', + 'description' => 'Field to assign', + 'multiOptions' => $this->optionalEnum($this->db->enumDatafields()) )); $this->optionalBoolean( 'is_required', $this->translate('Required'), - $this->translate('Whether this filed is required or not.') + $this->translate('Whether this field should be required or not') ); } } diff --git a/application/forms/IcingaServiceForm.php b/application/forms/IcingaServiceForm.php index 2a37227b..5f94f814 100644 --- a/application/forms/IcingaServiceForm.php +++ b/application/forms/IcingaServiceForm.php @@ -8,86 +8,23 @@ class IcingaServiceForm extends DirectorObjectForm { public function setup() { - $isTemplate = isset($_POST['object_type']) && $_POST['object_type'] === 'template'; - $this->addElement('select', 'object_type', array( - 'label' => $this->translate('Object type'), - 'description' => $this->translate('Whether this should be a template'), - 'multiOptions' => array( - null => '- please choose -', - 'object' => 'Service object', - 'template' => 'Service template', - ), - 'class' => 'autosubmit' - )); - - if ($isTemplate) { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Service template name'), - 'required' => true, - 'description' => $this->translate('Name for the Icinga service template you are going to create') - )); - } else { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Servicename'), - 'required' => true, - 'description' => $this->translate('Servicename for the Icinga service you are going to create') - )); - } - - $this->addElement('select', 'check_command_id', array( - 'label' => $this->translate('Check command'), - 'description' => $this->translate('Check command definition') - )); - - $this->optionalBoolean( - 'enable_notifications', - $this->translate('Send notifications'), - $this->translate('Whether to send notifications for this service') - ); - - $this->optionalBoolean( - 'enable_active_checks', - $this->translate('Execute active checks'), - $this->translate('Whether to actively check this service') - ); - - $this->optionalBoolean( - 'enable_passive_checks', - $this->translate('Accept passive checks'), - $this->translate('Whether to accept passive check results for this service') - ); - - $this->optionalBoolean( - 'enable_event_handler', - $this->translate('Enable event handler'), - $this->translate('Whether to enable event handlers this service') - ); - - $this->optionalBoolean( - 'enable_perfdata', - $this->translate('Process performance data'), - $this->translate('Whether to process performance data provided by this service') - ); - - $this->optionalBoolean( - 'volatile', - $this->translate('Volatile'), - $this->translate('Whether this check is volatile.') - ); - - $this->addElement('select', 'zone_id', array( - 'label' => $this->translate('Cluster Zone'), - 'description' => $this->translate('Check this host in this specific Icinga cluster zone') + $this->addElement('text', 'object_name', array( + 'label' => $this->translate('Name'), + 'required' => true, + 'description' => $this->translate('Name for the Icinga object you are going to create') )); + /* $this->addElement('text', 'groups', array( 'label' => $this->translate('Servicegroups'), 'description' => $this->translate('One or more comma separated servicegroup names') )); + */ - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited service template names') - )); + $this->addImportsElement(); + + if ($this->isTemplate()) { + $this->addCheckExecutionElements(); + } } } diff --git a/application/forms/IcingaServiceGroupForm.php b/application/forms/IcingaServiceGroupForm.php index 6829d16c..9ec7e02c 100644 --- a/application/forms/IcingaServiceGroupForm.php +++ b/application/forms/IcingaServiceGroupForm.php @@ -8,39 +8,15 @@ class IcingaServiceGroupForm extends DirectorObjectForm { public function setup() { - $isTemplate = isset($_POST['object_type']) && $_POST['object_type'] === 'template'; - $this->addElement('select', 'object_type', array( - 'label' => $this->translate('Object type'), - 'description' => $this->translate('Whether this should be a template'), - 'multiOptions' => array( - null => '- please choose -', - 'object' => 'Servicegroup object', - 'template' => 'Servicegroup template', - ) + $this->addElement('text', 'object_name', array( + 'label' => $this->translate('Servicegroup'), + 'required' => true, + 'description' => $this->translate('Icinga object name for this servicegroup') )); - if ($isTemplate) { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Servicegroup template name'), - 'required' => true, - 'description' => $this->translate('Servicegroup for the Icinga servicegroup template you are going to create') - )); - } else { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Servicegroup'), - 'required' => true, - 'description' => $this->translate('Servicegroup for the Icinga servicegroup you are going to create') - )); - } - $this->addElement('text', 'display_name', array( 'label' => $this->translate('Display Name'), 'description' => $this->translate('The name which should displayed.') )); - - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited servicegroup template names') - )); } } diff --git a/application/forms/IcingaServiceVarForm.php b/application/forms/IcingaServiceVarForm.php index e0fcb74d..e7ac4a07 100644 --- a/application/forms/IcingaServiceVarForm.php +++ b/application/forms/IcingaServiceVarForm.php @@ -12,23 +12,24 @@ class IcingaServiceVarForm extends DirectorObjectForm public function setup() { $this->addElement('select', 'service_id', array( - 'label' => $this->translate('Service'), - 'description' => $this->translate('The name of the service'), - 'required' => true + 'label' => $this->translate('Service'), + 'description' => $this->translate('The name of the service'), + 'multiOptions' => $this->optionalEnum($this->db->enumServices()), + 'required' => true )); $this->addElement('text', 'varname', array( - 'label' => $this->translate('Name'), + 'label' => $this->translate('Name'), 'description' => $this->translate('service var name') )); $this->addElement('textarea', 'varvalue', array( - 'label' => $this->translate('Value'), + 'label' => $this->translate('Value'), 'description' => $this->translate('service var value') )); $this->addElement('text', 'format', array( - 'label' => $this->translate('Format'), + 'label' => $this->translate('Format'), 'description' => $this->translate('value format') )); } diff --git a/application/forms/IcingaTimePeriodForm.php b/application/forms/IcingaTimePeriodForm.php index 2470f250..51a2de5f 100644 --- a/application/forms/IcingaTimePeriodForm.php +++ b/application/forms/IcingaTimePeriodForm.php @@ -43,15 +43,7 @@ class IcingaTimePeriodForm extends DirectorObjectForm 'description' => $this->translate('the update method'), )); - $this->addElement('select', 'zone_id', array( - 'label' => $this->translate('Cluster Zone'), - 'description' => $this->translate('Check this host in this specific Icinga cluster zone'), - 'required' => true - )); - - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited timperiods template names') - )); + $this->addZoneElement(); + $this->addImportsElement(); } } diff --git a/application/forms/IcingaUserForm.php b/application/forms/IcingaUserForm.php index b94bcf1a..133c3ca5 100644 --- a/application/forms/IcingaUserForm.php +++ b/application/forms/IcingaUserForm.php @@ -49,20 +49,11 @@ class IcingaUserForm extends DirectorObjectForm $this->translate('Whether to send notifications for this user') ); - - $this->addElement('select', 'zone_id', array( - 'label' => $this->translate('Cluster Zone'), - 'description' => $this->translate('Check this user in this specific Icinga cluster zone') - )); - $this->addElement('text', 'groups', array( 'label' => $this->translate('Usergroups'), 'description' => $this->translate('One or more comma separated usergroup names') )); - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited user template names') - )); + $this->addImportsElement(); } } diff --git a/application/forms/IcingaUserGroupForm.php b/application/forms/IcingaUserGroupForm.php index ff1b15c8..aa1a7faa 100644 --- a/application/forms/IcingaUserGroupForm.php +++ b/application/forms/IcingaUserGroupForm.php @@ -8,44 +8,15 @@ class IcingaUserGroupForm extends DirectorObjectForm { public function setup() { - $isTemplate = isset($_POST['object_type']) && $_POST['object_type'] === 'template'; - $this->addElement('select', 'object_type', array( - 'label' => $this->translate('Object type'), - 'description' => $this->translate('Whether this should be a template'), - 'multiOptions' => array( - null => '- please choose -', - 'object' => 'Usergroup object', - 'template' => 'Usergroup template', - ) + $this->addElement('text', 'object_name', array( + 'label' => $this->translate('Usergroup'), + 'required' => true, + 'description' => $this->translate('Icinga object name for this usergroup') )); - if ($isTemplate) { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Usergroup template name'), - 'required' => true, - 'description' => $this->translate('Usergroup for the Icinga usergroup template you are going to create') - )); - } else { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Usergroup'), - 'required' => true, - 'description' => $this->translate('Usergroup for the Icinga usergroup you are going to create') - )); - } - $this->addElement('text', 'display_name', array( 'label' => $this->translate('Display Name'), 'description' => $this->translate('The name which should displayed.') )); - - $this->addElement('select', 'zone_id', array( - 'label' => $this->translate('Cluster Zone'), - 'description' => $this->translate('Check this usergroup in this specific Icinga cluster zone') - )); - - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited usergroup template names') - )); } } diff --git a/application/forms/IcingaZoneForm.php b/application/forms/IcingaZoneForm.php index 27c8219b..1705423c 100644 --- a/application/forms/IcingaZoneForm.php +++ b/application/forms/IcingaZoneForm.php @@ -10,7 +10,7 @@ class IcingaZoneForm extends DirectorObjectForm { $this->addElement('select', 'object_type', array( 'label' => $this->translate('Object type'), - 'description' => $this->translate('Whether this should be a template'), + 'description' => $this->translate('Whether this should be a template'), 'multiOptions' => $this->optionalEnum(array( 'object' => $this->translate('Zone object'), 'template' => $this->translate('Zone template'), @@ -24,22 +24,23 @@ class IcingaZoneForm extends DirectorObjectForm )); $this->addElement('select', 'is_global', array( - 'label' => 'Global zone', - 'description' => 'Whether this zone should be available everywhere', + 'label' => 'Global zone', + 'description' => 'Whether this zone should be available everywhere', 'multiOptions' => array( 'n' => $this->translate('No'), 'y' => $this->translate('Yes'), ), - 'required' => true, + 'required' => true, )); $this->addElement('select', 'parent_zone_id', array( - 'label' => $this->translate('Parent Zone'), - 'description' => $this->translate('Chose an (optional) parent zone') + 'label' => $this->translate('Parent Zone'), + 'description' => $this->translate('Chose an (optional) parent zone'), + 'multiOptions' => $this->optionalEnum($this->db->enumZones()) )); $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), + 'label' => $this->translate('Imports'), 'description' => $this->translate('The inherited zone template names') )); } diff --git a/application/tables/DatafieldTable.php b/application/tables/DatafieldTable.php index 72f5bd37..3b38598b 100644 --- a/application/tables/DatafieldTable.php +++ b/application/tables/DatafieldTable.php @@ -13,9 +13,11 @@ class DatafieldTable extends QuickTable public function getColumns() { return array( - 'id' => 'f.id', - 'varname' => 'f.varname', - 'datatype' => 'f.datatype', + 'id' => 'f.id', + 'varname' => 'f.varname', + 'caption' => 'f.caption', + 'description' => 'f.description', + 'datatype' => 'f.datatype', ); } @@ -28,8 +30,8 @@ class DatafieldTable extends QuickTable { $view = $this->view(); return array( - 'varname' => $view->translate('Field name'), - 'datatype' => $view->translate('Data type'), + 'caption' => $view->translate('Label'), + 'varname' => $view->translate('Field name'), ); } @@ -40,7 +42,7 @@ class DatafieldTable extends QuickTable $query = $db->select()->from( array('f' => 'director_datafield'), array() - )->order('varname ASC'); + )->order('caption ASC'); return $query; } diff --git a/application/tables/IcingaHostTemplateTable.php b/application/tables/IcingaHostTemplateTable.php new file mode 100644 index 00000000..191eda59 --- /dev/null +++ b/application/tables/IcingaHostTemplateTable.php @@ -0,0 +1,13 @@ +getUnfilteredQuery()->where('h.object_type = ?', 'template'); + } +} diff --git a/application/tables/IcingaServiceTable.php b/application/tables/IcingaServiceTable.php index f8019f02..27a3a625 100644 --- a/application/tables/IcingaServiceTable.php +++ b/application/tables/IcingaServiceTable.php @@ -33,7 +33,7 @@ class IcingaServiceTable extends QuickTable ); } - public function getBaseQuery() + public function getUnfilteredQuery() { $db = $this->connection()->getConnection(); $query = $db->select()->from( @@ -47,4 +47,9 @@ class IcingaServiceTable extends QuickTable return $query; } + + public function getBaseQuery() + { + return $this->getUnfilteredQuery()->where('s.object_type = ?', 'object'); + } } diff --git a/application/tables/IcingaServiceTemplateTable.php b/application/tables/IcingaServiceTemplateTable.php new file mode 100644 index 00000000..e5cbe359 --- /dev/null +++ b/application/tables/IcingaServiceTemplateTable.php @@ -0,0 +1,13 @@ +getUnfilteredQuery()->where('s.object_type = ?', 'template'); + } +} diff --git a/application/views/scripts/hosttemplates/tree.phtml b/application/views/scripts/hosttemplates/tree.phtml new file mode 100644 index 00000000..a97bba7a --- /dev/null +++ b/application/views/scripts/hosttemplates/tree.phtml @@ -0,0 +1,65 @@ +children); + if ($level === 0) { + $class = 'root'; + } else { + $class = 'host'; + } + + if ($hasChildren) { + $collapsed = ''; + } else { + $collapsed = ' class="collapsed"'; + } + + $html = ''; + if ($hasChildren) { + ksort($tree->children); + $html .= ' '; + } + + if ($level === 0) { + $html .= '' . $self->escape($tree->name) . ''; + } else { + $html .= $self->qlink( + $tree->name, + 'director/host', + array('name' => $tree->name), + array('class' => $class) + ); + } + + if ($hasChildren) { + $html .= ''; + } + $html .= "\n"; + return $html; +} + +?> +
+tabs ?> +

translate('Host template tree') ?>

+
+ +
+ +
diff --git a/application/views/scripts/schema/schema.phtml b/application/views/scripts/schema/schema.phtml new file mode 100644 index 00000000..1f900f5e --- /dev/null +++ b/application/views/scripts/schema/schema.phtml @@ -0,0 +1,10 @@ +
+ +

escape($title) ?>

+icon('download') ?>Download +
+ +
+
escape($schema) ?>
+
+ diff --git a/application/views/scripts/settings/index.phtml b/application/views/scripts/settings/index.phtml index 37d437e0..9120812c 100644 --- a/application/views/scripts/settings/index.phtml +++ b/application/views/scripts/settings/index.phtml @@ -2,6 +2,6 @@ tabs ?> -
- +
+
diff --git a/application/views/scripts/show/activitylog-create.phtml b/application/views/scripts/show/activitylog-create.phtml index 67efbc6a..5f92930d 100644 --- a/application/views/scripts/show/activitylog-create.phtml +++ b/application/views/scripts/show/activitylog-create.phtml @@ -15,6 +15,8 @@ foreach ($new as $key => $value) { if ($value === null) continue; + if (is_array($value)) $value = implode(', ', $value); + echo ' ' . $this->escape($key) . ''; echo $this->escape($value); echo "\n"; diff --git a/application/views/scripts/show/activitylog-modify.phtml b/application/views/scripts/show/activitylog-modify.phtml index f9388df4..b392988c 100644 --- a/application/views/scripts/show/activitylog-modify.phtml +++ b/application/views/scripts/show/activitylog-modify.phtml @@ -17,12 +17,19 @@ foreach ($old as $key => $value) { $modified = array_key_exists($key, $new); if ($value === null && ! $modified) continue; + + if (is_array($value)) $value = implode(', ', $value); + if (is_object($value)) $value = json_encode($value); + echo ' ' . $this->escape($key) . ''; if ($modified) { + $newval = $new->$key; + if (is_array($newval)) $newval = implode(', ', $newval); + if (is_object($newval)) $newval = json_encode($newval); printf( '%s %s', $this->escape($value), - $this->escape($new->$key) + $this->escape($newval) ); } else { echo $this->escape($value); diff --git a/application/views/scripts/welcome/index.phtml b/application/views/scripts/welcome/index.phtml index c89dd824..ac658467 100644 --- a/application/views/scripts/welcome/index.phtml +++ b/application/views/scripts/welcome/index.phtml @@ -7,5 +7,25 @@ errorMessage): ?>

errorMessage ?>

-Nothing to see here yet. We will point you to the most common tasks later on. +translate('Monitoring Nodes'), 'director/commands'), + array('host', $this->translate('Host objecs'), 'director/hosts'), + array('services', $this->translate('Monitored Services'), 'director/services'), + array('users', $this->translate('Users / Contacts'), 'director/users'), + array('chat', $this->translate('Alarms and notifications'), 'director/notificatios'), + array('database', $this->translate('Sync / Import'), 'director/list/importsource'), + array('wrench', $this->translate('Configuration'), 'director/list/generatedconfig'), +); +?> +
diff --git a/configuration.php b/configuration.php index f91ff7d1..4ca0e9c8 100644 --- a/configuration.php +++ b/configuration.php @@ -1,7 +1,20 @@ providePermission('director/templates', 'Allow to modify templates'); +$this->providePermission('director/hosts/read', $this->translate('Allow to configure hosts')); +$this->providePermission('director/hosts/write', $this->translate('Allow to configure hosts')); +$this->providePermission('director/templates/read', $this->translate('Allow to see template details')); +$this->providePermission('director/templates/write', $this->translate('Allow to configure templates')); + +$this->provideRestriction( + 'director/hosttemplates/filter', + $this->translate('Allow to use only host templates matching this filter') +); + +$this->provideRestriction( + 'director/dbresources/use', + $this->translate('Allow to use only these db resources (comma separated list)') +); + $this->provideConfigTab('config', array( 'title' => 'Configuration', @@ -12,9 +25,10 @@ $section = $this->menuSection( $this->translate('Icinga Director') )->setIcon('cubes'); +$section->add($this->translate('Overview'))->setUrl('director/welcome')->setPriority(20); $section->add($this->translate('Global'))->setUrl('director/commands'); $section->add($this->translate('Hosts'))->setUrl('director/hosts'); -$section->add($this->translate('Fields'))->setUrl('director/field/host'); +$section->add($this->translate('Fields'))->setUrl('director/field/host')->setPriority(903); $section->add($this->translate('Services'))->setUrl('director/services'); $section->add($this->translate('Users'))->setUrl('director/users'); $section->add($this->translate('Import / Sync')) diff --git a/library/Director/CustomVariable/CustomVariable.php b/library/Director/CustomVariable/CustomVariable.php index 9f620a1b..4a824cf4 100644 --- a/library/Director/CustomVariable/CustomVariable.php +++ b/library/Director/CustomVariable/CustomVariable.php @@ -45,12 +45,17 @@ abstract class CustomVariable implements IcingaConfigRenderer } // TODO: implement delete() - public function hasBeenDeleted() { return $this->deleted; } + public function delete() + { + $this->deleted = true; + return $this; + } + // TODO: abstract public function getDbValue() { diff --git a/library/Director/CustomVariable/CustomVariables.php b/library/Director/CustomVariable/CustomVariables.php index 5c830c1e..61aa6cc8 100644 --- a/library/Director/CustomVariable/CustomVariables.php +++ b/library/Director/CustomVariable/CustomVariables.php @@ -89,7 +89,12 @@ class CustomVariables implements Iterator, Countable, IcingaConfigRenderer protected function refreshIndex() { - $this->idx = array_keys($this->vars); + $this->idx = array(); + foreach ($this->vars as $name => $var) { + if (! $var->hasBeenDeleted()) { + $this->idx[] = $name; + } + } } public static function loadForStoredObject(IcingaObject $object) @@ -169,10 +174,18 @@ class CustomVariables implements Iterator, Countable, IcingaConfigRenderer public function setUnmodified() { $this->modified = false; - $this->storedVars = $this->vars; + $this->storedVars = array(); + foreach ($this->vars as $key => $var) { + $this->storedVars[$key] = clone($var); + } return $this; } + public function getOriginalVars() + { + return $this->storedVars; + } + public function toConfigString() { $out = ''; @@ -223,10 +236,11 @@ class CustomVariables implements Iterator, Countable, IcingaConfigRenderer public function __unset($key) { if (! array_key_exists($key, $this->vars)) { - throw new Exception('Trying to unset invalid key'); + return; } - unset($this->vars[$key]); + $this->vars[$key]->delete(); + $this->modified = true; $this->refreshIndex(); } diff --git a/library/Director/DataType/DataTypeSqlQuery.php b/library/Director/DataType/DataTypeSqlQuery.php index d4026dea..fa1b05a2 100644 --- a/library/Director/DataType/DataTypeSqlQuery.php +++ b/library/Director/DataType/DataTypeSqlQuery.php @@ -2,9 +2,11 @@ namespace Icinga\Module\Director\DataType; +use Exception; +use Icinga\Data\Db\DbConnection; use Icinga\Module\Director\Web\Form\QuickForm; use Icinga\Module\Director\Web\Hook\DataTypeHook; -use Icinga\Data\Db\DbConnection; +use Icinga\Module\Director\Util; class DataTypeSqlQuery extends DataTypeHook { @@ -16,11 +18,22 @@ class DataTypeSqlQuery extends DataTypeHook public function getFormElement($name, QuickForm $form) { + try { + $data = $this->fetchData(); + $error = false; + } catch (Exception $e) { + $data = array(); + $error = sprintf($form->translate('Unable to fetch data: %s'), $e->getMessage()); + } + $element = $form->createElement('select', $name, array( - 'multiOptions' => array(null => '- please choose -') + - $this->fetchData(), + 'multiOptions' => $form->optionalEnum($data), )); + if ($error) { + $element->addError($error); + } + return $element; } @@ -36,17 +49,13 @@ class DataTypeSqlQuery extends DataTypeHook public static function addSettingsFormFields(QuickForm $form) { - $db = $form->getDb(); - - $form->addElement('text', 'resource', array( - 'label' => 'Resource name', - 'required' => true, - )); + Util::addDbResourceFormElement($form, 'resource'); $form->addElement('textarea', 'query', array( 'label' => 'DB Query', 'description' => 'This query should return exactly two columns, value and label', 'required' => true, + 'rows' => 10, )); return $form; diff --git a/library/Director/Db.php b/library/Director/Db.php index 2cc86138..0c762527 100644 --- a/library/Director/Db.php +++ b/library/Director/Db.php @@ -85,50 +85,51 @@ class Db extends DbConnection return $db->fetchOne($query); } - public function fetchHostTemplateTree() + public function fetchTemplateTree($type) { $db = $this->db(); $query = $db->select()->from( - array('ph' => 'icinga_host'), + array('p' => 'icinga_' . $type), array( - 'host' => 'h.object_name', - 'parent' => 'ph.object_name' + 'name' => 'o.object_name', + 'parent' => 'p.object_name' ) )->join( - array('hi' => 'icinga_host_inheritance'), - 'ph.id = hi.parent_host_id', + array('i' => 'icinga_' . $type . '_inheritance'), + 'p.id = i.parent_' . $type . '_id', array() )->join( - array('h' => 'icinga_host'), - 'h.id = hi.host_id', + array('o' => 'icinga_' . $type), + 'o.id = i.' . $type . '_id', array() - )->where("h.object_type = 'template'") - ->order('ph.object_name') - ->order('h.object_name'); + )->where("o.object_type = 'template'") + ->order('p.object_name') + ->order('o.object_name'); $relations = $db->fetchAll($query); $children = array(); - $hosts = array(); + $objects = array(); foreach ($relations as $rel) { - foreach (array('host', 'parent') as $col) { - if (! array_key_exists($rel->$col, $hosts)) { - $hosts[$rel->$col] = (object) array( + foreach (array('name', 'parent') as $col) { + if (! array_key_exists($rel->$col, $objects)) { + $objects[$rel->$col] = (object) array( 'name' => $rel->$col, 'children' => array() ); } } } + foreach ($relations as $rel) { - $hosts[$rel->parent]->children[$rel->host] = $hosts[$rel->host]; - $children[$rel->host] = $rel->parent; + $objects[$rel->parent]->children[$rel->name] = $objects[$rel->name]; + $children[$rel->name] = $rel->parent; } - foreach ($children as $name => $host) { - unset($hosts[$name]); + foreach ($children as $name => $object) { + unset($objects[$name]); } - return $hosts; + return $objects; } public function fetchLatestImportedRows($source, $columns = null) diff --git a/library/Director/Import/ImportSourceSql.php b/library/Director/Import/ImportSourceSql.php index 0fa36481..416dae3b 100644 --- a/library/Director/Import/ImportSourceSql.php +++ b/library/Director/Import/ImportSourceSql.php @@ -2,6 +2,7 @@ namespace Icinga\Module\Director\Import; +use Icinga\Module\Director\Util; use Icinga\Module\Director\Web\Form\QuickForm; use Icinga\Module\Director\Web\Hook\ImportSourceHook; use Icinga\Data\Db\DbConnection; @@ -23,13 +24,11 @@ class ImportSourceSql extends ImportSourceHook public static function addSettingsFormFields(QuickForm $form) { - $form->addElement('text', 'resource', array( - 'label' => 'Resource name', - 'required' => true, - )); + Util::addDbResourceFormElement($form, 'resource'); $form->addElement('textarea', 'query', array( 'label' => 'DB Query', 'required' => true, + 'rows' => 15, )); return $form; } diff --git a/library/Director/Objects/DirectorActivityLog.php b/library/Director/Objects/DirectorActivityLog.php index 1ff183cc..8f4e4f38 100644 --- a/library/Director/Objects/DirectorActivityLog.php +++ b/library/Director/Objects/DirectorActivityLog.php @@ -5,7 +5,7 @@ namespace Icinga\Module\Director\Objects; use Icinga\Module\Director\Data\Db\DbObject; use Icinga\Module\Director\Db; use Icinga\Module\Director\Util; -use Icinga\Authentication\Manager as Auth; +use Icinga\Authentication\Auth; class DirectorActivityLog extends DbObject { @@ -39,6 +39,59 @@ class DirectorActivityLog extends DbObject } } + protected static function prepareNewObjectProperties(DbObject $object) + { + $props = $object->getProperties(); + if ($object->supportsCustomVars()) { + // $props->vars = $object->vars()->toJson(); + } + if ($object->supportsGroups()) { + $props['groups'] = $object->groups()->listGroupNames(); + } + if ($object->supportsCustomVars()) { + $props['vars'] = $object->getVars(); + } + + return json_encode($props); + } + + protected static function prepareModifiedProperties(DbObject $object) + { + $props = $object->getModifiedProperties(); + if ($object->supportsCustomVars()) { + $mod = array(); + foreach ($object->vars() as $name => $var) { + if ($var->hasBeenModified()) { + $mod[$name] = $var->getValue(); + } + } + if (! empty($mod)) { + $props['vars'] = (object) $mod; + } + } + if ($object->supportsGroups()) { + // $props['groups'] = $object->groups()->listGroupNames(); + } + + return json_encode($props); + } + + protected static function prepareOriginalProperties(DbObject $object) + { + $props = $object->getModifiedProperties(); + if ($object->supportsCustomVars()) { + $props['vars'] = (object) array(); + foreach ($object->vars()->getOriginalVars() as $name => $var) { + $props['vars']->$name = $var->getValue(); + } + } + if ($object->supportsGroups()) { + // $props['groups'] = $object->groups()->listGroupNames(); + } + + return json_encode($props); + } + public static function logCreation(DbObject $object, Db $db) { $data = array( @@ -46,7 +99,7 @@ class DirectorActivityLog extends DbObject 'action_name' => 'create', 'author' => self::username(), 'object_type' => $object->getTableName(), - 'new_properties' => json_encode($object->getProperties()), + 'new_properties' => self::prepareNewObjectProperties($object), 'change_time' => date('Y-m-d H:i:s'), // TODO -> postgres! 'parent_checksum' => $db->getLastActivityChecksum() ); @@ -63,8 +116,8 @@ class DirectorActivityLog extends DbObject 'action_name' => 'modify', 'author' => self::username(), 'object_type' => $object->getTableName(), - 'old_properties' => json_encode($object->getOriginalProperties()), - 'new_properties' => json_encode($object->getModifiedProperties()), + 'old_properties' => self::prepareOriginalProperties($object), + 'new_properties' => self::prepareModifiedProperties($object), 'change_time' => date('Y-m-d H:i:s'), // TODO -> postgres! 'parent_checksum' => $db->getLastActivityChecksum() ); diff --git a/library/Director/Objects/DirectorDatafield.php b/library/Director/Objects/DirectorDatafield.php index 770587a0..07627495 100644 --- a/library/Director/Objects/DirectorDatafield.php +++ b/library/Director/Objects/DirectorDatafield.php @@ -23,7 +23,6 @@ class DirectorDatafield extends DbObject protected $settings = array(); - public function set($key, $value) { if ($this->hasProperty($key)) { @@ -33,6 +32,7 @@ class DirectorDatafield extends DbObject if (! array_key_exists($key, $this->settings) || $value !== $this->settings[$key]) { $this->hasBeenModified = true; } + $this->settings[$key] = $value; return $this; } @@ -50,6 +50,20 @@ class DirectorDatafield extends DbObject return parent::get($key); } + public function __unset($key) + { + if ($this->hasProperty($key)) { + return parent::__set($key, $value); + } + + if (array_key_exists($key, $this->settings)) { + unset($this->settings[$key]); + $this->hasBeenModified = true; + } + + return $this; + } + public function getSettings() { return $this->settings; @@ -100,7 +114,7 @@ class DirectorDatafield extends DbObject } foreach ($del as $key) { - $db->update( + $db->delete( 'director_datafield_setting', $db->quoteInto($where, $key) ); diff --git a/library/Director/Objects/IcingaHost.php b/library/Director/Objects/IcingaHost.php index 7462776c..1c918573 100644 --- a/library/Director/Objects/IcingaHost.php +++ b/library/Director/Objects/IcingaHost.php @@ -43,6 +43,8 @@ class IcingaHost extends IcingaObject protected $supportsImports = true; + protected $supportsFields = true; + protected function renderCheck_command_id() { return $this->renderCommandProperty($this->check_command_id); @@ -82,22 +84,4 @@ class IcingaHost extends IcingaObject { return $this->renderBooleanProperty('volatile'); } - - public function getFields(DirectorObjectForm $form) - { - $db = $this->getDb(); - - $query = $db->select() - ->from( - array('df' => 'director_datafield') - ) - ->join( - array('hf' => 'icinga_host_field'), - 'df.id = hf.datafield_id' - ) - ->where('hf.host_id = ?', (int) $this->id) - ->order('df.caption ASC'); - - return $db->fetchAll($query); - } } diff --git a/library/Director/Objects/IcingaObject.php b/library/Director/Objects/IcingaObject.php index f41ecafa..40f2047e 100644 --- a/library/Director/Objects/IcingaObject.php +++ b/library/Director/Objects/IcingaObject.php @@ -23,6 +23,8 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer protected $supportsImports = false; + protected $supportsFields = false; + private $type; private $vars; @@ -55,6 +57,11 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer return $this->supportsImports; } + public function supportsFields() + { + return $this->supportsFields; + } + public function hasBeenModified() { if ($this->supportsCustomVars() && $this->vars !== null && $this->vars()->hasBeenModified()) { @@ -132,37 +139,143 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer return $this->importedObjects; } + public function clearImportedObjects() + { + $this->importedObjects = null; + return $this; + } + public function getResolvedProperties() { - $res = $this->resolveProperties(); - return $res['_MERGED_']; + return $this->getResolved('Properties'); + } + + public function getInheritedProperties() + { + return $this->getInherited('Properties'); + } + + public function getOriginsProperties() + { + return $this->getOrigins('Properties'); } public function resolveProperties() { - $props = array(); - $props['_MERGED_'] = (object) array(); + return $this->resolve('Properties'); + } + + public function getResolvedFields() + { + return $this->getResolved('Fields'); + } + + public function getInheritedFields() + { + return $this->getInherited('Fields'); + } + + public function getOriginsFields() + { + return $this->getOrigins('Fields'); + } + + public function resolveFields() + { + return $this->resolve('Fields'); + } + + public function getResolvedVars() + { + return $this->getResolved('Vars'); + } + + public function getInheritedVars() + { + return $this->getInherited('Vars'); + } + + public function resolveVars() + { + return $this->resolve('Vars'); + } + + public function getOriginsVars() + { + return $this->getOrigins('Vars'); + } + + public function getVars() + { + $vars = (object) array(); + foreach ($this->vars() as $key => $var) { + $vars->$key = $var->getValue(); + } + + return $vars; + } + + protected function getResolved($what) + { + $func = 'resolve' . $what; + $res = $this->$func(); + return $res['_MERGED_']; + } + + protected function getInherited($what) + { + $func = 'resolve' . $what; + $res = $this->$func(); + return $res['_INHERITED_']; + } + + protected function getOrigins($what) + { + $func = 'resolve' . $what; + $res = $this->$func(); + return $res['_ORIGINS_']; + } + + protected function resolve($what) + { + $vals = array(); + $vals['_MERGED_'] = (object) array(); + $vals['_INHERITED_'] = (object) array(); + $vals['_ORIGINS_'] = (object) array(); $objects = $this->importedObjects(); - $objects[$this->object_name] = $this; - $blacklist = array('id', 'object_type', 'object_name'); + $get = 'get' . $what; + $getInherited = 'getInherited' . $what; + $getOrigins = 'getOrigins' . $what; + foreach ($objects as $name => $object) { - $props[$name] = (object) array(); - if ($name === $this->object_name) { - $pprops = $object->getProperties(); - } else { - $pprops = $object->getResolvedProperties(); + $origins = $object->$getOrigins(); + + foreach ($object->$getInherited() as $key => $value) { + // $vals[$name]->$key = $value; + $vals['_MERGED_']->$key = $value; + $vals['_INHERITED_']->$key = $value; + $vals['_ORIGINS_']->$key = $origins->$key; } - foreach ($pprops as $key => $value) { - if (in_array($key, $blacklist)) continue; - if ($value !== null) { - $props[$name]->$key = $value; - $props['_MERGED_']->$key = $value; - } + + foreach ($object->$get() as $key => $value) { + if ($value === null) continue; + $vals['_MERGED_']->$key = $value; + $vals['_INHERITED_']->$key = $value; + $vals['_ORIGINS_']->$key = $name; } } - return $props; + $blacklist = array('id', 'object_type', 'object_name'); + foreach ($this->$get() as $key => $value) { + if ($value === null) continue; + if (in_array($key, $blacklist)) continue; + + // $vals[$this->object_name]->$key = $value; + $vals['_MERGED_']->$key = $value; + } + + return $vals; } protected function assertCustomVarsSupport() @@ -243,6 +356,39 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer return $this->getShortTableName() . '_id'; } + public function getFields() + { + $fields = (object) array(); + + if (! $this->supportsFields()) { + return $fields; + } + + $db = $this->getDb(); + + $query = $db->select()->from( + array('df' => 'director_datafield'), + array( + 'datafield_id' => 'f.datafield_id', + 'is_required' => 'f.is_required', + 'varname' => 'df.varname' + ) + )->join( + array('f' => $this->getTableName() . '_field'), + 'df.id = f.datafield_id', + array() + )->where('f.' . $this->getShortTableName() . '_id = ?', (int) $this->id) + ->order('df.caption ASC'); + + $res = $db->fetchAll($query); + + foreach ($res as $r) { + $fields->{$r->varname} = $r; + } + + return $fields; + } + public function isTemplate() { return $this->hasProperty('object_type') diff --git a/library/Director/Objects/IcingaObjectImports.php b/library/Director/Objects/IcingaObjectImports.php index b8ab2c72..470c1244 100644 --- a/library/Director/Objects/IcingaObjectImports.php +++ b/library/Director/Objects/IcingaObjectImports.php @@ -158,19 +158,9 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer if ($import instanceof $class) { $this->imports[$import->object_name] = $import; } elseif (is_string($import)) { - $query = $this->object->getDb()->select()->from( - $this->object->getTableName() - )->where('object_name = ?', $import); - $imports = $class::loadAll($connection, $query, 'object_name'); + $import = $class::load($import, $connection); + $this->imports[$import->object_name] = $import; } - if (! array_key_exists($import, $imports)) { - throw new ProgrammingError( - 'The import "%s" doesn\'t exists.', - $import - ); - } - - $this->imports[$import] = $imports[$import]; $this->modified = true; $this->refreshIndex(); diff --git a/library/Director/Objects/IcingaService.php b/library/Director/Objects/IcingaService.php index b6aa6a20..1c92d25f 100644 --- a/library/Director/Objects/IcingaService.php +++ b/library/Director/Objects/IcingaService.php @@ -38,6 +38,8 @@ class IcingaService extends IcingaObject protected $supportsCustomVars = true; + protected $supportsFields = true; + protected $supportsImports = true; protected function renderCheck_command_id() diff --git a/library/Director/Util.php b/library/Director/Util.php index 6053a74b..09e9840d 100644 --- a/library/Director/Util.php +++ b/library/Director/Util.php @@ -2,13 +2,21 @@ namespace Icinga\Module\Director; +use Icinga\Authentication\Auth; +use Icinga\Data\ResourceFactory; +use Icinga\Module\Director\Web\Form\QuickForm; +use Icinga\Web\Url; use Zend_Db_Expr; class Util { + protected static $auth; + + protected static $allowedDbResources; + public static function pgBinEscape($binary) { - return new \Zend_Db_Expr("'\\x" . bin2hex($binary) . "'"); + return new Zend_Db_Expr("'\\x" . bin2hex($binary) . "'"); } public static function hex2binary($bin) @@ -20,4 +28,84 @@ class Util { return current(unpack('H*', $hex)); } + + public static function auth() + { + if (self::$auth === null) { + self::$auth = Auth::getInstance(); + } + return self::$auth; + } + + public static function hasPermission($name) + { + return self::auth()->hasPermission($name); + } + + public static function getRestrictions($name) + { + return self::auth()->getRestrictions($name); + } + + public static function dbResourceIsAllowed($name) + { + if (self::$allowedDbResources === null) { + $restrictions = self::getRestrictions('director/dbresources/use'); + $list = array(); + foreach ($restrictions as $restriction) { + foreach (preg_split('/\s*,\s*/', $restriction, -1, PREG_SPLIT_NO_EMPTY) as $key) { + $list[$key] = $key; + } + } + + self::$allowedDbResources = $list; + } else { + $list = self::$allowedDbResources; + } + + if (empty($list) || array_key_exists($name, $list)) { + return true; + } + + return false; + } + + public static function enumDbResources() + { + $resources = array(); + foreach (ResourceFactory::getResourceConfigs() as $name => $resource) { + if ($resource->type === 'db' && self::dbResourceIsAllowed($name)) { + $resources[$name] = $name; + } + } + + return $resources; + } + + public static function addDbResourceFormElement(QuickForm $form, $name) + { + $list = Util::enumDbResources(); + + $form->addElement('select', $name, array( + 'label' => 'Resource name', + 'multiOptions' => $form->optionalEnum($list), + 'required' => true, + )); + + if (true && empty($list)) { + if (self::hasPermission('config/application/resources')) { + $hint = $form->translate('Please click %s to create new DB resources'); + $link = sprintf( + '%s', + $form->translate('here') + ); + $form->addHtmlHint(sprintf($hint, $link)); + $msg = $form->translate('No db resource available'); + } else { + $msg = $form->translate('Please ask an administrator to grant you access to DB resources'); + } + + $form->getElement($name)->addError($msg); + } + } } diff --git a/library/Director/Web/Controller/ObjectController.php b/library/Director/Web/Controller/ObjectController.php index f46b61ed..c7191e2c 100644 --- a/library/Director/Web/Controller/ObjectController.php +++ b/library/Director/Web/Controller/ObjectController.php @@ -16,18 +16,18 @@ abstract class ObjectController extends ActionController if ($name = $this->params->get('name')) { $params['name'] = $name; - $this->getTabs()->add($type, array( - 'url' => sprintf('director/%s', $ltype), - 'urlParams' => $params, - 'label' => $this->translate(ucfirst($ltype)), - ))->add('modify', array( + $this->getTabs()->add('modify', array( 'url' => sprintf('director/%s/edit', $ltype), 'urlParams' => $params, - 'label' => $this->translate('Modify') + 'label' => $this->translate(ucfirst($ltype)) ))->add('delete', array( 'url' => sprintf('director/%s/delete', $ltype), 'urlParams' => $params, 'label' => $this->translate('Delete') + ))->add('render', array( + 'url' => sprintf('director/%s/render', $ltype), + 'urlParams' => $params, + 'label' => $this->translate('Preview'), ))->add('history', array( 'url' => sprintf('director/%s/history', $ltype), 'urlParams' => $params, @@ -35,16 +35,21 @@ abstract class ObjectController extends ActionController )); } else { $this->getTabs()->add('add', array( - 'url' => sprintf('director/%s', $type), + 'url' => sprintf('director/%s/add', $type), 'label' => sprintf($this->translate('Add %s'), ucfirst($type)), )); } } public function indexAction() + { + return $this->editAction(); + } + + public function renderAction() { $type = $this->getType(); - $this->getTabs()->activate($type); + $this->getTabs()->activate('render'); $this->view->object = $this->object(); $this->render('object/show', null, true); } @@ -80,17 +85,22 @@ abstract class ObjectController extends ActionController 'icinga' . ucfirst($type) )->setDb($this->db()); $form->loadObject($this->params->get('name')); + $object = $form->getObject(); $url = Url::fromPath( sprintf('director/%s', $ltype), - array('name' => $form->getObject()->object_name) + array('name' => $object->object_name) ); $form->setSuccessUrl($url); - $this->view->title = sprintf( - $this->translate('Modify Icinga %s'), - ucfirst($ltype) - ); + if ($object->isTemplate()) { + $title = $this->translate('Modify Icinga %s template'); + $form->setObjectType('template'); // WHY?? + } else { + $title = $this->translate('Modify Icinga %s'); + } + + $this->view->title = sprintf($title, ucfirst($ltype)); $this->view->form->handleRequest(); $this->render('object/form', null, true); } @@ -102,15 +112,19 @@ abstract class ObjectController extends ActionController $ltype = strtolower($type); $url = sprintf('director/%ss', $ltype); - $this->view->form = $this->loadForm('icinga' . ucfirst($type)) + $form = $this->view->form = $this->loadForm('icinga' . ucfirst($type)) ->setDb($this->db()) ->setSuccessUrl($url); - $this->view->title = sprintf( - $this->translate('Add new Icinga %s'), - ucfirst($ltype) - ); - $this->view->form->handleRequest(); + if ($this->params->get('type') === 'template') { + $form->setObjectType('template'); + $title = $this->translate('Add new Icinga %s template'); + } else { + $title = $this->translate('Add new Icinga %s'); + } + + $this->view->title = sprintf($title, ucfirst($ltype)); + $form->handleRequest(); $this->render('object/form', null, true); } diff --git a/library/Director/Web/Controller/ObjectsController.php b/library/Director/Web/Controller/ObjectsController.php index e40c48ff..7b3e364e 100644 --- a/library/Director/Web/Controller/ObjectsController.php +++ b/library/Director/Web/Controller/ObjectsController.php @@ -16,14 +16,12 @@ abstract class ObjectsController extends ActionController public function init() { + $tabs = $this->getTabs(); $type = $this->getType(); - $ltype = strtolower($type); - - $object = $this->dummyObject(); if (in_array(ucfirst($type), $this->globalTypes)) { + $ltype = strtolower($type); - $tabs = $this->getTabs(); foreach ($this->globalTypes as $tabType) { $ltabType = strtolower($tabType); $tabs->add($ltabType, array( @@ -33,67 +31,98 @@ abstract class ObjectsController extends ActionController } $tabs->activate($ltype); - } elseif ($object->isGroup()) { - - $singleType = substr($type, 0, -5); - $tabs = $this->getTabs()->add('objects', array( - 'url' => sprintf('director/%ss', $singleType), - 'label' => $this->translate(ucfirst($singleType) . 's'), - )); - - $tabs->add('objectgroups', array( - 'url' => sprintf('director/%ss', strtolower($type)), - 'label' => $this->translate(ucfirst(strtolower($type)) . 's') - )); - - } else { - - $tabs = $this->getTabs()->add('objects', array( - 'url' => sprintf('director/%ss', strtolower($type)), - 'label' => $this->translate(ucfirst($type) . 's'), - )); - if ($object->supportsGroups()) { - $tabs->add('objectgroups', array( - 'url' => sprintf('director/%sgroups', $type), - 'label' => $this->translate(ucfirst($type) . 'groups') - )); - } - + return; } + + + $object = $this->dummyObject(); + if ($object->isGroup()) { + $type = substr($type, 0, -5); + } + + $tabs = $this->getTabs()->add('objects', array( + 'url' => sprintf('director/%ss', strtolower($type)), + 'label' => $this->translate(ucfirst($type) . 's'), + )); + $tabs = $this->getTabs()->add('objecttemplates', array( + 'url' => sprintf('director/%stemplates', strtolower($type)), + 'label' => $this->translate('Templates'), + )); + if ($object->supportsGroups() || $object->isGroup()) { + $tabs->add('objectgroups', array( + 'url' => sprintf('director/%sgroups', $type), + 'label' => $this->translate('Groups') + )); + } + + $tabs->add('tree', array( + 'url' => sprintf('director/%stemplates/tree', $type), + 'label' => $this->translate('Tree'), + )); } public function indexAction() { $type = $this->getType(); $ltype = strtolower($type); - + $dummy = $this->dummyObject(); if (! in_array($type, $this->globalTypes)) { - if ($this->dummyObject()->isGroup()) { + if ($dummy->isGroup()) { $this->getTabs()->activate('objectgroups'); + $table = 'icinga' . ucfirst($type); + } elseif ($dummy->isTemplate()) { + $this->getTabs()->activate('objecttemplates'); + $table = 'icinga' . ucfirst($type); + $this->loadTable($table); + $table = 'icinga' . ucfirst($type) . 'Template'; } else { $this->getTabs()->activate('objects'); + $table = 'icinga' . ucfirst($type); } + } else { + $table = 'icinga' . ucfirst($type); + } + + if ($dummy->isTemplate()) { + $addParams = array('type' => 'template'); + $addTitle = $this->translate('Add %s template'); + } else { + $addParams = array(); + $addTitle = $this->translate('Add %s'); } $this->view->addLink = $this->view->qlink( - $this->translate('Add ' . ucfirst($ltype)), - 'director/' . $ltype . '/add' + sprintf($addTitle, $this->translate(ucfirst($ltype))), + 'director/' . $ltype .'/add', + $addParams ); $this->view->title = $this->translate('Icinga ' . ucfirst($ltype)); - - $table = $this->loadTable('icinga' . ucfirst($type))->setConnection($this->db()); + $table = $this->loadTable($table)->setConnection($this->db()); $this->setupFilterControl($table->getFilterEditor($this->getRequest())); $this->view->table = $this->applyPaginationLimits($table); $this->render('objects/table', null, true); } + public function treeAction() + { + $this->getTabs()->activate('tree'); + $this->view->tree = $this->db()->fetchTemplateTree(strtolower($this->getType())); + } + protected function dummyObject() { if ($this->dummy === null) { $class = $this->getObjectClassname(); $this->dummy = $class::create(array()); + if ($this->dummy->hasProperty('object_type')) { + if (false === strpos($this->getRequest()->getControllerName(), 'template')) { + $this->dummy->object_type = 'object'; + } else { + $this->dummy->object_type = 'template'; + } + } } return $this->dummy; @@ -105,7 +134,11 @@ abstract class ObjectsController extends ActionController return preg_replace( array('/group$/', '/period$/', '/argument$/'), array('Group', 'Period', 'Argument'), - substr($this->getRequest()->getControllerName(), 0, -1) + str_replace( + 'template', + '', + substr($this->getRequest()->getControllerName(), 0, -1) + ) ); } diff --git a/library/Director/Web/Form/DirectorObjectForm.php b/library/Director/Web/Form/DirectorObjectForm.php index d3765d8c..1ef09a29 100644 --- a/library/Director/Web/Form/DirectorObjectForm.php +++ b/library/Director/Web/Form/DirectorObjectForm.php @@ -3,6 +3,8 @@ namespace Icinga\Module\Director\Web\Form; use Icinga\Module\Director\Objects\IcingaObject; +use Icinga\Module\Director\Objects\DirectorDatafield; +use Zend_Form_Element_Select as Zf_Select; abstract class DirectorObjectForm extends QuickForm { @@ -10,10 +12,12 @@ abstract class DirectorObjectForm extends QuickForm protected $object; - private $objectName; + protected $objectName; private $className; + private $objectType = 'object'; + protected function object($values = array()) { if ($this->object === null) { @@ -37,49 +41,124 @@ abstract class DirectorObjectForm extends QuickForm return; } - if ($object->supportsCustomVars()) { - $this->addElement('note', '_newvar_hint', array('label' => 'New custom variable')); - $this->addElement('text', '_newvar_name', array( - 'label' => 'Name' - )); - $this->addElement('text', '_newvar_value', array( - 'label' => 'Value' - )); - $this->addElement('select', '_newvar_format', array( - 'label' => 'Type', - 'multiOptions' => array('string' => $this->translate('String')) - )); + } + + protected function isTemplate() + { + return $this->objectType === 'template'; + } + + protected function handleImports($object, & $values) + { + if (! $object->supportsImports()) { + return; } - if (false && $object->supportsRanges()) { - /* TODO implement when new logic is there - $this->addElement('note', '_newrange_hint', array('label' => 'New range')); - $this->addElement('text', '_newrange_name', array( - 'label' => 'Name' - )); - $this->addElement('text', '_newrange_value', array( - 'label' => 'Value' - )); - */ + if (array_key_exists('imports', $values)) { + $value = $values['imports']; + unset($values['imports']); + $object->clearImportedObjects(); + $object->imports()->set($value); + } + + $el = $this->getElement('imports'); + if ($el) { + $el->setMultiOptions($this->enumAllowedTemplates()); + $el->setValue($object->imports()->listImportNames()); } } - protected function handleIcingaObject(& $values) + protected function handleRanges($object, & $values) { - $object = $this->object(); - $handled = array(); - - if ($object->supportsGroups()) { - if (array_key_exists('groups', $values)) { - $object->groups()->set( - preg_split('/\s*,\s*/', $values['groups'], -1, PREG_SPLIT_NO_EMPTY) - ); - $handled['groups'] = true; - } + if (! $object->supportsRanges()) { + return; } - if ($this->object->supportsCustomVars()) { + $key = 'ranges'; + $object = $this->object(); + + /* Sample: + + array( + 'monday' => 'eins', + 'tuesday' => '00:00-24:00', + 'sunday' => 'zwei', + ); + + */ + if (array_key_exists($key, $values)) { + $object->ranges()->set($values[$key]); + unset($values[$key]); + } + + foreach ($object->ranges()->getRanges() as $key => $value) { + $this->addRange($key, $value); + } + + /* + // TODO implement when new logic is there + $this->addElement('note', '_newrange_hint', array('label' => 'New range')); + $this->addElement('text', '_newrange_name', array( + 'label' => 'Name' + )); + $this->addElement('text', '_newrange_value', array( + 'label' => 'Value' + )); + */ + } + + protected function handleGroups($object, & $values) + { + if (! $object->supportsGroups()) { + return; + } + + if (array_key_exists('groups', $values)) { + $value = $values['groups']; + unset($values['groups']); + + // TODO: Drop this once we have arrays everwhere + if (is_string($value)) { + $value = preg_split('/\s*,\s*/', $value, -1, PREG_SPLIT_NO_EMPTY); + } + + $object->groups()->set($value); + } + } + + protected function handleProperties($object, & $values) + { + if ($this->hasBeenSent()) { + $object->setProperties($values); + } + + $props = $object->getProperties(); + if (! $object instanceof IcingaObject) { + $this->setDefaults($props); + return $this; + } + + $inherited = $object->getInheritedProperties(); + $origins = $object->getOriginsProperties(); + + foreach ($props as $k => $v) { + if (property_exists($inherited, $k)) { + $this->setElementValue($k, $v, $inherited->$k, $origins->$k); + } else { + $this->setElementValue($k, $v); + } + } + } + + protected function handleCustomVars($object, & $values) + { + if (! $object->supportsCustomVars()) { + return; + } + + if ($this->hasBeenSent()) { $vars = array(); + $handled = array(); $newvar = array( 'type' => 'string', 'name' => null, @@ -99,62 +178,160 @@ abstract class DirectorObjectForm extends QuickForm } foreach ($vars as $k => $v) { - $this->object->vars()->$k = $v; + if ($v === '' || $v === null) { + unset($object->vars()->$k); + } else { + $object->vars()->$k = $v; + } } if ($newvar['name'] && $newvar['value']) { - $this->object->vars()->{$newvar['name']} = $newvar['value']; + $object->vars()->{$newvar['name']} = $newvar['value']; + } + + foreach ($handled as $key) { + unset($values[$key]); } } - if ($object->supportsImports()) { - if (array_key_exists('imports', $values)) { - $object->imports()->set( - preg_split('/\s*,\s*/', $values['imports'], -1, PREG_SPLIT_NO_EMPTY) - ); - $handled['imports'] = true; + $vars = $object->getVars(); + + $fields = $object->getResolvedFields(); + $inherits = $object->getInheritedVars(); + $origins = $object->getOriginsVars(); + + foreach ($fields as $field) { + $varname = $field->varname; + + // Get value from the related varname if set: + if (property_exists($vars, $varname)) { + $value = $vars->$varname; + } else { + $value = null; } + + if (property_exists($inherits, $varname)) { + $inheritedValue = $inherits->$varname; + $inheritFrom = $origins->$varname; + if ($inheritFrom === $object->object_name) { + $inherited = false; + } else { + $inherited = true; + } + } else { + $inheritedValue = null; + $inheritFrom = false; + $inherited = false; + } + + $this->addField($field, $value, $inheritedValue, $inheritFrom); } - if ($object->supportsRanges()) { - $object->ranges()->set(array( - 'monday' => 'eins', - 'tuesday' => '00:00-24:00', - 'sunday' => 'zwei', + // Additional vars + foreach ($vars as $key => $value) { + // Did we already create a field for this var? Then skip it: + if (array_key_exists($key, $fields)) { + continue; + } + + // Show inheritance information in case we inherited this var: + if (isset($inherited->$key)) { + $this->addCustomVar($key, $value, $inherited->$key, $origins->$key); + } else { + $this->addCustomVar($key, $value); + } + + } + + if ($object->isTemplate()) { + $this->addHtml('

Add a custom variable

'); + $this->addElement('text', '_newvar_name', array( + 'label' => 'Name' + )); + $this->addElement('text', '_newvar_value', array( + 'label' => 'Value' + )); + $this->addElement('select', '_newvar_format', array( + 'label' => 'Type', + 'multiOptions' => array('string' => $this->translate('String')) )); } + } - foreach ($handled as $key => $value) { - unset($values[$key]); + public function setObjectType($type) + { + $this->objectType = $type; + return $this; + } + + protected function addField($field, $value = null, $inherited = null, $inheritedFrom = null) + { + $datafield = DirectorDatafield::load($field->datafield_id, $this->getDb()); + $datatype = new $datafield->datatype; + $datatype->setSettings($datafield->getSettings()); + + $name = 'var_' . $datafield->varname; + $el = $datatype->getFormElement($name, $this); + + $el->setLabel($datafield->caption); + $el->setDescription($datafield->description); + + if ($field->is_required === 'y' && ! $this->isTemplate()) { + $el->setRequired(true); + } + + $this->addElement($el); + $this->setElementValue($name, $value, $inherited, $inheritedFrom); + + return $el; + } + + protected function setElementValue($name, $value = null, $inherited = null, $inheritedFrom = null) + { + $el = $this->getElement($name); + if (! $el) { + return; + } + + if ($value !== null) { + $el->setValue($value); + } + + if ($inherited === null || empty($inherited)) { + return; + } + + if ($el instanceof Zf_Select) { + $multi = $el->getMultiOptions(); + if (array_key_exists($inherited, $multi)) { + $multi[null] = $multi[$inherited] . sprintf(' (%s)', $inheritedFrom); + } else { + $multi[null] = $this->translate('- inherited -'); + } + $el->setMultiOptions($multi); + } else { + $el->setAttrib('placeholder', $inherited . sprintf(' (%s)', $inheritedFrom)); } } public function onSuccess() { - $object = $this->object; - $values = $this->getValues(); - if ($object instanceof IcingaObject) { - $this->handleIcingaObject($values); + $object = $this->object(); + if ($object->hasBeenModified()) { + + $msg = sprintf( + $object->hasBeenLoadedFromDb() + ? $this->translate('The %s has successfully been stored') + : $this->translate('A new %s has successfully been created'), + $this->translate($this->getObjectName()) + ); + $object->store($this->db); + } else { + $msg = $this->translate('No action taken, object has not been modified'); } - $object->setProperties($values); - $msg = sprintf( - $object->hasBeenLoadedFromDb() - ? 'The Icinga %s has successfully been stored' - : 'A new Icinga %s has successfully been created', - $this->translate($this->getObjectName()) - ); - - $object->store($this->db); $this->redirectOnSuccess($msg); } - protected function optionalEnum($enum) - { - return array( - null => $this->translate('- please choose -') - ) + $enum; - } - protected function optionalBoolean($key, $label, $description) { return $this->addElement('select', $key, array( @@ -202,65 +379,55 @@ abstract class DirectorObjectForm extends QuickForm return $this->objectName; } + protected function onRequest() + { + $object = $this->object(); + $values = array(); + + if ($this->hasBeenSent()) { + $post = $this->getRequest()->getPost(); + foreach ($post as $key => $value) { + $el = $this->getElement($key); + if ($el && ! $el->getIgnore()) { + $values[$key] = $value; + } + } + } + + if ($object instanceof IcingaObject) { + if (! $object->hasBeenLoadedFromDb()) { + $object->object_type = $this->objectType; + } + $this->handleImports($object, $values); + $this->handleCustomVars($object, $values); + $this->handleGroups($object, $values); + $this->handleRanges($object, $values); + } + + $this->handleProperties($object, $values); + + $this->moveSubmitToBottom(); + } + public function loadObject($id) { - $this->prepareElements(); $class = $this->getObjectClassname(); $this->object = $class::load($id, $this->db); + + // TODO: hmmmm... if (! is_array($id)) { $this->addHidden('id'); } - $this->setDefaults($this->object->getProperties()); - if (! $this->object instanceof IcingaObject) { - return $this; - } - if ($submit = $this->getElement('submit')) { - $this->removeElement('submit'); - } - - if ($this->object->supportsGroups()) { - $this->getElement('groups')->setValue( - implode(', ', $this->object->groups()->listGroupNames()) - ); - } - - if ($this->object->supportsImports()) { - $this->getElement('imports')->setValue( - implode(', ', $this->object->imports()->listImportNames()) - ); - } - - if ($this->object->supportsCustomVars()) { - foreach ($this->object->vars() as $key => $value) { - $this->addCustomVar($key, $value); - } - } - - if ($this->object->supportsRanges()) { - /* TODO implement when new logic for customvars is there - foreach ($this->object->ranges()->getRanges() as $key => $value) { - $this->addRange($key, $value); - } - */ - } - - if ($submit) { - $this->addElement($submit); - } - - if (! $this->hasBeenSubmitted()) { - $this->beforeValidation($this->object->getProperties()); - } return $this; } - protected function addCustomVar($key, $range) + protected function addCustomVar($key, $value, $inherited = null, $inheritedFrom = null) { - $this->addElement('text', 'var_' . $key, array( - 'label' => 'vars.' . $key, - 'value' => $range->getValue() - )); + $label = 'vars.' . $key; + $key = 'var_' . $key; + $this->addElement('text', $key, array('label' => $label)); + $this->setElementValue($key, $value, $inherited, $inheritedFrom); } protected function addRange($key, $range) @@ -282,55 +449,102 @@ abstract class DirectorObjectForm extends QuickForm if ($this->object !== null) { $this->object->setConnection($db); } - if ($this->hasElement('parent_zone_id')) { - $this->getElement('parent_zone_id') - ->setMultiOptions($this->optionalEnum($db->enumZones())); - } - if ($this->hasElement('host_id')) { - $this->getElement('host_id') - ->setMultiOptions($this->optionalEnum($db->enumHosts())); - } - if ($this->hasElement('hostgroup_id')) { - $this->getElement('hostgroup_id') - ->setMultiOptions($this->optionalEnum($db->enumHostgroups())); - } - if ($this->hasElement('service_id')) { - $this->getElement('service_id') - ->setMultiOptions($this->optionalEnum($db->enumServices())); - } - if ($this->hasElement('servicegroup_id')) { - $this->getElement('servicegroup_id') - ->setMultiOptions($this->optionalEnum($db->enumServicegroups())); - } - if ($this->hasElement('user_id')) { - $this->getElement('user_id') - ->setMultiOptions($this->optionalEnum($db->enumUsers())); - } - if ($this->hasElement('usergroup_id')) { - $this->getElement('usergroup_id') - ->setMultiOptions($this->optionalEnum($db->enumUsergroups())); - } - if ($this->hasElement('zone_id')) { - $this->getElement('zone_id') - ->setMultiOptions($this->optionalEnum($db->enumZones())); - } - if ($this->hasElement('check_command_id')) { - $this->getElement('check_command_id') - ->setMultiOptions($this->optionalEnum($db->enumCheckCommands())); - } - if ($this->hasElement('command_id')) { - $this->getElement('command_id') - ->setMultiOptions($this->optionalEnum($db->enumCommands())); - } + return $this; } + protected function addZoneElement() + { + $this->addElement('select', 'zone_id', array( + 'label' => $this->translate('Cluster Zone'), + 'description' => $this->translate('Icinga cluster zone'), + 'multiOptions' => $this->optionalEnum($this->db->enumZones()) + )); + + return $this; + } + + protected function addImportsElement() + { + $this->addElement('multiselect', 'imports', array( + 'label' => $this->translate('Imports'), + 'description' => $this->translate('Importable templates'), + 'multiOptions' => $this->enumAllowedTemplates(), + 'class' => 'autosubmit' + )); + + return $this; + } + + protected function addCheckExecutionElements() + { + $this->addHtml('

Check execution

'); + + $this->addElement('select', 'check_command_id', array( + 'label' => $this->translate('Check command'), + 'description' => $this->translate('Check command definition'), + 'multiOptions' => $this->optionalEnum($this->db->enumCheckCommands()) + )); + + $this->optionalBoolean( + 'enable_active_checks', + $this->translate('Execute active checks'), + $this->translate('Whether to actively check this object') + ); + + $this->optionalBoolean( + 'enable_passive_checks', + $this->translate('Accept passive checks'), + $this->translate('Whether to accept passive check results for this object') + ); + + $this->optionalBoolean( + 'enable_notifications', + $this->translate('Send notifications'), + $this->translate('Whether to send notifications for this object') + ); + + $this->optionalBoolean( + 'enable_event_handler', + $this->translate('Enable event handler'), + $this->translate('Whether to enable event handlers this object') + ); + + $this->optionalBoolean( + 'enable_perfdata', + $this->translate('Process performance data'), + $this->translate('Whether to process performance data provided by this object') + ); + + $this->optionalBoolean( + 'volatile', + $this->translate('Volatile'), + $this->translate('Whether this check is volatile.') + ); + + return $this; + } + + protected function enumAllowedTemplates() + { + $object = $this->object(); + $tpl = $this->db->enumIcingaTemplates($object->getShortTableName()); + $tpl = array_combine($tpl, $tpl); + $id = $object->object_name; + + if (array_key_exists($id, $tpl)) { + unset($tpl[$id]); + } + return $tpl; + } + private function dummyForTranslation() { $this->translate('Host'); $this->translate('Service'); $this->translate('Zone'); $this->translate('Command'); + $this->translate('User'); // ... TBC } } diff --git a/library/Director/Web/Form/QuickForm.php b/library/Director/Web/Form/QuickForm.php index 93c32a79..fd255233 100644 --- a/library/Director/Web/Form/QuickForm.php +++ b/library/Director/Web/Form/QuickForm.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Director\Web\Form; use Icinga\Application\Icinga; use Icinga\Application\Modules\Module; +use Icinga\Exception\ProgrammingError; use Icinga\Web\Notification; use Icinga\Web\Request; use Icinga\Web\Url; @@ -60,24 +61,39 @@ abstract class QuickForm extends Zend_Form */ protected $icingaModule; + protected $icingaModuleName; + + protected $hintCount = 0; + public function __construct($options = null) { - if ($options !== null && array_key_exists('icingaModule', $options)) { - $this->icingaModule = $options['icingaModule']; - unset($options['icingaModule']); - } - parent::__construct($options); + parent::__construct($this->handleOptions($options)); $this->setMethod('post'); $this->setAction(Url::fromRequest()); $this->createIdElement(); $this->regenerateCsrfToken(); } + protected function handleOptions($options = null) + { + if ($options === null) { + return $options; + } + + if (array_key_exists('icingaModule', $options)) { + $this->icingaModule = $options['icingaModule']; + $this->icingaModuleName = $this->icingaModule->getName(); + unset($options['icingaModule']); + } + + return $options; + } + protected function addSubmitButtonIfSet() { if (false !== ($label = $this->getSubmitLabel())) { $this->addElement('submit', $label); - $this->getElement($label)->setLabel($label); + $this->getElement($label)->setLabel($label)->removeDecorator('Label'); } } @@ -140,6 +156,7 @@ abstract class QuickForm extends Zend_Form $element = $this->getElement(self::CSRF); } $element->setValue(CsrfToken::generate())->setIgnore(true); + return $this; } @@ -159,6 +176,30 @@ abstract class QuickForm extends Zend_Form return $this; } + public function addHtmlHint($html, $options = array()) + { + return $this->addHtml('
' . $html . '
', $options); + } + + public function addHtml($html, $options = array()) + { + $name = '_HINT' . ++$this->hintCount; + $this->addElement('note', $name, $options); + $this->getElement($name) + ->setValue($html) + ->setIgnore(true) + ->removeDecorator('Label'); + + return $this; + } + + public function optionalEnum($enum) + { + return array( + null => $this->translate('- please choose -') + ) + $enum; + } + public function setSuccessUrl($url) { $this->successUrl = $url; @@ -217,8 +258,8 @@ abstract class QuickForm extends Zend_Form { if (! $this->didSetup) { $this->setup(); - $this->onSetup(); $this->addSubmitButtonIfSet(); + $this->onSetup(); $this->didSetup = true; } @@ -228,11 +269,9 @@ abstract class QuickForm extends Zend_Form public function handleRequest(Request $request = null) { if ($request !== null) { - $this->request = $request; + $this->setRequest($request); } - $this->prepareElements(); - if ($this->hasBeenSent()) { $post = $this->getRequest()->getPost(); if ($this->hasBeenSubmitted()) { @@ -254,8 +293,11 @@ abstract class QuickForm extends Zend_Form public function translate($string) { - // TODO: A module should use it's own domain - return t($string); + if ($this->icingaModuleName === null) { + return t($string); + } else { + return mt($this->icingaModuleName, $string); + } } public function onSuccess() @@ -311,10 +353,26 @@ abstract class QuickForm extends Zend_Form Icinga::app()->getFrontController()->getResponse()->redirectAndExit($url); } + protected function onRequest() + { + } + + public function setRequest(Request $request) + { + if ($this->request !== null) { + throw new ProgrammingError('Unable to set request twice'); + } + + $this->request = $request; + $this->prepareElements(); + $this->onRequest(); + return $this; + } + public function getRequest() { if ($this->request === null) { - $this->request = Icinga::app()->getFrontController()->getRequest(); + $this->setRequest(Icinga::app()->getFrontController()->getRequest()); } return $this->request; } diff --git a/public/css/module.less b/public/css/module.less index e89f29d5..e663a858 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -1,6 +1,90 @@ +form p.description { + display: none; +} + +input, select, select option, textarea { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +select::-ms-expand, input::-ms-expand, textarea::-ms-expand { /* for IE 11 */ + display: none; +} + +select { + border: 1px solid #f9f9f9; + cursor: pointer; +} + +input, textarea { + border: 1px solid #f9f9f9; + padding-left: 0.5em; +} + +select:hover, input:hover, textarea:hover, select:focus, input:focus, textarea:focus { + border: 1px solid #666; +} + +select[value=""] { + color: blue; + border: 1px solid #666; +} + +select option { + color: inherit; + padding-left: 0.5em; +} + +select option[value=""] { + color: #aaa; +} + +ul.main-actions { + margin: 0; + li { + list-style-type: none; + width: 20em; + height: 20em; + text-align: center; + display: block; + padding: 0; + float: left; + + a { + i { + width: 100%; + height: 1.3em; + font-size: 8em; + display: inline-block; + line-height: 1em; + } + + border: 1px solid #666; + padding: 1em; + margin: 0.5em; + font-size: 1.1em; + color: #666; + font-weight: bold; + display: block; + + &:hover { + background-color: #666; + color: white; + text-decoration: none; + } + } + } +} + /* BEGIN Forms */ form dt label { width: auto; + font-weight: normal; + + &.required { + font-weight: bold; + } } form dd { @@ -10,15 +94,44 @@ form dd { form dt { display: inline-block; - min-width: 16em; + min-width: 12em; + width: 30%; } form dd:after { display: block; content: ''; } -textarea { - height: 10em; + +form textarea { + height: auto; +} + +form dd ul.errors { + list-style-type: none; + padding-left: 0.3em; + font-size: 0.857em; + + li { + color: @colorCritical; + padding: 0.3em; + } +} + +form div.hint { + font-size: 0.857em; + padding: 1em; + background-color: #f2f4fd; + border: 1px solid lightgrey; + margin: 1em 0; + + pre { + font-style: normal; + background-color: white; + font-size: 1.25em; + margin: 0; + padding: 1em; + } } /* END of Forms */ @@ -145,10 +258,30 @@ table.simple { tr { padding: 0; margin: 0; + + td:first-child { + border-left: 0.5em solid transparent; + } + + td:last-child { + border-right: 0.5em solid transparent; + } + background-color: #fafcfe; } tr:nth-child(even) { - background-color: #e6e6e6; + background-color: #f6f8fa; + } + + tr:hover { + background-color: #888; + td { + color: white; + } + } + + tr.active td { + border-color: black; } th { @@ -158,7 +291,7 @@ table.simple { } td { - padding: 0.1em; + padding: 0.1em 1em; margin: 0; white-space: nowrap; } diff --git a/schema/pgsql-changes/upgrade_34.sql b/schema/pgsql-changes/upgrade_34.sql new file mode 100644 index 00000000..c8e57abe --- /dev/null +++ b/schema/pgsql-changes/upgrade_34.sql @@ -0,0 +1,189 @@ +ALTER TABLE director_generated_file ALTER COLUMN content SET DEFAULT NULL; +ALTER TABLE icinga_host_field ALTER COLUMN is_required SET NOT NULL; +ALTER TABLE icinga_service_field ALTER COLUMN is_required SET NOT NULL; + +CREATE TABLE import_source ( + id serial, + source_name character varying(64) NOT NULL, + key_column character varying(64) NOT NULL, + provider_class character varying(72) NOT NULL, + PRIMARY KEY (id) +); + +CREATE INDEX import_source_search_idx ON import_source (key_column); + + +CREATE TABLE import_source_setting ( + source_id integer NOT NULL, + setting_name character varying(64) NOT NULL, + setting_value text NOT NULL, + PRIMARY KEY (source_id, setting_name), + CONSTRAINT import_source_settings_source + FOREIGN KEY (source_id) + REFERENCES import_source (id) + ON DELETE CASCADE + ON UPDATE CASCADE +); + +CREATE INDEX import_source_setting_source ON import_source_setting (source_id); + + +CREATE TABLE imported_rowset ( + checksum bytea CHECK(LENGTH(checksum) = 20), + PRIMARY KEY (checksum) +); + + +CREATE TABLE import_run ( + id serial, + source_id integer NOT NULL, + rowset_checksum bytea CHECK(LENGTH(rowset_checksum) = 20), + start_time timestamp with time zone NOT NULL, + end_time timestamp with time zone NOT NULL, + succeeded enum_boolean DEFAULT NULL, + PRIMARY KEY (id), + CONSTRAINT import_run_source + FOREIGN KEY (source_id) + REFERENCES import_source (id) + ON DELETE RESTRICT + ON UPDATE CASCADE, + CONSTRAINT import_run_rowset + FOREIGN KEY (rowset_checksum) + REFERENCES imported_rowset (checksum) + ON DELETE RESTRICT + ON UPDATE CASCADE +); + +CREATE INDEX import_run_import_source ON import_run (source_id); +CREATE INDEX import_run_rowset ON import_run (rowset_checksum); + + +CREATE TABLE imported_row ( + checksum bytea CHECK(LENGTH(checksum) = 20), + object_name character varying(255) NOT NULL, + PRIMARY KEY (checksum) +); + +COMMENT ON COLUMN imported_row.checksum IS 'sha1(object_name;property_checksum;...)'; + + +CREATE TABLE imported_rowset_row ( + rowset_checksum bytea CHECK(LENGTH(checksum) = 20), + row_checksum bytea CHECK(LENGTH(checksum) = 20), + PRIMARY KEY (rowset_checksum, row_checksum), + CONSTRAINT imported_rowset_row_rowset + FOREIGN KEY (rowset_checksum) + REFERENCES imported_rowset (checksum) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT imported_rowset_row_row + FOREIGN KEY (row_checksum) + REFERENCES imported_row (checksum) + ON DELETE RESTRICT + ON UPDATE CASCADE +); + +CREATE INDEX imported_rowset_row_rowset_checksum ON imported_rowset_row (rowset_checksum); +CREATE INDEX imported_rowset_row_row_checksum ON imported_rowset_row (row_checksum); + +CREATE TABLE imported_property ( + checksum bytea CHECK(LENGTH(checksum) = 20), + property_name character varying(64) NOT NULL, + property_value text NOT NULL, + format enum_property_format, + PRIMARY KEY (checksum) +); + +CREATE INDEX imported_property_search_idx ON imported_property (property_name); + +CREATE TABLE imported_row_property ( + row_checksum bytea CHECK(LENGTH(row_checksum) = 20), + property_checksum bytea CHECK(LENGTH(property_checksum) = 20), + PRIMARY KEY (row_checksum, property_checksum), + CONSTRAINT imported_row_property_row + FOREIGN KEY (row_checksum) + REFERENCES imported_row (checksum) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT imported_row_property_property + FOREIGN KEY (property_checksum) + REFERENCES imported_property (checksum) + ON DELETE RESTRICT + ON UPDATE CASCADE +); + +CREATE INDEX imported_row_property_row_checksum ON imported_row_property (row_checksum); +CREATE INDEX imported_row_property_property_checksum ON imported_row_property (property_checksum); + + +CREATE TYPE enum_sync_rule_object_type AS ENUM('host', 'user'); +CREATE TYPE enum_sync_rule_update_policy AS ENUM('merge', 'override', 'ignore'); + +CREATE TABLE sync_rule ( + id serial, + rule_name character varying(255) NOT NULL, + object_type enum_sync_rule_object_type NOT NULL, + update_policy enum_sync_rule_update_policy NOT NULL, + purge_existing enum_boolean NOT NULL DEFAULT 'n', + filter_expression text DEFAULT NULL, + PRIMARY KEY (id) +); + + +CREATE TYPE enum_sync_property_merge_policy AS ENUM('override', 'merge'); + +CREATE TABLE sync_property ( + id serial, + rule_id integer NOT NULL, + source_id integer NOT NULL, + source_expression character varying(255) NOT NULL, + destination_field character varying(64), + priority smallint NOT NULL, + filter_expression text DEFAULT NULL, + merge_policy enum_sync_property_merge_policy NOT NULL, + PRIMARY KEY (id), + CONSTRAINT sync_property_rule + FOREIGN KEY (rule_id) + REFERENCES sync_rule (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT sync_property_source + FOREIGN KEY (source_id) + REFERENCES import_source (id) + ON DELETE RESTRICT + ON UPDATE CASCADE +); + +CREATE INDEX sync_property_rule ON sync_property (rule_id); +CREATE INDEX sync_property_source ON sync_property (source_id); + + +CREATE TABLE import_row_modifier ( + id serial, + property_id integer NOT NULL, + provider_class character varying(72) NOT NULL, + PRIMARY KEY (id) +); + + +CREATE TABLE import_row_modifier_setting ( + modifier_id integer NOT NULL, + setting_name character varying(64) NOT NULL, + setting_value text DEFAULT NULL, + PRIMARY KEY (modifier_id) +); + + +CREATE TABLE director_datafield_setting ( + datafield_id integer NOT NULL, + setting_name character varying(64) NOT NULL, + setting_value text NOT NULL, + PRIMARY KEY (datafield_id, setting_name), + CONSTRAINT datafield_id_settings + FOREIGN KEY (datafield_id) + REFERENCES director_datafield (id) + ON DELETE CASCADE + ON UPDATE CASCADE +); + +CREATE INDEX director_datafield_datafield ON director_datafield_setting (datafield_id); diff --git a/schema/pgsql.sql b/schema/pgsql.sql index 8dd92bf8..b60f5787 100644 --- a/schema/pgsql.sql +++ b/schema/pgsql.sql @@ -9,11 +9,6 @@ -- INSERT INTO director_activity_log (object_type, object_name, action_name, author, change_time, checksum) VALUES('object', 'foo', 'create', 'alex', CURRENT_TIMESTAMP, decode('cf23df2207d99a74fbe169e3eba035e633b65d94', 'hex')); -- --- --- Enumerable Types --- --- TODO: what about translation of the strings? - CREATE TYPE enum_activity_action AS ENUM('create', 'delete', 'modify'); CREATE TYPE enum_boolean AS ENUM('y', 'n'); CREATE TYPE enum_property_format AS ENUM('string', 'expression', 'json'); @@ -24,6 +19,9 @@ CREATE TYPE enum_command_object_type AS ENUM('object', 'template', 'external_obj CREATE TYPE enum_apply_object_type AS ENUM('object', 'template', 'apply'); CREATE TYPE enum_state_name AS ENUM('OK', 'Warning', 'Critical', 'Unknown', 'Up', 'Down'); CREATE TYPE enum_type_name AS ENUM('DowntimeStart', 'DowntimeEnd', 'DowntimeRemoved', 'Custom', 'Acknowledgement', 'Problem', 'Recovery', 'FlappingStart', 'FlappingEnd'); +CREATE TYPE enum_sync_rule_object_type AS ENUM('host', 'user'); +CREATE TYPE enum_sync_rule_update_policy AS ENUM('merge', 'override', 'ignore'); +CREATE TYPE enum_sync_property_merge_policy AS ENUM('override', 'merge'); CREATE TABLE director_dbversion ( @@ -73,7 +71,7 @@ COMMENT ON COLUMN director_generated_config.duration IS 'Config generation durat CREATE TABLE director_generated_file ( checksum bytea CHECK(LENGTH(checksum) = 20), - content text NOT NULL, + content text DEFAULT NULL, PRIMARY KEY (checksum) ); @@ -486,7 +484,7 @@ CREATE INDEX host_inheritance_host_parent ON icinga_host_inheritance (parent_hos CREATE TABLE icinga_host_field ( host_id integer NOT NULL, datafield_id integer NOT NULL, - is_required enum_boolean DEFAULT NULL, + is_required enum_boolean NOT NULL, PRIMARY KEY (host_id, datafield_id), CONSTRAINT icinga_host_field_host FOREIGN KEY (host_id) @@ -610,7 +608,7 @@ CREATE INDEX service_inheritance_service_parent ON icinga_service_inheritance (p CREATE TABLE icinga_service_field ( service_id integer NOT NULL, datafield_id integer NOT NULL, - is_required enum_boolean DEFAULT NULL, + is_required enum_boolean NOT NULL, PRIMARY KEY (service_id, datafield_id), CONSTRAINT icinga_service_field_service FOREIGN KEY (service_id) @@ -963,6 +961,183 @@ CREATE TABLE icinga_usergroup_parent ( CREATE INDEX usergroup_parent_usergroup ON icinga_usergroup_parent (usergroup_id); CREATE INDEX usergroup_parent_parent ON icinga_usergroup_parent (parent_usergroup_id); --- --- TODO: unfinished: see mysql.sql schema from sync_* --- + +CREATE TABLE import_source ( + id serial, + source_name character varying(64) NOT NULL, + key_column character varying(64) NOT NULL, + provider_class character varying(72) NOT NULL, + PRIMARY KEY (id) +); + +CREATE INDEX import_source_search_idx ON import_source (key_column); + + +CREATE TABLE import_source_setting ( + source_id integer NOT NULL, + setting_name character varying(64) NOT NULL, + setting_value text NOT NULL, + PRIMARY KEY (source_id, setting_name), + CONSTRAINT import_source_settings_source + FOREIGN KEY (source_id) + REFERENCES import_source (id) + ON DELETE CASCADE + ON UPDATE CASCADE +); + +CREATE INDEX import_source_setting_source ON import_source_setting (source_id); + + +CREATE TABLE imported_rowset ( + checksum bytea CHECK(LENGTH(checksum) = 20), + PRIMARY KEY (checksum) +); + + +CREATE TABLE import_run ( + id serial, + source_id integer NOT NULL, + rowset_checksum bytea CHECK(LENGTH(rowset_checksum) = 20), + start_time timestamp with time zone NOT NULL, + end_time timestamp with time zone NOT NULL, + succeeded enum_boolean DEFAULT NULL, + PRIMARY KEY (id), + CONSTRAINT import_run_source + FOREIGN KEY (source_id) + REFERENCES import_source (id) + ON DELETE RESTRICT + ON UPDATE CASCADE, + CONSTRAINT import_run_rowset + FOREIGN KEY (rowset_checksum) + REFERENCES imported_rowset (checksum) + ON DELETE RESTRICT + ON UPDATE CASCADE +); + +CREATE INDEX import_run_import_source ON import_run (source_id); +CREATE INDEX import_run_rowset ON import_run (rowset_checksum); + + +CREATE TABLE imported_row ( + checksum bytea CHECK(LENGTH(checksum) = 20), + object_name character varying(255) NOT NULL, + PRIMARY KEY (checksum) +); + +COMMENT ON COLUMN imported_row.checksum IS 'sha1(object_name;property_checksum;...)'; + + +CREATE TABLE imported_rowset_row ( + rowset_checksum bytea CHECK(LENGTH(checksum) = 20), + row_checksum bytea CHECK(LENGTH(checksum) = 20), + PRIMARY KEY (rowset_checksum, row_checksum), + CONSTRAINT imported_rowset_row_rowset + FOREIGN KEY (rowset_checksum) + REFERENCES imported_rowset (checksum) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT imported_rowset_row_row + FOREIGN KEY (row_checksum) + REFERENCES imported_row (checksum) + ON DELETE RESTRICT + ON UPDATE CASCADE +); + +CREATE INDEX imported_rowset_row_rowset_checksum ON imported_rowset_row (rowset_checksum); +CREATE INDEX imported_rowset_row_row_checksum ON imported_rowset_row (row_checksum); + +CREATE TABLE imported_property ( + checksum bytea CHECK(LENGTH(checksum) = 20), + property_name character varying(64) NOT NULL, + property_value text NOT NULL, + format enum_property_format, + PRIMARY KEY (checksum) +); + +CREATE INDEX imported_property_search_idx ON imported_property (property_name); + +CREATE TABLE imported_row_property ( + row_checksum bytea CHECK(LENGTH(row_checksum) = 20), + property_checksum bytea CHECK(LENGTH(property_checksum) = 20), + PRIMARY KEY (row_checksum, property_checksum), + CONSTRAINT imported_row_property_row + FOREIGN KEY (row_checksum) + REFERENCES imported_row (checksum) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT imported_row_property_property + FOREIGN KEY (property_checksum) + REFERENCES imported_property (checksum) + ON DELETE RESTRICT + ON UPDATE CASCADE +); + +CREATE INDEX imported_row_property_row_checksum ON imported_row_property (row_checksum); +CREATE INDEX imported_row_property_property_checksum ON imported_row_property (property_checksum); + +CREATE TABLE sync_rule ( + id serial, + rule_name character varying(255) NOT NULL, + object_type enum_sync_rule_object_type NOT NULL, + update_policy enum_sync_rule_update_policy NOT NULL, + purge_existing enum_boolean NOT NULL DEFAULT 'n', + filter_expression text DEFAULT NULL, + PRIMARY KEY (id) +); + + +CREATE TABLE sync_property ( + id serial, + rule_id integer NOT NULL, + source_id integer NOT NULL, + source_expression character varying(255) NOT NULL, + destination_field character varying(64), + priority smallint NOT NULL, + filter_expression text DEFAULT NULL, + merge_policy enum_sync_property_merge_policy NOT NULL, + PRIMARY KEY (id), + CONSTRAINT sync_property_rule + FOREIGN KEY (rule_id) + REFERENCES sync_rule (id) + ON DELETE CASCADE + ON UPDATE CASCADE, + CONSTRAINT sync_property_source + FOREIGN KEY (source_id) + REFERENCES import_source (id) + ON DELETE RESTRICT + ON UPDATE CASCADE +); + +CREATE INDEX sync_property_rule ON sync_property (rule_id); +CREATE INDEX sync_property_source ON sync_property (source_id); + + +CREATE TABLE import_row_modifier ( + id serial, + property_id integer NOT NULL, + provider_class character varying(72) NOT NULL, + PRIMARY KEY (id) +); + + +CREATE TABLE import_row_modifier_setting ( + modifier_id integer NOT NULL, + setting_name character varying(64) NOT NULL, + setting_value text DEFAULT NULL, + PRIMARY KEY (modifier_id) +); + + +CREATE TABLE director_datafield_setting ( + datafield_id integer NOT NULL, + setting_name character varying(64) NOT NULL, + setting_value text NOT NULL, + PRIMARY KEY (datafield_id, setting_name), + CONSTRAINT datafield_id_settings + FOREIGN KEY (datafield_id) + REFERENCES director_datafield (id) + ON DELETE CASCADE + ON UPDATE CASCADE +); + +CREATE INDEX director_datafield_datafield ON director_datafield_setting (datafield_id);