/** * Copies all of the triggers from the source discovery to the target discovery rule. * * @throws APIException if trigger saving fails * * @param array $srcDiscovery The source discovery rule to copy from * @param array $dstDiscovery The target discovery rule to copy to * @param array $srcHost The host the source discovery belongs to * @param array $dstHost The host the target discovery belongs to * * @return array */ protected function copyTriggerPrototypes(array $srcDiscovery, array $dstDiscovery, array $srcHost, array $dstHost) { $srcTriggers = API::TriggerPrototype()->get(['discoveryids' => $srcDiscovery['itemid'], 'output' => API_OUTPUT_EXTEND, 'selectHosts' => API_OUTPUT_EXTEND, 'selectItems' => ['itemid', 'type'], 'selectDiscoveryRule' => API_OUTPUT_EXTEND, 'selectFunctions' => API_OUTPUT_EXTEND, 'selectDependencies' => ['triggerid'], 'preservekeys' => true]); if (!$srcTriggers) { return []; } foreach ($srcTriggers as $id => $trigger) { // Skip trigger prototypes with web items and remove them from source. if (httpItemExists($trigger['items'])) { unset($srcTriggers[$id]); } } /* * Copy the remaining trigger prototypes to a new source. These will contain IDs and original dependencies. * The dependencies from $srcTriggers will be removed. */ $trigger_prototypes = $srcTriggers; // Contains original trigger prototype dependency IDs. $dep_triggerids = []; /* * Collect dependency trigger IDs and remove them from source. Otherwise these IDs do not pass * validation, since they don't belong to destination discovery rule. */ $add_dependencies = false; foreach ($srcTriggers as $id => &$trigger) { if ($trigger['dependencies']) { foreach ($trigger['dependencies'] as $dep_trigger) { $dep_triggerids[] = $dep_trigger['triggerid']; } $add_dependencies = true; } unset($trigger['dependencies']); } unset($trigger); // Save new trigger prototypes and without dependencies for now. $dstTriggers = $srcTriggers; $dstTriggers = CMacrosResolverHelper::resolveTriggerExpressions($dstTriggers); foreach ($dstTriggers as $id => &$trigger) { unset($dstTriggers[$id]['triggerid'], $dstTriggers[$id]['templateid']); // Update the destination expression. $trigger['expression'] = triggerExpressionReplaceHost($trigger['expression'], $srcHost['host'], $dstHost['host']); } unset($trigger); $result = API::TriggerPrototype()->create($dstTriggers); if (!$result) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Cannot clone trigger prototypes.')); } // Process dependencies, if at least one trigger prototype has a dependency. if ($add_dependencies) { $trigger_prototypeids = array_keys($trigger_prototypes); foreach ($result['triggerids'] as $i => $triggerid) { $new_trigger_prototypes[$trigger_prototypeids[$i]] = ['new_triggerid' => $triggerid, 'new_hostid' => $dstHost['hostid'], 'new_host' => $dstHost['host'], 'src_hostid' => $srcHost['hostid'], 'src_host' => $srcHost['host']]; } /* * Search for original dependant triggers and expressions to find corresponding triggers on destination host * with same expression. */ $dep_triggers = API::Trigger()->get(['output' => ['description', 'expression'], 'selectHosts' => ['hostid'], 'triggerids' => $dep_triggerids, 'preservekeys' => true]); $dep_triggers = CMacrosResolverHelper::resolveTriggerExpressions($dep_triggers); // Map dependencies to the new trigger IDs and save. foreach ($trigger_prototypes as &$trigger_prototype) { // Get corresponding created trigger prototype ID. $new_trigger_prototype = $new_trigger_prototypes[$trigger_prototype['triggerid']]; if ($trigger_prototype['dependencies']) { foreach ($trigger_prototype['dependencies'] as &$dependency) { $dep_triggerid = $dependency['triggerid']; /* * We have added a dependant trigger prototype and we know corresponding trigger prototype ID * for newly created trigger prototype. */ if (array_key_exists($dependency['triggerid'], $new_trigger_prototypes)) { /* * Dependency is within same host according to $srcHostId parameter or dep trigger has * single host. */ if ($new_trigger_prototype['src_hostid'] == $new_trigger_prototypes[$dep_triggerid]['src_hostid']) { $dependency['triggerid'] = $new_trigger_prototypes[$dep_triggerid]['new_triggerid']; } } elseif (in_array(['hostid' => $new_trigger_prototype['src_hostid']], $dep_triggers[$dep_triggerid]['hosts'])) { // Get all possible $depTrigger matching triggers by description. $target_triggers = API::Trigger()->get(['output' => ['hosts', 'triggerid', 'expression'], 'hostids' => $new_trigger_prototype['new_hostid'], 'filter' => ['description' => $dep_triggers[$dep_triggerid]['description']], 'preservekeys' => true]); $target_triggers = CMacrosResolverHelper::resolveTriggerExpressions($target_triggers); // Compare exploded expressions for exact match. $expr1 = $dep_triggers[$dep_triggerid]['expression']; $dependency['triggerid'] = null; foreach ($target_triggers as $target_trigger) { $expr2 = triggerExpressionReplaceHost($target_trigger['expression'], $new_trigger_prototype['new_host'], $new_trigger_prototype['src_host']); if ($expr2 === $expr1) { // Matching trigger has been found. $dependency['triggerid'] = $target_trigger['triggerid']; break; } } // If matching trigger was not found, raise exception. if ($dependency['triggerid'] === null) { $expr2 = triggerExpressionReplaceHost($dep_triggers[$dep_triggerid]['expression'], $new_trigger_prototype['src_host'], $new_trigger_prototype['new_host']); error(_s('Cannot add dependency from trigger "%1$s:%2$s" to non existing trigger "%3$s:%4$s".', $trigger_prototype['description'], $trigger_prototype['expression'], $dep_triggers[$dep_triggerid]['description'], $expr2)); return false; } } } unset($dependency); $trigger_prototype['triggerid'] = $new_trigger_prototype['new_triggerid']; } } unset($trigger_prototype); // If adding a dependency fails, the exception will be raised in TriggerPrototype API. API::TriggerPrototype()->addDependencies($trigger_prototypes); } return $result; }
// use the host ID from the page filter since it may not be present in the request // if all hosts are selected, preserve the selected trigger if ($triggerId != 0 && $pageFilter->hostid != 0) { $old_triggers = API::Trigger()->get(['output' => ['description', 'expression'], 'selectHosts' => ['hostid', 'host'], 'triggerids' => [$triggerId]]); $old_trigger = reset($old_triggers); $old_trigger['hosts'] = zbx_toHash($old_trigger['hosts'], 'hostid'); // if the trigger doesn't belong to the selected host - find a new one on that host if (!array_key_exists($pageFilter->hostid, $old_trigger['hosts'])) { $triggerId = 0; $old_expression = CMacrosResolverHelper::resolveTriggerExpression($old_trigger['expression']); $new_triggers = API::Trigger()->get(['output' => ['triggerid', 'description', 'expression'], 'selectHosts' => ['hostid', 'host'], 'filter' => ['description' => $old_trigger['description']], 'hostids' => [$pageFilter->hostid]]); $new_triggers = CMacrosResolverHelper::resolveTriggerExpressions($new_triggers); foreach ($new_triggers as $new_trigger) { $new_trigger['hosts'] = zbx_toHash($new_trigger['hosts'], 'hostid'); foreach ($old_trigger['hosts'] as $old_host) { $new_expression = triggerExpressionReplaceHost($new_trigger['expression'], $new_trigger['hosts'][$pageFilter->hostid]['host'], $old_host['host']); if ($old_expression === $new_expression) { CProfile::update('web.events.filter.triggerid', $new_trigger['triggerid'], PROFILE_TYPE_ID); $triggerId = $new_trigger['triggerid']; break 2; } } } } } } $eventsWidget = (new CWidget())->setTitle(_('Events')); // header $frmForm = (new CForm('get'))->addVar('stime', $stime, 'stime_csv')->addVar('period', $period, 'period_csv')->addVar('page', getPageNumber(), 'page_csv'); if (hasRequest('source')) { $frmForm->addVar('source', getRequest('source'), 'source_csv');
/** * Copies the given triggers to the given hosts or templates. * * Without the $srcHostId parameter it will only be able to copy triggers that belong to only one host. If the * $srcHostId parameter is not passed, and a trigger has multiple hosts, it will throw an error. If the * $srcHostId parameter is passed, the given host will be replaced with the destination host. * * This function takes care of copied trigger dependencies. * If trigger is copied alongside with trigger on which it depends, then dependencies is replaced directly using new ids, * If there is target host within dependency trigger, algorithm will search for potential matching trigger in target host, * if matching trigger is found, then id from this trigger is used, if not rise exception, * otherwise original dependency will be left. * * * @param int|array $srcTriggerIds triggers which will be copied to $dstHostIds * @param int|array $dstHostIds hosts and templates to whom add triggers, ids not present in DB (host table) will be ignored * @param int $srcHostId host id in which context trigger with multiple hosts will be treated * * @return bool */ function copyTriggersToHosts($srcTriggerIds, $dstHostIds, $srcHostId = null) { $options = ['triggerids' => $srcTriggerIds, 'output' => ['triggerid', 'expression', 'description', 'url', 'status', 'priority', 'comments', 'type'], 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL], 'selectDependencies' => ['triggerid']]; if ($srcHostId) { $srcHost = API::Host()->get(['output' => ['host'], 'hostids' => $srcHostId, 'preservekeys' => true, 'templated_hosts' => true]); // If provided $srcHostId doesn't match any record in DB, return false. if (!($srcHost = reset($srcHost))) { return false; } } else { $options['selectHosts'] = ['host']; } $dbSrcTriggers = API::Trigger()->get($options); $dbSrcTriggers = CMacrosResolverHelper::resolveTriggerExpressions($dbSrcTriggers); $dbDstHosts = API::Host()->get(['output' => ['hostid', 'host'], 'hostids' => $dstHostIds, 'preservekeys' => true, 'templated_hosts' => true]); $newTriggers = []; // Create each trigger for each host. foreach ($dbDstHosts as $dstHost) { foreach ($dbSrcTriggers as $srcTrigger) { // If $srcHostId provided, get host 'host' for triggerExpressionReplaceHost(). if ($srcHostId != 0) { $host = $srcHost['host']; $srcTriggerContextHostId = $srcHostId; } else { /* * If we have multiple hosts in trigger expression and we haven't pointed ($srcHostId) which host to * replace, call error. */ if (count($srcTrigger['hosts']) > 1) { error(_s('Cannot copy trigger "%1$s:%2$s", because it has multiple hosts in the expression.', $srcTrigger['description'], $srcTrigger['expression'])); return false; } $host = $srcTrigger['hosts'][0]['host']; $srcTriggerContextHostId = $srcTrigger['hosts'][0]['hostid']; } $srcTrigger['expression'] = triggerExpressionReplaceHost($srcTrigger['expression'], $host, $dstHost['host']); // The dependddencies must be added after all triggers are created. $result = API::Trigger()->create([['description' => $srcTrigger['description'], 'expression' => $srcTrigger['expression'], 'url' => $srcTrigger['url'], 'status' => $srcTrigger['status'], 'priority' => $srcTrigger['priority'], 'comments' => $srcTrigger['comments'], 'type' => $srcTrigger['type']]]); if (!$result) { return false; } $newTriggers[$srcTrigger['triggerid']][] = ['newTriggerId' => reset($result['triggerids']), 'newTriggerExpression' => $srcTrigger['expression'], 'newTriggerHostId' => $dstHost['hostid'], 'newTriggerHost' => $dstHost['host'], 'srcTriggerContextHostId' => $srcTriggerContextHostId, 'srcTriggerContextHost' => $host]; } } $depids = []; foreach ($dbSrcTriggers as $srcTrigger) { foreach ($srcTrigger['dependencies'] as $depTrigger) { $depids[] = $depTrigger['triggerid']; } } $depTriggers = API::Trigger()->get(['triggerids' => $depids, 'output' => ['description', 'expression'], 'selectHosts' => ['hostid'], 'preservekeys' => true]); $depTriggers = CMacrosResolverHelper::resolveTriggerExpressions($depTriggers); // Map dependencies to the new trigger IDs and save. if ($newTriggers) { $dependencies = []; foreach ($dbSrcTriggers as $srcTrigger) { if ($srcTrigger['dependencies']) { // Get corresponding created triggers. $dst_triggers = $newTriggers[$srcTrigger['triggerid']]; foreach ($dst_triggers as $dst_trigger) { foreach ($srcTrigger['dependencies'] as $depTrigger) { /* * We have added $depTrigger trigger and we know corresponding trigger ID for newly * created trigger. */ if (array_key_exists($depTrigger['triggerid'], $newTriggers)) { $dst_dep_triggers = $newTriggers[$depTrigger['triggerid']]; foreach ($dst_dep_triggers as $dst_dep_trigger) { /* * Dependency is within same host according to $srcHostId parameter or dep trigger has * single host. */ if ($dst_trigger['srcTriggerContextHostId'] == $dst_dep_trigger['srcTriggerContextHostId']) { $depTriggerId = $dst_dep_trigger['newTriggerId']; break; } else { $depTriggerId = $depTrigger['triggerid']; } } } elseif (in_array(['hostid' => $dst_trigger['srcTriggerContextHostId']], $depTriggers[$depTrigger['triggerid']]['hosts'])) { // Get all possible $depTrigger matching triggers by description. $targetHostTriggersByDescription = API::Trigger()->get(['hostids' => $dst_trigger['newTriggerHostId'], 'output' => ['hosts', 'triggerid', 'expression'], 'filter' => ['description' => $depTriggers[$depTrigger['triggerid']]['description']], 'preservekeys' => true]); $targetHostTriggersByDescription = CMacrosResolverHelper::resolveTriggerExpressions($targetHostTriggersByDescription); // Compare exploded expressions for exact match. $expr1 = $depTriggers[$depTrigger['triggerid']]['expression']; $depTriggerId = null; foreach ($targetHostTriggersByDescription as $potentialTargetTrigger) { $expr2 = triggerExpressionReplaceHost($potentialTargetTrigger['expression'], $dst_trigger['newTriggerHost'], $dst_trigger['srcTriggerContextHost']); if ($expr2 == $expr1) { // Matching trigger has been found. $depTriggerId = $potentialTargetTrigger['triggerid']; break; } } // If matching trigger wasn't found raise exception. if ($depTriggerId === null) { $expr2 = triggerExpressionReplaceHost($expr1, $dst_trigger['srcTriggerContextHost'], $dst_trigger['newTriggerHost']); error(_s('Cannot add dependency from trigger "%1$s:%2$s" to non existing trigger "%3$s:%4$s".', $srcTrigger['description'], $dst_trigger['newTriggerExpression'], $depTriggers[$depTrigger['triggerid']]['description'], $expr2)); return false; } } else { $depTriggerId = $depTrigger['triggerid']; } $dependencies[] = ['triggerid' => $dst_trigger['newTriggerId'], 'dependsOnTriggerid' => $depTriggerId]; } } } } if ($dependencies) { if (!API::Trigger()->addDependencies($dependencies)) { return false; } } } return true; }