applyGenerated = $applyGenerated; return $this; } public function setInheritedFrom($hostname) { $this->inheritedFrom = $hostname; return $this; } /** * @throws IcingaException * @throws ProgrammingError * @throws \Zend_Form_Exception */ public function setup() { if (!$this->isNew() || $this->providesOverrides()) { $this->tryToFetchHost(); } if ($this->providesOverrides()) { return; } if ($this->host && $this->set) { // Probably never reached, as providesOverrides includes this $this->setupOnHostForSet(); return; } if ($this->set !== null) { $this->setupSetRelatedElements(); } elseif ($this->host === null) { $this->setupServiceElements(); } else { $this->setupHostRelatedElements(); } } protected function tryToFetchHost() { try { if ($this->host === null) { $this->host = $this->object->getResolvedRelated('host'); } } catch (NestingError $nestingError) { // ignore for the form to load } } public function providesOverrides() { return $this->applyGenerated || $this->inheritedFrom || ($this->host && $this->set) || ($this->object && $this->object->usesVarOverrides()); } /** * @throws IcingaException * @throws ProgrammingError * @throws \Zend_Form_Exception */ protected function addFields() { if ($this->providesOverrides() && $this->hasBeenBlacklisted()) { $this->onAddedFields(); return; } else { parent::addFields(); } } /** * @throws IcingaException * @throws ProgrammingError * @throws \Zend_Form_Exception */ protected function onAddedFields() { if (! $this->providesOverrides()) { return; } $hasDeleteButton = false; $isBranch = $this->branch && $this->branch->isBranch(); if ($this->hasBeenBlacklisted()) { $this->addHtml( Hint::warning($this->translate('This Service has been deactivated on this host')), ['name' => 'HINT_blacklisted'] ); $group = null; if (! $isBranch) { $this->addDeleteButton($this->translate('Reactivate')); $hasDeleteButton = true; } $this->setSubmitLabel(false); } else { $this->addOverrideHint(); $group = $this->getDisplayGroup('custom_fields'); if (! $group) { foreach ($this->getDisplayGroups() as $groupName => $eventualGroup) { if (preg_match('/^custom_fields:/', $groupName)) { $group = $eventualGroup; break; } } } if ($group) { $elements = $group->getElements(); $group->setElements([$this->getElement('inheritance_hint')]); $group->addElements($elements); $this->setSubmitLabel($this->translate('Override vars')); } else { $this->addElementsToGroup( ['inheritance_hint'], 'custom_fields', 20, $this->translate('Hints regarding this service') ); $this->setSubmitLabel(false); } if (! $isBranch) { $this->addDeleteButton($this->translate('Deactivate')); $hasDeleteButton = true; } } if (! $this->hasSubmitButton() && $hasDeleteButton) { $this->addDisplayGroup([$this->deleteButtonName], 'buttons', [ 'decorators' => [ 'FormElements', ['HtmlTag', ['tag' => 'dl']], 'DtDdWrapper', ], 'order' => self::GROUP_ORDER_BUTTONS, ]); } } /** * @return IcingaHost|null */ public function getHost() { return $this->host; } /** * @param IcingaService $service * @return IcingaService * @throws \Icinga\Exception\NotFoundError */ protected function getFirstParent(IcingaService $service) { /** @var IcingaService[] $objects */ $objects = $service->imports()->getObjects(); if (empty($objects)) { throw new RuntimeException('Something went wrong, got no parent'); } reset($objects); return current($objects); } /** * @return bool * @throws \Icinga\Exception\NotFoundError */ protected function hasBeenBlacklisted() { if (! $this->providesOverrides() || $this->object === null) { return false; } if ($this->blacklisted === null) { $host = $this->host; // Safety check, branches $hostId = $host->get('id'); $service = $this->getServiceToBeBlacklisted(); $serviceId = $service->get('id'); if (! $hostId || ! $serviceId) { return false; } $db = $this->db->getDbAdapter(); if ($this->providesOverrides()) { $this->blacklisted = 1 === (int)$db->fetchOne( $db->select()->from('icinga_host_service_blacklist', 'COUNT(*)') ->where('host_id = ?', $hostId) ->where('service_id = ?', $serviceId) ); } else { $this->blacklisted = false; } } return $this->blacklisted; } /** * @param $object * @throws IcingaException * @throws ProgrammingError * @throws \Zend_Db_Adapter_Exception */ protected function deleteObject($object) { /** @var IcingaService $object */ if ($this->providesOverrides()) { if ($this->hasBeenBlacklisted()) { $this->removeFromBlacklist(); } else { $this->blacklist(); } } else { parent::deleteObject($object); } } /** * @throws IcingaException * @throws \Zend_Db_Adapter_Exception */ protected function blacklist() { $host = $this->host; $service = $this->getServiceToBeBlacklisted(); $db = $this->db->getDbAdapter(); $host->unsetOverriddenServiceVars($this->object->getObjectName())->store(); if ($db->insert('icinga_host_service_blacklist', [ 'host_id' => $host->get('id'), 'service_id' => $service->get('id') ])) { $msg = sprintf( $this->translate('%s has been deactivated on %s'), $service->getObjectName(), $host->getObjectName() ); $this->redirectOnSuccess($msg); } } /** * @return IcingaService * @throws \Icinga\Exception\NotFoundError */ protected function getServiceToBeBlacklisted() { if ($this->set) { return $this->object; } else { return $this->getFirstParent($this->object); } } /** * @throws \Icinga\Exception\NotFoundError */ protected function removeFromBlacklist() { $host = $this->host; $service = $this->getServiceToBeBlacklisted(); $db = $this->db->getDbAdapter(); $where = implode(' AND ', [ $db->quoteInto('host_id = ?', $host->get('id')), $db->quoteInto('service_id = ?', $service->get('id')), ]); if ($db->delete('icinga_host_service_blacklist', $where)) { $msg = sprintf( $this->translate('%s is no longer deactivated on %s'), $service->getObjectName(), $host->getObjectName() ); $this->redirectOnSuccess($msg); } } /** * @param IcingaService $service * @return $this */ public function createApplyRuleFor(IcingaService $service) { $this->apply = $service; $object = $this->object(); $object->set('imports', $service->getObjectName()); $object->set('object_type', 'apply'); $object->set('object_name', $service->getObjectName()); return $this; } /** * @throws \Zend_Form_Exception */ protected function setupServiceElements() { if ($this->object) { $objectType = $this->object->get('object_type'); } elseif ($this->preferredObjectType) { $objectType = $this->preferredObjectType; } else { $objectType = 'template'; } $this->addHidden('object_type', $objectType); $forceCommandElements = $this->hasPermission('director/admin'); $this->addNameElement() ->addHostObjectElement() ->addImportsElement() ->addChoices('service') ->addGroupsElement() ->addDisabledElement() ->addApplyForElement() ->groupMainProperties() ->addAssignmentElements() ->addCheckCommandElements($forceCommandElements) ->addCheckExecutionElements() ->addExtraInfoElements() ->addAgentAndZoneElements() ->setButtons(); } /** * @throws IcingaException * @throws ProgrammingError */ protected function addOverrideHint() { if ($this->object && $this->object->usesVarOverrides()) { $hint = $this->translate( 'This service has been generated in an automated way, but still' . ' allows you to override the following properties in a safe way.' ); } elseif ($apply = $this->applyGenerated) { $hint = Html::sprintf( $this->translate( 'This service has been generated using the %s apply rule, assigned where %s' ), Link::create( $apply->getObjectName(), 'director/service', ['id' => $apply->get('id')], ['data-base-target' => '_next'] ), (string) Filter::fromQueryString($apply->assign_filter) ); } elseif ($this->host && $this->set) { $hint = Html::sprintf( $this->translate( 'This service belongs to the %s Service Set. Still, you might want' . ' to override the following properties for this host only.' ), Link::create( $this->set->getObjectName(), 'director/serviceset', ['id' => $this->set->get('id')], ['data-base-target' => '_next'] ) ); } elseif ($this->inheritedFrom) { $msg = $this->translate( 'This service has been inherited from %s. Still, you might want' . ' to change the following properties for this host only.' ); $name = $this->inheritedFrom; $link = Link::create( $name, 'director/service', [ 'host' => $name, 'name' => $this->object->getObjectName(), ], ['data-base-target' => '_next'] ); $hint = Html::sprintf($msg, $link); } else { throw new ProgrammingError('Got no override hint for your situation'); } $this->setSubmitLabel($this->translate('Override vars')); $this->addHtmlHint($hint, ['name' => 'inheritance_hint']); } protected function setupOnHostForSet() { $msg = $this->translate( 'This service belongs to the service set "%s". Still, you might want' . ' to change the following properties for this host only.' ); $name = $this->set->getObjectName(); $link = Link::create( $name, 'director/serviceset', ['name' => $name], ['data-base-target' => '_next'] ); $this->addHtmlHint( Html::sprintf($msg, $link), ['name' => 'inheritance_hint'] ); $this->addElementsToGroup( ['inheritance_hint'], 'custom_fields', 50, $this->translate('Custom properties') ); $this->setSubmitLabel($this->translate('Override vars')); } protected function addAssignmentElements() { $this->addAssignFilter([ 'suggestionContext' => 'HostFilterColumns', 'required' => true, 'description' => $this->translate( 'This allows you to configure an assignment filter. Please feel' . ' free to combine as many nested operators as you want. The' . ' "contains" operator is valid for arrays only. Please use' . ' wildcards and the = (equals) operator when searching for' . ' partial string matches, like in *.example.com' ) ]); return $this; } /** * @throws \Zend_Form_Exception */ protected function setupHostRelatedElements() { $this->addHidden('host', $this->host->getObjectName()); $this->addHidden('object_type', 'object'); $this->addImportsElement(); $imports = $this->getSentOrObjectValue('imports'); if ($this->hasBeenSent()) { $imports = $this->getElement('imports')->setValue($imports)->getValue(); } if ($this->isNew() && empty($imports)) { $this->groupMainProperties(); return; } $this->addNameElement() ->addChoices('service') ->addDisabledElement() ->addGroupsElement() ->groupMainProperties() ->addCheckCommandElements() ->addExtraInfoElements() ->setButtons(); $this->setDefaultNameFromTemplate($imports); } /** * @param IcingaHost $host * @return $this */ public function setHost(IcingaHost $host) { $this->host = $host; return $this; } /** * @throws \Zend_Form_Exception */ protected function setupSetRelatedElements() { $this->addHidden('service_set', $this->set->getObjectName()); $this->addHidden('object_type', 'apply'); $this->addImportsElement(); $this->setButtons(); $imports = $this->getSentOrObjectValue('imports'); if ($this->hasBeenSent()) { $imports = $this->getElement('imports')->setValue($imports)->getValue(); } if ($this->isNew() && empty($imports)) { $this->groupMainProperties(); return; } $this->addNameElement() ->addDisabledElement() ->addGroupsElement() ->groupMainProperties(); if ($this->hasPermission('director/admin')) { $this->addCheckCommandElements(true) ->addCheckExecutionElements(true) ->addExtraInfoElements(); } $this->setDefaultNameFromTemplate($imports); } public function setServiceSet(IcingaServiceSet $set) { $this->set = $set; return $this; } /** * @return $this * @throws \Zend_Form_Exception */ protected function addNameElement() { $this->addElement('text', 'object_name', array( 'label' => $this->translate('Name'), 'required' => !$this->object()->isApplyRule(), 'description' => $this->translate( 'Name for the Icinga service you are going to create' ) )); if ($this->object()->isApplyRule()) { $this->eventuallyAddNameRestriction('director/service/apply/filter-by-name'); } return $this; } /** * @return $this * @throws \Zend_Form_Exception */ protected function addHostObjectElement() { if ($this->isObject()) { $this->addElement('select', 'host', [ 'label' => $this->translate('Host'), 'required' => true, 'multiOptions' => $this->optionalEnum($this->enumHostsAndTemplates()), 'description' => $this->translate( 'Choose the host this single service should be assigned to' ) ]); } return $this; } /** * @return $this * @throws \Zend_Form_Exception */ protected function addApplyForElement() { if ($this->object->isApplyRule()) { $hostProperties = IcingaHost::enumProperties( $this->object->getConnection(), 'host.', new ArrayCustomVariablesFilter() ); $this->addElement('select', 'apply_for', array( 'label' => $this->translate('Apply For'), 'class' => 'assign-property autosubmit', 'multiOptions' => $this->optionalEnum($hostProperties, $this->translate('None')), 'description' => $this->translate( 'Evaluates the apply for rule for ' . 'all objects with the custom attribute specified. ' . 'E.g selecting "host.vars.custom_attr" will generate "for (config in ' . 'host.vars.array_var)" where "config" will be accessible through "$config$". ' . 'NOTE: only custom variables of type "Array" are eligible.' ) )); } return $this; } /** * @return $this * @throws \Zend_Form_Exception */ protected function addGroupsElement() { $groups = $this->enumServicegroups(); if (! empty($groups)) { $this->addElement('extensibleSet', 'groups', array( 'label' => $this->translate('Groups'), 'multiOptions' => $this->optionallyAddFromEnum($groups), 'positional' => false, 'description' => $this->translate( 'Service groups that should be directly assigned to this service.' . ' Servicegroups can be useful for various reasons. They are' . ' helpful to provided service-type specific view in Icinga Web 2,' . ' either for custom dashboards or as an instrument to enforce' . ' restrictions. Service groups can be directly assigned to' . ' single services or to service templates.' ) )); } return $this; } /** * @return $this * @throws \Zend_Form_Exception */ protected function addAgentAndZoneElements() { if (!$this->isTemplate()) { return $this; } $this->optionalBoolean( 'use_agent', $this->translate('Run on agent'), $this->translate( 'Whether the check commmand for this service should be executed' . ' on the Icinga agent' ) ); $this->addZoneElement(); $elements = array( 'use_agent', 'zone_id', ); $this->addDisplayGroup($elements, 'clustering', array( 'decorators' => array( 'FormElements', array('HtmlTag', array('tag' => 'dl')), 'Fieldset', ), 'order' => self::GROUP_ORDER_CLUSTERING, 'legend' => $this->translate('Icinga Agent and zone settings') )); return $this; } protected function enumHostsAndTemplates() { if ($this->branch && $this->branch->isBranch()) { return $this->enumHosts(); } return [ $this->translate('Templates') => $this->enumHostTemplates(), $this->translate('Hosts') => $this->enumHosts(), ]; } protected function enumHostTemplates() { $names = array_values($this->db->enumHostTemplates()); return array_combine($names, $names); } protected function enumHosts() { $db = $this->db->getDbAdapter(); $table = new ObjectsTableHost($this->db); $table->setAuth($this->getAuth()); if ($this->branch && $this->branch->isBranch()) { $table->setBranchUuid($this->branch->getUuid()); } $result = []; foreach ($db->fetchAll($table->getQuery()->reset(\Zend_Db_Select::LIMIT_COUNT)) as $row) { $result[$row->object_name] = $row->object_name; } return $result; } protected function enumServicegroups() { $db = $this->db->getDbAdapter(); $select = $db->select()->from( 'icinga_servicegroup', array( 'name' => 'object_name', 'display' => 'COALESCE(display_name, object_name)' ) )->where('object_type = ?', 'object')->order('display'); return $db->fetchPairs($select); } protected function succeedForOverrides() { $vars = array(); foreach ($this->object->vars() as $key => $var) { $vars[$key] = $var->getValue(); } $host = $this->host; $serviceName = $this->object->getObjectName(); $this->host->overrideServiceVars($serviceName, (object) $vars); if ($host->hasBeenModified()) { $msg = sprintf( empty($vars) ? $this->translate('All overrides have been removed from "%s"') : $this->translate('The given properties have been stored for "%s"'), $this->translate($host->getObjectName()) ); $this->getDbObjectStore()->store($host); } else { if ($this->isApiRequest()) { $this->setHttpResponseCode(304); } $msg = $this->translate('No action taken, object has not been modified'); } $this->redirectOnSuccess($msg); } public function onSuccess() { if ($this->providesOverrides()) { $this->succeedForOverrides(); return; } parent::onSuccess(); } /** * @param array $imports */ protected function setDefaultNameFromTemplate($imports) { if ($this->hasBeenSent()) { $name = $this->getSentOrObjectValue('object_name'); if ($name === null || !strlen($name)) { $this->setElementValue('object_name', end($imports)); $this->object->set('object_name', end($imports)); } } } }