/** * Expand functional macros in given map label. * * @param string $label label to expand * @param array $replaceHosts list of hosts in order which they appear in trigger expression if trigger label is given, * or single host when host label is given * * @return string expanded label */ function resolveMapLabelMacros($label, $replaceHosts = null) { // find functional macro pattern $pattern = null === $replaceHosts ? '/{' . ZBX_PREG_HOST_FORMAT . ":.+\\.(last|max|min|avg)\\([0-9]+[smhdwKMGT]?\\)}/Uu" : '/{(' . ZBX_PREG_HOST_FORMAT . "|{HOSTNAME[0-9]?}|{HOST\\.HOST[0-9]?}):.+\\.(last|max|min|avg)\\([0-9]+[smhdwKMGT]?\\)}/Uu"; preg_match_all($pattern, $label, $matches); // for each functional macro foreach ($matches[0] as $expr) { $macro = $expr; if ($replaceHosts !== null) { // search for macros with all possible indecies foreach ($replaceHosts as $i => $host) { $macroTmp = $macro; // repalce only macro in first position $macro = preg_replace('/{({HOSTNAME' . $i . '}|{HOST\\.HOST' . $i . '}):(.*)}/U', '{' . $host['host'] . ':$2}', $macro); // only one simple macro possible inside functional macro if ($macro != $macroTmp) { break; } } } // try to create valid expression $expressionData = new CTriggerExpression(); if (!$expressionData->parse($macro) || !isset($expressionData->expressions[0])) { continue; } // look in DB for coressponding item $itemHost = $expressionData->expressions[0]['host']; $key = $expressionData->expressions[0]['item']; $function = $expressionData->expressions[0]['functionName']; $parameter = convertFunctionValue($expressionData->expressions[0]['functionParamList'][0]); $item = API::Item()->get(array('webitems' => true, 'filter' => array('host' => $itemHost, 'key_' => $key), 'output' => array('lastclock', 'value_type', 'lastvalue', 'units', 'valuemapid'))); $item = reset($item); // if no corresponding item found with functional macro key and host if (!$item) { $label = str_replace($expr, '???', $label); continue; } // do function type (last, min, max, avg) related actions if (0 == strcmp($function, 'last')) { $value = $item['lastclock'] ? formatHistoryValue($item['lastvalue'], $item) : UNRESOLVED_MACRO_STRING; } elseif (0 == strcmp($function, 'min') || 0 == strcmp($function, 'max') || 0 == strcmp($function, 'avg')) { $value = getItemFunctionalValue($item, $function, $parameter); } if (isset($value)) { $label = str_replace($expr, $value, $label); } } return $label; }
/** * Expand functional macros in given map label. * * @param string $label label to expand * @param array $replaceHosts list of hosts in order which they appear in trigger expression if trigger label is given, * or single host when host label is given * * @return string */ public function resolveMapLabelMacros($label, $replaceHosts = null) { $functionsPattern = '(last|max|min|avg)\\(([0-9]+[' . ZBX_TIME_SUFFIXES . ']?)?\\)'; // Find functional macro pattern. $pattern = $replaceHosts === null ? '/{' . ZBX_PREG_HOST_FORMAT . ':.+\\.' . $functionsPattern . '}/Uu' : '/{(' . ZBX_PREG_HOST_FORMAT . '|{HOSTNAME[0-9]?}|{HOST\\.HOST[0-9]?}):.+\\.' . $functionsPattern . '}/Uu'; preg_match_all($pattern, $label, $matches); // For each functional macro. foreach ($matches[0] as $expr) { $macro = $expr; if ($replaceHosts !== null) { // Search for macros with all possible indices. foreach ($replaceHosts as $i => $host) { $macroTmp = $macro; // Replace only macro in first position. $macro = preg_replace('/{({HOSTNAME' . $i . '}|{HOST\\.HOST' . $i . '}):(.*)}/U', '{' . $host['host'] . ':$2}', $macro); // Only one simple macro possible inside functional macro. if ($macro !== $macroTmp) { break; } } } // Try to create valid expression. $expressionData = new CTriggerExpression(); if (!$expressionData->parse($macro) || !isset($expressionData->expressions[0])) { continue; } // Look in DB for corresponding item. $itemHost = $expressionData->expressions[0]['host']; $key = $expressionData->expressions[0]['item']; $function = $expressionData->expressions[0]['functionName']; $item = API::Item()->get(['output' => ['itemid', 'value_type', 'units', 'valuemapid', 'lastvalue', 'lastclock'], 'webitems' => true, 'filter' => ['host' => $itemHost, 'key_' => $key]]); $item = reset($item); // If no corresponding item found with functional macro key and host. if (!$item) { $label = str_replace($expr, UNRESOLVED_MACRO_STRING, $label); continue; } // Do function type (last, min, max, avg) related actions. if ($function === 'last') { $value = $item['lastclock'] ? formatHistoryValue($item['lastvalue'], $item) : UNRESOLVED_MACRO_STRING; } else { $value = getItemFunctionalValue($item, $function, $expressionData->expressions[0]['functionParamList'][0]); } if (isset($value)) { $label = str_replace($expr, $value, $label); } } return $label; }
/** * Resolve functional macros, like {hostname:key.function(param)}. * If macro can not be resolved it is replaced with UNRESOLVED_MACRO_STRING string i.e. "*UNKNOWN*" * Supports function "last", "min", "max" and "avg". * Supports seconds as parameters, except "last" function. * Supports postfixes s,m,h,d and w for parameter. * * @param array $strList list of string in which macros should be resolved * @param array $itemsList list of lists of graph items * @param int $items[n][m]['hostid'] n-th graph m-th item corresponding host Id * @param string $items[n][m]['host'] n-th graph m-th item corresponding host name * * @return array list of strings with macros replaced with corresponding values */ private function resolveGraphsFunctionalItemMacros($strList, $itemsList) { // retrieve all string macros and all host-key pairs $hostKeyPairs = array(); $matchesList = array(); $items = reset($itemsList); foreach ($strList as $str) { // extract all macros into $matches - keys: macros, hosts, keys, functions and parameters are used // searches for macros, for example, "{somehost:somekey["param[123]"].min(10m)}" preg_match_all('/(?<macros>{' . '(?<hosts>(' . ZBX_PREG_HOST_FORMAT . '|({(' . self::PATTERN_HOST_INTERNAL . ')' . self::PATTERN_MACRO_PARAM . '}))):' . '(?<keys>' . ZBX_PREG_ITEM_KEY_FORMAT . ')\\.' . '(?<functions>(last|max|min|avg))\\(' . '(?<parameters>([0-9]+[smhdw]?))' . '\\)}{1})/Uux', $str, $matches, PREG_OFFSET_CAPTURE); if (!empty($matches['hosts'])) { foreach ($matches['hosts'] as $i => $host) { $matches['hosts'][$i][0] = $this->resolveGraphPositionalMacros($host[0], $items); if ($matches['hosts'][$i][0] !== UNRESOLVED_MACRO_STRING) { if (!isset($hostKeyPairs[$matches['hosts'][$i][0]])) { $hostKeyPairs[$matches['hosts'][$i][0]] = array(); } $hostKeyPairs[$matches['hosts'][$i][0]][$matches['keys'][$i][0]] = 1; } } $matchesList[] = $matches; $items = next($itemsList); } } // stop, if no macros found if (empty($matchesList)) { return $strList; } // build item retrieval query from host-key pairs $query = 'SELECT h.host,i.key_,i.itemid,i.value_type,i.units,i.valuemapid' . ' FROM items i, hosts h' . ' WHERE i.hostid=h.hostid AND ('; foreach ($hostKeyPairs as $host => $keys) { $query .= '(h.host=' . zbx_dbstr($host) . ' AND i.key_ IN('; foreach ($keys as $key => $val) { $query .= zbx_dbstr($key) . ','; } $query = substr($query, 0, -1) . ')) OR '; } $query = substr($query, 0, -4) . ')'; // get necessary items for all graph strings $items = DBfetchArrayAssoc(DBselect($query), 'itemid'); $allowedItems = API::Item()->get(array('itemids' => array_keys($items), 'webitems' => true, 'output' => array('itemid', 'value_type'), 'preservekeys' => true)); // map item data only for allowed items foreach ($items as $item) { if (isset($allowedItems[$item['itemid']])) { $hostKeyPairs[$item['host']][$item['key_']] = $item; } } // fetch history $history = Manager::History()->getLast($items); // replace macros with their corresponding values in graph strings $matches = reset($matchesList); foreach ($strList as &$str) { // iterate array backwards! $i = count($matches['macros']); while ($i--) { // host is real and item exists and has permissions if ($matches['hosts'][$i][0] !== UNRESOLVED_MACRO_STRING && is_array($hostKeyPairs[$matches['hosts'][$i][0]][$matches['keys'][$i][0]])) { $item = $hostKeyPairs[$matches['hosts'][$i][0]][$matches['keys'][$i][0]]; // macro function is "last" if ($matches['functions'][$i][0] == 'last') { $value = isset($history[$item['itemid']]) ? formatHistoryValue($history[$item['itemid']][0]['value'], $item) : UNRESOLVED_MACRO_STRING; } else { $value = getItemFunctionalValue($item, $matches['functions'][$i][0], $matches['parameters'][$i][0]); } } else { $value = UNRESOLVED_MACRO_STRING; } $str = substr_replace($str, $value, $matches['macros'][$i][1], strlen($matches['macros'][$i][0])); } $matches = next($matchesList); } unset($str); return $strList; }