/** * @param $data * * @return bool */ public function syncTemplates(array $data) { $data['templateids'] = zbx_toArray($data['templateids']); $data['hostids'] = zbx_toArray($data['hostids']); $triggers = $this->get(['output' => ['triggerid', 'expression', 'description', 'url', 'status', 'priority', 'comments', 'type'], 'hostids' => $data['templateids'], 'preservekeys' => true]); $triggers = CMacrosResolverHelper::resolveTriggerExpressions($triggers); foreach ($triggers as $trigger) { $this->inherit($trigger, $data['hostids']); } return true; }
** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ $widget = (new CWidget())->setTitle(_('Trigger prototypes'))->setControls((new CForm('get'))->cleanItems()->addVar('parent_discoveryid', $this->data['parent_discoveryid'])->addItem((new CList())->addItem(new CSubmit('form', _('Create trigger prototype')))))->addItem(get_header_host_table('triggers', $this->data['hostid'], $this->data['parent_discoveryid'])); // create form $triggersForm = (new CForm())->setName('triggersForm')->addVar('parent_discoveryid', $this->data['parent_discoveryid']); // create table $triggersTable = (new CTableInfo())->setHeader([(new CColHeader((new CCheckBox('all_triggers'))->onClick("checkAll('" . $triggersForm->getName() . "', 'all_triggers', 'g_triggerid');")))->addClass(ZBX_STYLE_CELL_WIDTH), make_sorting_header(_('Severity'), 'priority', $this->data['sort'], $this->data['sortorder']), make_sorting_header(_('Name'), 'description', $this->data['sort'], $this->data['sortorder']), _('Expression'), make_sorting_header(_('Status'), 'status', $this->data['sort'], $this->data['sortorder'])]); $this->data['triggers'] = CMacrosResolverHelper::resolveTriggerExpressions($this->data['triggers'], ['html' => true]); foreach ($this->data['triggers'] as $trigger) { $triggerid = $trigger['triggerid']; $trigger['discoveryRuleid'] = $this->data['parent_discoveryid']; // description $description = []; if ($trigger['templateid'] > 0) { if (!isset($this->data['realHosts'][$triggerid])) { $description[] = (new CSpan(_('Template')))->addClass(ZBX_STYLE_GREY); $description[] = NAME_DELIMITER; } else { $real_hosts = $this->data['realHosts'][$triggerid]; $real_host = reset($real_hosts); $tpl_disc_ruleid = get_realrule_by_itemid_and_hostid($this->data['parent_discoveryid'], $real_host['hostid']); $description[] = (new CLink(CHtml::encode($real_host['name']), 'trigger_prototypes.php?parent_discoveryid=' . $tpl_disc_ruleid))->addClass(ZBX_STYLE_LINK_ALT)->addClass(ZBX_STYLE_GREY); $description[] = NAME_DELIMITER;
$host = reset($item['hosts']); show_messages(); $exprs = getRequest('expressions', false); if ($exprs && ($expression = $constructor->getExpressionFromParts($host['host'], $item['key_'], $exprs))) { if (!check_right_on_trigger_by_expression(PERM_READ_WRITE, $expression)) { access_deny(); } $now = time(); $status = hasRequest('status') ? TRIGGER_STATUS_DISABLED : TRIGGER_STATUS_ENABLED; $type = TRIGGER_MULT_EVENT_ENABLED; if (hasRequest('triggerid')) { $triggerId = getRequest('triggerid'); $description = getRequest('description', ''); $db_triggers = API::Trigger()->get(['output' => ['description', 'expression', 'templateid'], 'triggerids' => [$triggerId]]); if ($db_triggers[0]['templateid'] != 0) { $db_triggers = CMacrosResolverHelper::resolveTriggerExpressions($db_triggers); $description = $db_triggers[0]['description']; $expression = $db_triggers[0]['expression']; } $trigger = []; $trigger['triggerid'] = $triggerId; $trigger['expression'] = $expression; $trigger['description'] = $description; $trigger['type'] = $type; $trigger['priority'] = getRequest('priority', 0); $trigger['status'] = $status; $trigger['comments'] = getRequest('comments', ''); $trigger['url'] = getRequest('url', ''); $result = (bool) API::Trigger()->update($trigger); $auditAction = AUDIT_ACTION_UPDATE; show_messages($result, _('Trigger updated'), _('Cannot update trigger'));
/** * Get triggers references by trigger ids. * * @param array $triggerIds * * @return array */ protected function getTriggersReferences(array $triggerIds) { $ids = []; $triggers = API::Trigger()->get(['triggerids' => $triggerIds, 'output' => ['description', 'expression'], 'preservekeys' => true]); $triggers = CMacrosResolverHelper::resolveTriggerExpressions($triggers); foreach ($triggers as $id => $trigger) { $ids[$id] = ['description' => $trigger['description'], 'expression' => $trigger['expression']]; } return $ids; }
/** * Unlinks the templates from the given hosts. If $tragetids is set to null, the templates will be unlinked from * all hosts. * * @param array $templateids * @param null|array $targetids the IDs of the hosts to unlink the templates from * @param bool $clear delete all of the inherited objects from the hosts */ protected function unlink($templateids, $targetids = null, $clear = false) { $flags = $clear ? [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE] : [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE, ZBX_FLAG_DISCOVERY_PROTOTYPE]; // check that all triggers on templates that we unlink, don't have items from another templates $sql = 'SELECT DISTINCT t.description' . ' FROM triggers t,functions f,items i' . ' WHERE t.triggerid=f.triggerid' . ' AND f.itemid=i.itemid' . ' AND ' . dbConditionInt('i.hostid', $templateids) . ' AND EXISTS (' . 'SELECT ff.triggerid' . ' FROM functions ff,items ii' . ' WHERE ff.itemid=ii.itemid' . ' AND ff.triggerid=t.triggerid' . ' AND ' . dbConditionInt('ii.hostid', $templateids, true) . ')' . ' AND t.flags=' . ZBX_FLAG_DISCOVERY_NORMAL; if ($dbTrigger = DBfetch(DBSelect($sql, 1))) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Cannot unlink trigger "%s", it has items from template that is left linked to host.', $dbTrigger['description'])); } $sqlFrom = ' triggers t,hosts h'; $sqlWhere = ' EXISTS (' . 'SELECT ff.triggerid' . ' FROM functions ff,items ii' . ' WHERE ff.triggerid=t.templateid' . ' AND ii.itemid=ff.itemid' . ' AND ' . dbConditionInt('ii.hostid', $templateids) . ')' . ' AND ' . dbConditionInt('t.flags', $flags); if (!is_null($targetids)) { $sqlFrom = ' triggers t,functions f,items i,hosts h'; $sqlWhere .= ' AND ' . dbConditionInt('i.hostid', $targetids) . ' AND f.itemid=i.itemid' . ' AND t.triggerid=f.triggerid' . ' AND h.hostid=i.hostid'; } $sql = 'SELECT DISTINCT t.triggerid,t.description,t.flags,t.expression,h.name as host' . ' FROM ' . $sqlFrom . ' WHERE ' . $sqlWhere; $dbTriggers = DBSelect($sql); $triggers = [ZBX_FLAG_DISCOVERY_NORMAL => [], ZBX_FLAG_DISCOVERY_PROTOTYPE => []]; $triggerids = []; while ($trigger = DBfetch($dbTriggers)) { $triggers[$trigger['flags']][$trigger['triggerid']] = ['description' => $trigger['description'], 'expression' => $trigger['expression'], 'triggerid' => $trigger['triggerid'], 'host' => $trigger['host']]; if (!in_array($trigger['triggerid'], $triggerids)) { array_push($triggerids, $trigger['triggerid']); } } if (!empty($triggers[ZBX_FLAG_DISCOVERY_NORMAL])) { $triggers[ZBX_FLAG_DISCOVERY_NORMAL] = CMacrosResolverHelper::resolveTriggerExpressions($triggers[ZBX_FLAG_DISCOVERY_NORMAL]); if ($clear) { $result = API::Trigger()->delete(array_keys($triggers[ZBX_FLAG_DISCOVERY_NORMAL]), true); if (!$result) { self::exception(ZBX_API_ERROR_INTERNAL, _('Cannot unlink and clear triggers')); } } else { DB::update('triggers', ['values' => ['templateid' => 0], 'where' => ['triggerid' => array_keys($triggers[ZBX_FLAG_DISCOVERY_NORMAL])]]); foreach ($triggers[ZBX_FLAG_DISCOVERY_NORMAL] as $trigger) { info(_s('Unlinked: Trigger "%1$s" on "%2$s".', $trigger['description'], $trigger['host'])); } } } if (!empty($triggers[ZBX_FLAG_DISCOVERY_PROTOTYPE])) { $triggers[ZBX_FLAG_DISCOVERY_PROTOTYPE] = CMacrosResolverHelper::resolveTriggerExpressions($triggers[ZBX_FLAG_DISCOVERY_PROTOTYPE]); if ($clear) { $result = API::TriggerPrototype()->delete(array_keys($triggers[ZBX_FLAG_DISCOVERY_PROTOTYPE]), true); if (!$result) { self::exception(ZBX_API_ERROR_INTERNAL, _('Cannot unlink and clear triggers')); } } else { DB::update('triggers', ['values' => ['templateid' => 0], 'where' => ['triggerid' => array_keys($triggers[ZBX_FLAG_DISCOVERY_PROTOTYPE])]]); foreach ($triggers[ZBX_FLAG_DISCOVERY_PROTOTYPE] as $trigger) { info(_s('Unlinked: Trigger prototype "%1$s" on "%2$s".', $trigger['description'], $trigger['host'])); } } } /* ITEMS, DISCOVERY RULES {{{ */ $sqlFrom = ' items i1,items i2,hosts h'; $sqlWhere = ' i2.itemid=i1.templateid' . ' AND ' . dbConditionInt('i2.hostid', $templateids) . ' AND ' . dbConditionInt('i1.flags', $flags) . ' AND h.hostid=i1.hostid'; if (!is_null($targetids)) { $sqlWhere .= ' AND ' . dbConditionInt('i1.hostid', $targetids); } $sql = 'SELECT DISTINCT i1.itemid,i1.flags,i1.name,i1.hostid,h.name as host' . ' FROM ' . $sqlFrom . ' WHERE ' . $sqlWhere; $dbItems = DBSelect($sql); $items = [ZBX_FLAG_DISCOVERY_NORMAL => [], ZBX_FLAG_DISCOVERY_RULE => [], ZBX_FLAG_DISCOVERY_PROTOTYPE => []]; while ($item = DBfetch($dbItems)) { $items[$item['flags']][$item['itemid']] = ['name' => $item['name'], 'host' => $item['host']]; } if (!empty($items[ZBX_FLAG_DISCOVERY_RULE])) { if ($clear) { $result = API::DiscoveryRule()->delete(array_keys($items[ZBX_FLAG_DISCOVERY_RULE]), true); if (!$result) { self::exception(ZBX_API_ERROR_INTERNAL, _('Cannot unlink and clear discovery rules')); } } else { DB::update('items', ['values' => ['templateid' => 0], 'where' => ['itemid' => array_keys($items[ZBX_FLAG_DISCOVERY_RULE])]]); foreach ($items[ZBX_FLAG_DISCOVERY_RULE] as $discoveryRule) { info(_s('Unlinked: Discovery rule "%1$s" on "%2$s".', $discoveryRule['name'], $discoveryRule['host'])); } } } if (!empty($items[ZBX_FLAG_DISCOVERY_NORMAL])) { if ($clear) { $result = API::Item()->delete(array_keys($items[ZBX_FLAG_DISCOVERY_NORMAL]), true); if (!$result) { self::exception(ZBX_API_ERROR_INTERNAL, _('Cannot unlink and clear items')); } } else { DB::update('items', ['values' => ['templateid' => 0], 'where' => ['itemid' => array_keys($items[ZBX_FLAG_DISCOVERY_NORMAL])]]); foreach ($items[ZBX_FLAG_DISCOVERY_NORMAL] as $item) { info(_s('Unlinked: Item "%1$s" on "%2$s".', $item['name'], $item['host'])); } } } if (!empty($items[ZBX_FLAG_DISCOVERY_PROTOTYPE])) { $item_prototypeids = array_keys($items[ZBX_FLAG_DISCOVERY_PROTOTYPE]); if ($clear) { // This will include deletion of linked application prototypes. $result = API::Itemprototype()->delete($item_prototypeids, true); if (!$result) { self::exception(ZBX_API_ERROR_INTERNAL, _('Cannot unlink and clear item prototypes')); } } else { DB::update('items', ['values' => ['templateid' => 0], 'where' => ['itemid' => $item_prototypeids]]); foreach ($items[ZBX_FLAG_DISCOVERY_PROTOTYPE] as $item) { info(_s('Unlinked: Item prototype "%1$s" on "%2$s".', $item['name'], $item['host'])); } /* * Convert templated application prototypes to normal application prototypes * who are linked to these item prototypes. */ $application_prototypes = DBfetchArray(DBselect('SELECT ap.application_prototypeid' . ' FROM application_prototype ap' . ' WHERE EXISTS (' . 'SELECT NULL' . ' FROM item_application_prototype iap' . ' WHERE ' . dbConditionInt('iap.itemid', $item_prototypeids) . ' AND iap.application_prototypeid=ap.application_prototypeid' . ')')); if ($application_prototypes) { $application_prototypeids = zbx_objectValues($application_prototypes, 'application_prototypeid'); DB::update('application_prototype', ['values' => ['templateid' => 0], 'where' => ['application_prototypeid' => $application_prototypeids]]); } } } /* }}} ITEMS, DISCOVERY RULES */ // host prototypes // we need only to unlink host prototypes. in case of unlink and clear they will be deleted together with LLD rules. if (!$clear && isset($items[ZBX_FLAG_DISCOVERY_RULE])) { $discoveryRuleIds = array_keys($items[ZBX_FLAG_DISCOVERY_RULE]); $hostPrototypes = DBfetchArrayAssoc(DBSelect('SELECT DISTINCT h.hostid,h.host,h3.host AS parent_host' . ' FROM hosts h' . ' INNER JOIN host_discovery hd ON h.hostid=hd.hostid' . ' INNER JOIN hosts h2 ON h.templateid=h2.hostid' . ' INNER JOIN host_discovery hd2 ON h.hostid=hd.hostid' . ' INNER JOIN items i ON hd.parent_itemid=i.itemid' . ' INNER JOIN hosts h3 ON i.hostid=h3.hostid' . ' WHERE ' . dbConditionInt('hd.parent_itemid', $discoveryRuleIds)), 'hostid'); if ($hostPrototypes) { DB::update('hosts', ['values' => ['templateid' => 0], 'where' => ['hostid' => array_keys($hostPrototypes)]]); DB::update('group_prototype', ['values' => ['templateid' => 0], 'where' => ['hostid' => array_keys($hostPrototypes)]]); foreach ($hostPrototypes as $hostPrototype) { info(_s('Unlinked: Host prototype "%1$s" on "%2$s".', $hostPrototype['host'], $hostPrototype['parent_host'])); } } } /* GRAPHS {{{ */ $sqlFrom = ' graphs g,hosts h'; $sqlWhere = ' EXISTS (' . 'SELECT ggi.graphid' . ' FROM graphs_items ggi,items ii' . ' WHERE ggi.graphid=g.templateid' . ' AND ii.itemid=ggi.itemid' . ' AND ' . dbConditionInt('ii.hostid', $templateids) . ')' . ' AND ' . dbConditionInt('g.flags', $flags); if (!is_null($targetids)) { $sqlFrom = ' graphs g,graphs_items gi,items i,hosts h'; $sqlWhere .= ' AND ' . dbConditionInt('i.hostid', $targetids) . ' AND gi.itemid=i.itemid' . ' AND g.graphid=gi.graphid' . ' AND h.hostid=i.hostid'; } $sql = 'SELECT DISTINCT g.graphid,g.name,g.flags,h.name as host' . ' FROM ' . $sqlFrom . ' WHERE ' . $sqlWhere; $dbGraphs = DBSelect($sql); $graphs = [ZBX_FLAG_DISCOVERY_NORMAL => [], ZBX_FLAG_DISCOVERY_PROTOTYPE => []]; while ($graph = DBfetch($dbGraphs)) { $graphs[$graph['flags']][$graph['graphid']] = ['name' => $graph['name'], 'graphid' => $graph['graphid'], 'host' => $graph['host']]; } if (!empty($graphs[ZBX_FLAG_DISCOVERY_PROTOTYPE])) { if ($clear) { $result = API::GraphPrototype()->delete(array_keys($graphs[ZBX_FLAG_DISCOVERY_PROTOTYPE]), true); if (!$result) { self::exception(ZBX_API_ERROR_INTERNAL, _('Cannot unlink and clear graph prototypes')); } } else { DB::update('graphs', ['values' => ['templateid' => 0], 'where' => ['graphid' => array_keys($graphs[ZBX_FLAG_DISCOVERY_PROTOTYPE])]]); foreach ($graphs[ZBX_FLAG_DISCOVERY_PROTOTYPE] as $graph) { info(_s('Unlinked: Graph prototype "%1$s" on "%2$s".', $graph['name'], $graph['host'])); } } } if (!empty($graphs[ZBX_FLAG_DISCOVERY_NORMAL])) { if ($clear) { $result = API::Graph()->delete(array_keys($graphs[ZBX_FLAG_DISCOVERY_NORMAL]), true); if (!$result) { self::exception(ZBX_API_ERROR_INTERNAL, _('Cannot unlink and clear graphs.')); } } else { DB::update('graphs', ['values' => ['templateid' => 0], 'where' => ['graphid' => array_keys($graphs[ZBX_FLAG_DISCOVERY_NORMAL])]]); foreach ($graphs[ZBX_FLAG_DISCOVERY_NORMAL] as $graph) { info(_s('Unlinked: Graph "%1$s" on "%2$s".', $graph['name'], $graph['host'])); } } } /* }}} GRAPHS */ // http tests $sqlWhere = ''; if (!is_null($targetids)) { $sqlWhere = ' AND ' . dbConditionInt('ht1.hostid', $targetids); } $sql = 'SELECT DISTINCT ht1.httptestid,ht1.name,h.name as host' . ' FROM httptest ht1' . ' INNER JOIN httptest ht2 ON ht2.httptestid=ht1.templateid' . ' INNER JOIN hosts h ON h.hostid=ht1.hostid' . ' WHERE ' . dbConditionInt('ht2.hostid', $templateids) . $sqlWhere; $dbHttpTests = DBSelect($sql); $httpTests = []; while ($httpTest = DBfetch($dbHttpTests)) { $httpTests[$httpTest['httptestid']] = ['name' => $httpTest['name'], 'host' => $httpTest['host']]; } if (!empty($httpTests)) { if ($clear) { $result = API::HttpTest()->delete(array_keys($httpTests), true); if (!$result) { self::exception(ZBX_API_ERROR_INTERNAL, _('Cannot unlink and clear Web scenarios.')); } } else { DB::update('httptest', ['values' => ['templateid' => 0], 'where' => ['httptestid' => array_keys($httpTests)]]); foreach ($httpTests as $httpTest) { info(_s('Unlinked: Web scenario "%1$s" on "%2$s".', $httpTest['name'], $httpTest['host'])); } } } /* APPLICATIONS {{{ */ $sql = 'SELECT at.application_templateid,at.applicationid,h.name,h.host,h.hostid' . ' FROM applications a1,application_template at,applications a2,hosts h' . ' WHERE a1.applicationid=at.applicationid' . ' AND at.templateid=a2.applicationid' . ' AND ' . dbConditionInt('a2.hostid', $templateids) . ' AND a1.hostid=h.hostid'; if ($targetids) { $sql .= ' AND ' . dbConditionInt('a1.hostid', $targetids); } $query = DBselect($sql); $applicationTemplates = []; while ($applicationTemplate = DBfetch($query)) { $applicationTemplates[] = ['applicationid' => $applicationTemplate['applicationid'], 'application_templateid' => $applicationTemplate['application_templateid'], 'name' => $applicationTemplate['name'], 'hostid' => $applicationTemplate['hostid'], 'host' => $applicationTemplate['host']]; } if ($applicationTemplates) { // unlink applications from templates DB::delete('application_template', ['application_templateid' => zbx_objectValues($applicationTemplates, 'application_templateid')]); if ($clear) { // Delete inherited applications that are no longer linked to any templates and items. $applicationids = zbx_objectValues($applicationTemplates, 'applicationid'); $applications = DBfetchArray(DBselect('SELECT a.applicationid' . ' FROM applications a' . ' LEFT JOIN application_template at ON a.applicationid=at.applicationid' . ' WHERE ' . dbConditionInt('a.applicationid', $applicationids) . ' AND at.applicationid IS NULL' . ' AND a.applicationid NOT IN (' . 'SELECT ia.applicationid' . ' FROM items_applications ia' . ' WHERE ' . dbConditionInt('ia.applicationid', $applicationids) . ')')); $result = API::Application()->delete(zbx_objectValues($applications, 'applicationid'), true); if (!$result) { self::exception(ZBX_API_ERROR_INTERNAL, _('Cannot unlink and clear applications.')); } } else { foreach ($applicationTemplates as $application) { info(_s('Unlinked: Application "%1$s" on "%2$s".', $application['name'], $application['host'])); } } } /* * Process discovered applications when parent is a host, not template. * If a discovered application has no longer linked items, remove them. */ if ($targetids) { $discovered_applications = API::Application()->get(['output' => ['applicationid'], 'hostids' => $targetids, 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_CREATED], 'preservekeys' => true]); if ($discovered_applications) { $discovered_applications = API::Application()->get(['output' => ['applicationid'], 'selectItems' => ['itemid'], 'applicationids' => array_keys($discovered_applications), 'filter' => ['flags' => ZBX_FLAG_DISCOVERY_CREATED]]); $applications_to_delete = []; foreach ($discovered_applications as $discovered_application) { if (!$discovered_application['items']) { $applications_to_delete[$discovered_application['applicationid']] = true; } } if ($applications_to_delete) { API::Application()->delete(array_keys($applications_to_delete), true); } } } /* }}} APPLICATIONS */ parent::unlink($templateids, $targetids); }
/** * Generate data for the trigger configuration form. * * @param string $exprAction expression constructor action, see remakeExpression() for a list of supported values * * @return array */ function getTriggerFormData($exprAction) { $data = ['form' => getRequest('form'), 'form_refresh' => getRequest('form_refresh'), 'parent_discoveryid' => getRequest('parent_discoveryid'), 'dependencies' => getRequest('dependencies', []), 'db_dependencies' => [], 'triggerid' => getRequest('triggerid'), 'expression' => getRequest('expression', ''), 'expr_temp' => getRequest('expr_temp', ''), 'description' => getRequest('description', ''), 'type' => getRequest('type', 0), 'priority' => getRequest('priority', 0), 'status' => getRequest('status', 0), 'comments' => getRequest('comments', ''), 'url' => getRequest('url', ''), 'input_method' => getRequest('input_method', IM_ESTABLISHED), 'limited' => false, 'templates' => [], 'hostid' => getRequest('hostid', 0)]; if (!empty($data['triggerid'])) { // get trigger $options = ['output' => API_OUTPUT_EXTEND, 'selectHosts' => ['hostid'], 'triggerids' => $data['triggerid']]; $triggers = $data['parent_discoveryid'] ? API::TriggerPrototype()->get($options) : API::Trigger()->get($options); $triggers = CMacrosResolverHelper::resolveTriggerExpressions($triggers); $data['trigger'] = reset($triggers); // get templates $tmp_triggerid = $data['triggerid']; do { $db_triggers = DBfetch(DBselect('SELECT t.triggerid,t.templateid,id.parent_itemid,h.name,h.hostid' . ' FROM triggers t' . ' LEFT JOIN functions f ON t.triggerid=f.triggerid' . ' LEFT JOIN items i ON f.itemid=i.itemid' . ' LEFT JOIN hosts h ON i.hostid=h.hostid' . ' LEFT JOIN item_discovery id ON i.itemid=id.itemid' . ' WHERE t.triggerid=' . zbx_dbstr($tmp_triggerid))); if (bccomp($data['triggerid'], $tmp_triggerid) != 0) { // parent trigger prototype link if ($data['parent_discoveryid']) { $link = 'trigger_prototypes.php?form=update&triggerid=' . $db_triggers['triggerid'] . '&parent_discoveryid=' . $db_triggers['parent_itemid'] . '&hostid=' . $db_triggers['hostid']; } else { $link = 'triggers.php?form=update&triggerid=' . $db_triggers['triggerid'] . '&hostid=' . $db_triggers['hostid']; } $data['templates'][] = new CLink(CHtml::encode($db_triggers['name']), $link); $data['templates'][] = SPACE . '⇒' . SPACE; } $tmp_triggerid = $db_triggers['templateid']; } while ($tmp_triggerid != 0); $data['templates'] = array_reverse($data['templates']); array_shift($data['templates']); $data['limited'] = $data['trigger']['templateid'] != 0; // select first host from triggers if gived not match $hosts = $data['trigger']['hosts']; if (count($hosts) > 0 && !in_array(['hostid' => $data['hostid']], $hosts)) { $host = reset($hosts); $data['hostid'] = $host['hostid']; } } if (!empty($data['triggerid']) && !isset($_REQUEST['form_refresh']) || $data['limited']) { $data['expression'] = $data['trigger']['expression']; if (!$data['limited'] || !isset($_REQUEST['form_refresh'])) { $data['description'] = $data['trigger']['description']; $data['type'] = $data['trigger']['type']; $data['priority'] = $data['trigger']['priority']; $data['status'] = $data['trigger']['status']; $data['comments'] = $data['trigger']['comments']; $data['url'] = $data['trigger']['url']; $db_triggers = DBselect('SELECT t.triggerid,t.description' . ' FROM triggers t,trigger_depends d' . ' WHERE t.triggerid=d.triggerid_up' . ' AND d.triggerid_down=' . zbx_dbstr($data['triggerid'])); while ($trigger = DBfetch($db_triggers)) { if (uint_in_array($trigger['triggerid'], $data['dependencies'])) { continue; } array_push($data['dependencies'], $trigger['triggerid']); } } } if ($data['input_method'] == IM_TREE) { $analyze = analyzeExpression($data['expression']); if ($analyze !== false) { list($data['outline'], $data['eHTMLTree']) = $analyze; if ($exprAction !== null && $data['eHTMLTree'] != null) { $new_expr = remakeExpression($data['expression'], $_REQUEST['expr_target_single'], $exprAction, $data['expr_temp']); if ($new_expr !== false) { $data['expression'] = $new_expr; $analyze = analyzeExpression($data['expression']); if ($analyze !== false) { list($data['outline'], $data['eHTMLTree']) = $analyze; } else { show_messages(false, '', _('Expression Syntax Error.')); } $data['expr_temp'] = ''; } else { show_messages(false, '', _('Expression Syntax Error.')); } } $data['expression_field_name'] = 'expr_temp'; $data['expression_field_value'] = $data['expr_temp']; $data['expression_field_readonly'] = true; } else { show_messages(false, '', _('Expression Syntax Error.')); $data['input_method'] = IM_ESTABLISHED; } } if ($data['input_method'] != IM_TREE) { $data['expression_field_name'] = 'expression'; $data['expression_field_value'] = $data['expression']; $data['expression_field_readonly'] = $data['limited']; } if ($data['dependencies']) { $dependencyTriggers = API::Trigger()->get(['output' => ['triggerid', 'description', 'flags'], 'selectHosts' => ['hostid', 'name'], 'triggerids' => $data['dependencies'], 'preservekeys' => true]); if ($data['parent_discoveryid']) { $dependencyTriggerPrototypes = API::TriggerPrototype()->get(['output' => ['triggerid', 'description', 'flags'], 'selectHosts' => ['hostid', 'name'], 'triggerids' => $data['dependencies'], 'preservekeys' => true]); $data['db_dependencies'] = $dependencyTriggers + $dependencyTriggerPrototypes; } else { $data['db_dependencies'] = $dependencyTriggers; } } foreach ($data['db_dependencies'] as &$dependency) { order_result($dependency['hosts'], 'name', ZBX_SORT_UP); } unset($dependency); order_result($data['db_dependencies'], 'description'); return $data; }
$exprAction = 'r'; } elseif (getRequest('remove_expression')) { $exprAction = 'R'; $_REQUEST['expr_target_single'] = $_REQUEST['remove_expression']; } elseif (isset($_REQUEST['clone']) && isset($_REQUEST['triggerid'])) { unset($_REQUEST['triggerid']); $_REQUEST['form'] = 'clone'; } elseif (hasRequest('add') || hasRequest('update')) { $trigger = ['expression' => getRequest('expression'), 'description' => getRequest('description'), 'url' => getRequest('url'), 'status' => getRequest('status'), 'priority' => getRequest('priority'), 'comments' => getRequest('comments'), 'type' => getRequest('type'), 'dependencies' => zbx_toObject(getRequest('dependencies', []), 'triggerid')]; if (hasRequest('update')) { // Update only changed fields. $old_trigger_prototypes = API::TriggerPrototype()->get(['output' => ['expression', 'description', 'url', 'status', 'priority', 'comments', 'type'], 'selectDependencies' => ['triggerid'], 'triggerids' => getRequest('triggerid')]); if (!$old_trigger_prototypes) { access_deny(); } $old_trigger_prototypes = CMacrosResolverHelper::resolveTriggerExpressions($old_trigger_prototypes); $old_trigger_prototype = reset($old_trigger_prototypes); $old_trigger_prototype['dependencies'] = zbx_toHash(zbx_objectValues($old_trigger_prototype['dependencies'], 'triggerid')); $newDependencies = $trigger['dependencies']; $oldDependencies = $old_trigger_prototype['dependencies']; unset($trigger['dependencies'], $old_trigger_prototype['dependencies']); $triggerToUpdate = array_diff_assoc($trigger, $old_trigger_prototype); $triggerToUpdate['triggerid'] = getRequest('triggerid'); // dependencies $updateDepencencies = false; if (count($newDependencies) != count($oldDependencies)) { $updateDepencencies = true; } else { foreach ($newDependencies as $dependency) { if (!isset($oldDependencies[$dependency['triggerid']])) { $updateDepencencies = true;
/** * 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; }
/** * Select trigger ids for previously added trigger names/expressions. */ protected function selectTriggers() { if (!empty($this->triggers)) { $this->triggersRefs = []; $dbTriggers = API::Trigger()->get(['output' => ['triggerid', 'expression', 'description'], 'filter' => ['description' => array_keys($this->triggers), 'flags' => [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_PROTOTYPE, ZBX_FLAG_DISCOVERY_CREATED]]]); $dbTriggers = CMacrosResolverHelper::resolveTriggerExpressions($dbTriggers); foreach ($dbTriggers as $dbTrigger) { if (isset($this->triggers[$dbTrigger['description']][$dbTrigger['expression']])) { $this->triggersRefs[$dbTrigger['description']][$dbTrigger['expression']] = $dbTrigger['triggerid']; } } } }
/** * Checks that no trigger with the same description and expression as $trigger exist on the given host. * Assumes the given trigger is valid. * * @throws APIException if at least one trigger exists * * @param array $trigger a trigger with an exploded expression * @param null $hostid * * @return void */ protected function checkIfExistsOnHost(array $trigger, $hostId = null) { // skip the check if the description and expression haven't been changed if (!isset($trigger['description']) && !isset($trigger['expression'])) { return; } // make sure we have all the required data if (!isset($trigger['description']) || !isset($trigger['expression'])) { $explodeExpression = !isset($trigger['expression']); $trigger = $this->extendObject($this->tableName(), $trigger, ['description', 'expression']); if ($explodeExpression) { $trigger['expression'] = CMacrosResolverHelper::resolveTriggerExpression($trigger['expression']); } } $filter = ['description' => $trigger['description']]; if ($hostId) { $filter['hostid'] = $hostId; } else { $expressionData = new CTriggerExpression($trigger['expression']); $expressionData->parse($trigger['expression']); $expressionHosts = $expressionData->getHosts(); $filter['host'] = reset($expressionHosts); } $triggers = $this->get(['filter' => $filter, 'output' => ['expression', 'triggerid'], 'nopermissions' => true]); $triggers = CMacrosResolverHelper::resolveTriggerExpressions($triggers); foreach ($triggers as $dbTrigger) { // check if the expressions are also equal and that this is a different trigger $differentTrigger = !isset($trigger['triggerid']) || !idcmp($trigger['triggerid'], $dbTrigger['triggerid']); if ($dbTrigger['expression'] === $trigger['expression'] && $differentTrigger) { $options = ['output' => ['name'], 'templated_hosts' => true, 'nopermissions' => true, 'limit' => 1]; if (isset($filter['host'])) { $options['filter'] = ['host' => $filter['host']]; } else { $options['hostids'] = $hostId; } $host = API::Host()->get($options); $host = reset($host); self::exception(ZBX_API_ERROR_PARAMETERS, _s('Trigger "%1$s" already exists on "%2$s".', $trigger['description'], $host['name'])); } } }
/** * 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; }
} if (!$showMaintenance) { $options['maintenance'] = false; } $triggers = API::Trigger()->get($options); order_result($triggers, $sortField, $sortOrder); $url = (new CUrl('tr_status.php'))->setArgument('fullscreen', getRequest('fullscreen'))->setArgument('groupid', $pageFilter->groupid)->setArgument('hostid', $pageFilter->hostid)->setArgument('show_triggers', getRequest('show_triggers'))->setArgument('filter_set', getRequest('filter_set')); $paging = getPagingLine($triggers, $sortOrder, $url); $triggers = API::Trigger()->get(['triggerids' => zbx_objectValues($triggers, 'triggerid'), 'output' => API_OUTPUT_EXTEND, 'selectHosts' => ['hostid', 'name', 'description', 'status', 'maintenanceid', 'maintenance_status', 'maintenance_type'], 'selectItems' => ['itemid', 'hostid', 'name', 'key_', 'value_type'], 'selectDependencies' => API_OUTPUT_EXTEND, 'selectLastEvent' => ['eventid', 'objectid', 'clock', 'ns'], 'preservekeys' => true]); $triggers = CMacrosResolverHelper::resolveTriggerUrls($triggers); if ($showDetails) { foreach ($triggers as &$trigger) { $trigger['expression_orig'] = $trigger['expression']; } unset($trigger); $triggers = CMacrosResolverHelper::resolveTriggerExpressions($triggers, ['html' => true, 'resolve_usermacros' => true, 'resolve_macros' => true]); foreach ($triggers as &$trigger) { $trigger['expression_html'] = $trigger['expression']; $trigger['expression'] = $trigger['expression_orig']; unset($trigger['expression_orig']); } unset($trigger); } order_result($triggers, $sortField, $sortOrder); // sort trigger hosts by name foreach ($triggers as &$trigger) { if (count($trigger['hosts']) > 1) { order_result($trigger['hosts'], 'name', ZBX_SORT_UP); } } unset($trigger);