} else { $resp = UNKNOWN_VALUE; } $httpdetailsTable->addRow(array(CMacrosResolverHelper::resolveHttpTestName($httpTest['hostid'], $httpstep_data['name']), $speed, $respTime, $resp, new CSpan($status['msg'], $status['style']))); } if (!isset($httpTestData['lastfailedstep'])) { $status['msg'] = _('Never executed'); $status['style'] = 'unknown'; } elseif ($httpTestData['lastfailedstep'] != 0) { $status['msg'] = $httpTestData['error'] === null ? _('Unknown error') : _s('Error: %1$s', $httpTestData['error']); $status['style'] = 'disabled'; } else { $status['msg'] = _('OK'); $status['style'] = 'enabled'; } $httpdetailsTable->addRow(array(bold(_('TOTAL')), SPACE, bold($totalTime['value'] ? formatHistoryValue($totalTime['value'], $totalTime) : UNKNOWN_VALUE), SPACE, new CSpan($status['msg'], $status['style'] . ' bold'))); $httpdetailsWidget->addItem($httpdetailsTable); $httpdetailsWidget->show(); echo SBR; // create graphs widget $graphsWidget = new CWidget(); $graphsWidget->addFlicker(new CDiv(null, null, 'scrollbar_cntr'), CProfile::get('web.httpdetails.filter.state', 0)); $graphsWidget->addItem(SPACE); $graphTable = new CTableInfo(); $graphTable->setAttribute('id', 'graph'); // dims $graphDims = getGraphDims(); $graphDims['shiftYtop'] += 1; $graphDims['width'] = -120; $graphDims['graphHeight'] = 150; /*
function getItemDataOverviewCells($tableRow, $ithosts, $hostName) { $css = ''; $value = '-'; $ack = null; if (isset($ithosts[$hostName])) { $item = $ithosts[$hostName]; if ($item['tr_value'] == TRIGGER_VALUE_TRUE) { $css = getSeverityStyle($item['severity']); $ack = get_last_event_by_triggerid($item['triggerid']); $ack = $ack['acknowledged'] == 1 ? array(SPACE, new CImg('images/general/tick.png', 'ack')) : null; } $value = $item['value'] !== null ? formatHistoryValue($item['value'], $item) : UNKNOWN_VALUE; } if ($value != '-') { $value = new CSpan($value, 'link'); } $column = new CCol(array($value, $ack), $css); if (isset($ithosts[$hostName])) { $column->setMenuPopup(getMenuPopupHistory($item)); } $tableRow[] = $column; return $tableRow; }
/** * 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; }
/** * Process screen. * * @return CDiv (screen inside container) */ public function get() { $this->dataId = 'httptest_details'; $httptest = API::HttpTest()->get(['output' => ['httptestid', 'name', 'hostid'], 'selectSteps' => ['httpstepid', 'name', 'no'], 'httptestids' => $this->profileIdx2, 'preservekeys' => true]); $httptest = reset($httptest); if (!$httptest) { $messages = [['type' => 'error', 'message' => _('No permissions to referred object or it does not exist!')]]; return $this->getOutput(makeMessageBox(false, $messages, null, false, false)); } $httptest['lastfailedstep'] = 0; $httptest['error'] = ''; // fetch http test execution data $httptest_data = Manager::HttpTest()->getLastData([$httptest['httptestid']]); if ($httptest_data) { $httptest_data = reset($httptest_data); } // fetch HTTP step items $items = DBfetchArray(DBselect('SELECT i.value_type,i.valuemapid,i.units,i.itemid,hi.type,hs.httpstepid' . ' FROM items i,httpstepitem hi,httpstep hs' . ' WHERE hi.itemid=i.itemid' . ' AND hi.httpstepid=hs.httpstepid' . ' AND hs.httptestid=' . zbx_dbstr($httptest['httptestid']))); $step_items = []; foreach ($items as $item) { $step_items[$item['httpstepid']][$item['type']] = $item; } // fetch HTTP item history $item_history = Manager::History()->getLast($items); $table = (new CTableInfo())->setHeader([_('Step'), _('Speed'), _('Response time'), _('Response code'), _('Status')]); $total_time = ['value' => 0, 'value_type' => null, 'valuemapid' => null, 'units' => null]; order_result($httptest['steps'], 'no'); foreach ($httptest['steps'] as $step_data) { $items_by_type = $step_items[$step_data['httpstepid']]; $status['msg'] = _('OK'); $status['style'] = ZBX_STYLE_GREEN; $status['afterError'] = false; if (!array_key_exists('lastfailedstep', $httptest_data)) { $status['msg'] = _('Never executed'); $status['style'] = ZBX_STYLE_GREY; } elseif ($httptest_data['lastfailedstep'] != 0) { if ($httptest_data['lastfailedstep'] == $step_data['no']) { $status['msg'] = $httptest_data['error'] === null ? _('Unknown error') : _s('Error: %1$s', $httptest_data['error']); $status['style'] = ZBX_STYLE_RED; } elseif ($httptest_data['lastfailedstep'] < $step_data['no']) { $status['msg'] = _('Unknown'); $status['style'] = ZBX_STYLE_GREY; $status['afterError'] = true; } } foreach ($items_by_type as &$item) { // Calculate the total time it took to execute the scenario. // Skip steps that come after a failed step. if (!$status['afterError'] && $item['type'] == HTTPSTEP_ITEM_TYPE_TIME) { $total_time['value_type'] = $item['value_type']; $total_time['valuemapid'] = $item['valuemapid']; $total_time['units'] = $item['units']; if (array_key_exists($item['itemid'], $item_history)) { $history = $item_history[$item['itemid']][0]; $total_time['value'] += $history['value']; } } } unset($item); // step speed $speed_item = $items_by_type[HTTPSTEP_ITEM_TYPE_IN]; if (!$status['afterError'] && array_key_exists($speed_item['itemid'], $item_history) && $item_history[$speed_item['itemid']][0]['value'] > 0) { $speed = formatHistoryValue($item_history[$speed_item['itemid']][0]['value'], $speed_item); } else { $speed = UNKNOWN_VALUE; } // step response time $resptime_item = $items_by_type[HTTPSTEP_ITEM_TYPE_TIME]; if (!$status['afterError'] && array_key_exists($resptime_item['itemid'], $item_history) && $item_history[$resptime_item['itemid']][0]['value'] > 0) { $resp_time = formatHistoryValue($item_history[$resptime_item['itemid']][0]['value'], $resptime_item); } else { $resp_time = UNKNOWN_VALUE; } // step response code $resp_item = $items_by_type[HTTPSTEP_ITEM_TYPE_RSPCODE]; if (!$status['afterError'] && array_key_exists($resp_item['itemid'], $item_history) && $item_history[$resp_item['itemid']][0]['value'] > 0) { $resp = formatHistoryValue($item_history[$resp_item['itemid']][0]['value'], $resp_item); } else { $resp = UNKNOWN_VALUE; } $table->addRow([CMacrosResolverHelper::resolveHttpTestName($httptest['hostid'], $step_data['name']), $speed, $resp_time, $resp, (new CSpan($status['msg']))->addClass($status['style'])]); } if (!array_key_exists('lastfailedstep', $httptest_data)) { $status['msg'] = _('Never executed'); $status['style'] = ZBX_STYLE_GREY; } elseif ($httptest_data['lastfailedstep'] != 0) { $status['msg'] = $httptest_data['error'] === null ? _('Unknown error') : _s('Error: %1$s', $httptest_data['error']); $status['style'] = ZBX_STYLE_RED; } else { $status['msg'] = _('OK'); $status['style'] = ZBX_STYLE_GREEN; } $table->addRow([bold(_('TOTAL')), '', bold($total_time['value'] ? formatHistoryValue($total_time['value'], $total_time) : UNKNOWN_VALUE), '', (new CSpan($status['msg']))->addClass($status['style'])]); return $this->getOutput($table); }
/** * Display OTHER ITEMS (which are not linked to application) */ $tab_rows = array(); foreach ($items as $item) { $lastHistory = isset($history[$item['itemid']][0]) ? $history[$item['itemid']][0] : null; $prevHistory = isset($history[$item['itemid']][1]) ? $history[$item['itemid']][1] : null; if (strpos($item['units'], ',') !== false) { list($item['units'], $item['unitsLong']) = explode(',', $item['units']); } else { $item['unitsLong'] = ''; } // last check time and last value if ($lastHistory) { $lastClock = zbx_date2str(DATE_TIME_FORMAT_SECONDS, $lastHistory['clock']); $lastValue = formatHistoryValue($lastHistory['value'], $item, false); } else { $lastClock = UNKNOWN_VALUE; $lastValue = UNKNOWN_VALUE; } // column "change" $digits = $item['value_type'] == ITEM_VALUE_TYPE_FLOAT ? 2 : 0; if (isset($lastHistory['value']) && isset($prevHistory['value']) && ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT || $item['value_type'] == ITEM_VALUE_TYPE_UINT64) && bcsub($lastHistory['value'], $prevHistory['value'], $digits) != 0) { $change = ''; if ($lastHistory['value'] - $prevHistory['value'] > 0) { $change = '+'; } // for 'unixtime' change should be calculated as uptime $change .= convert_units(array('value' => bcsub($lastHistory['value'], $prevHistory['value'], $digits), 'units' => $item['units'] == 'unixtime' ? 'uptime' : $item['units'])); $change = nbsp($change); } else {
/** * 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; }
/** * Get {ITEM.VALUE} macro. * For triggers macro is resolved in same way as {ITEM.LASTVALUE} macro. Separate methods are created for event description, * where {ITEM.VALUE} macro resolves in different way. * * @param mixed $lastValue * @param array $item * @param array $trigger * * @return string */ protected function getItemValueMacro($lastValue, array $item, array $trigger) { if ($this->config === 'eventDescription') { $value = item_get_history($item, $trigger['clock'], $trigger['ns']); return $value === null ? UNRESOLVED_MACRO_STRING : formatHistoryValue($value, $item); } else { return $this->getItemLastValueMacro($lastValue, $item); } }
/** * 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; }
function getItemDataOverviewCells($tableRow, $ithosts, $hostName) { $ack = null; $css = ''; $value = UNKNOWN_VALUE; if (isset($ithosts[$hostName])) { $item = $ithosts[$hostName]; if ($item['tr_value'] == TRIGGER_VALUE_TRUE) { $css = getSeverityStyle($item['severity']); // Display event acknowledgement. $config = select_config(); if ($config['event_ack_enable']) { $ack = get_last_event_by_triggerid($item['triggerid']); $ack = $ack['acknowledged'] == 1 ? [SPACE, (new CSpan())->addClass(ZBX_STYLE_ICON_ACKN)] : null; } } if ($item['value'] !== null) { $value = formatHistoryValue($item['value'], $item); } } if ($value != UNKNOWN_VALUE) { $value = $value; } $column = (new CCol([$value, $ack]))->addClass($css); if (isset($ithosts[$hostName])) { $column->setMenuPopup(CMenuPopupHelper::getHistory($item))->addClass(ZBX_STYLE_CURSOR_POINTER)->addClass(ZBX_STYLE_NOWRAP); } $tableRow[] = $column; return $tableRow; }
/** * Get item macros. * * @param array $macros * @param array $triggers * @param array $macroValues * @param bool $events resolve {ITEM.VALUE} macro using 'clock' and 'ns' fields * * @return array */ protected function getItemMacros(array $macros, array $triggers, array $macroValues, $events) { if ($macros) { $functions = DbFetchArray(DBselect('SELECT f.triggerid,f.functionid,i.itemid,i.value_type,i.units,i.valuemapid' . ' FROM functions f' . ' JOIN items i ON f.itemid=i.itemid' . ' JOIN hosts h ON i.hostid=h.hostid' . ' WHERE ' . dbConditionInt('f.functionid', array_keys($macros)))); $history = Manager::History()->getLast($functions, 1, ZBX_HISTORY_PERIOD); // False passed to DBfetch to get data without null converted to 0, which is done by default. foreach ($functions as $func) { foreach ($macros[$func['functionid']] as $macro => $fNums) { $lastValue = isset($history[$func['itemid']]) ? $history[$func['itemid']][0]['value'] : null; switch ($macro) { case 'ITEM.LASTVALUE': $replace = $this->getItemLastValueMacro($lastValue, $func); break; case 'ITEM.VALUE': if ($events) { $trigger = $triggers[$func['triggerid']]; $value = item_get_history($func, $trigger['clock'], $trigger['ns']); $replace = $value === null ? UNRESOLVED_MACRO_STRING : formatHistoryValue($value, $func); } else { $replace = $this->getItemLastValueMacro($lastValue, $func); } break; } $macroValues = $this->getFunctionMacroValues($macroValues, $fNums, $func['triggerid'], $macro, $replace); } } } return $macroValues; }