Example #1
0
 /**
  * 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;
 }
Example #2
0
     // 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');
Example #3
0
/**
 * 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;
}