get([ 'output' => ['graphid', 'name'], 'hostids' => $hostids, 'search' => $name !== '' ? ['name' => $name] : null, 'selectItems' => ['itemid'], 'preservekeys' => true ]); $graph_items = []; foreach ($graphs as $graph) { foreach ($graph['items'] as $item) { $graph_items[$item['itemid']] = $item; } } $filter_items = []; if ($name !== '') { $filter_items = API::Item()->get([ 'output' => ['itemid'], 'hostids' => $hostids, 'search' => ['name' => $name], 'preservekeys' => true ]); if ($filter_items) { $graphs += API::Graph()->get([ 'output' => ['graphid', 'name'], 'hostids' => $hostids, 'itemids' => array_keys($filter_items), 'selectItems' => ['itemid'], 'preservekeys' => true ]); } } $items = []; if ($graph_items || $filter_items) { $items = API::Item()->get([ 'output' => ['itemid', 'name'], 'hostids' => $hostids, 'itemids' => array_keys($graph_items + $filter_items), 'selectTags' => ['tag', 'value'], 'preservekeys' => true ]); } return $this->addTagsToGraphs($graphs, $items); } private function addTagsToGraphs(array $graphs, array $items): array { foreach ($graphs as &$graph) { $graph['tags'] = []; $tags = []; foreach ($graph['items'] as $item) { if (!array_key_exists($item['itemid'], $items)) { continue; } foreach ($items[$item['itemid']]['tags'] as $tag) { $tags[$tag['tag']][$tag['value']] = true; } } foreach ($tags as $tag_name => $tag) { foreach (array_keys($tag) as $tag_value) { $graph['tags'][] = [ 'tag' => $tag_name, 'value' => $tag_value ]; } } } unset($graph); return $graphs; } /** * Fetches all host graphs based on hostid and graph name or item name used in graph. * * @param array $hostids Limit returned graphs to these hosts. * @param string $name Graphs or items in them should contain this string in their name. * * @return array */ protected function getSimpleGraphs(array $hostids, string $name): array { return API::Item()->get([ 'output' => ['itemid', 'name'], 'selectTags' => ['tag', 'value'], // TODO VM: filter by tags 'hostids' => $hostids, 'search' => $name !== '' ? ['name' => $name] : null, 'filter' => ['value_type' => [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT]], 'preservekeys' => true ]); } /** * Prepares graph display details (graph dimensions, URL and sBox-ing flag). * * @param array $graphids Graph IDs for which details need to be prepared. * * @return array */ protected function getCharts(array $graphs): array { $charts = []; foreach ($graphs as $graph) { if (array_key_exists('graphid', $graph)) { $chart = [ 'chartid' => 'graph_'.$graph['graphid'], 'graphid' => $graph['graphid'], 'dimensions' => getGraphDims($graph['graphid']) ]; if (in_array($chart['dimensions']['graphtype'], [GRAPH_TYPE_PIE, GRAPH_TYPE_EXPLODED])) { $chart['sbox'] = false; $chart['src'] = 'chart6.php'; } else { $chart['sbox'] = true; $chart['src'] = 'chart2.php'; } } else { $chart = [ 'chartid' => 'item_'.$graph['itemid'], 'itemid' => $graph['itemid'], 'dimensions' => getGraphDims(), 'sbox' => true, 'src' => 'chart.php' ]; } $charts[] = $chart; } return $charts; } /** * Prepare subfilter fields from filter. * * @param array $filter * @param array $filter['subfilter_tagnames'] Selected tagname subfilter parameters. * @param array $filter['subfilter_tags'] Selected tags subfilter parameters. * * @return array */ protected static function getSubfilterFields(array $filter): array { $subfilters = []; $subfilter_keys = ['subfilter_tagnames', 'subfilter_tags']; foreach ($subfilter_keys as $key) { if (!array_key_exists($key, $filter)) { continue; } if ($key === 'subfilter_tags') { $tmp_tags = []; foreach ($filter[$key] as $tag => $tag_values) { $tmp_tags[urldecode($tag)] = array_flip($tag_values); } $subfilters[$key] = $tmp_tags; unset($tmp_tags); } else { $subfilters[$key] = array_flip($filter[$key]); } } return CArrayHelper::renameKeys($subfilters, [ 'subfilter_tagnames' => 'tagnames', 'subfilter_tags' => 'tags' ]); } /** * Find what subfilters are available based on hosts selected using the main filter. * * @param array $graphs [IN/OUT] Result of host/simple graphs matching primary filter. * @param string $graphs[]['graphid'] [IN] Host graph graphid. * @param array $graphs[]['tags'] [IN] Item tags array. * @param string $graphs[]['tags'][]['tag'] [IN] Tag name. * @param string $graphs[]['tags'][]['value'] [IN] Tag value. * @param array $graphs[]['matching_subfilters'] [OUT] Flag for each of subfilter group showing either graph fits * its subfilter requirements. * @param bool $graphs[]['has_data'] [OUT] Flag either item has data. * @param array $subfilters Selected subfilters. * * @return array */ protected static function getSubfilters(array &$graphs, array $subfilters): array { $subfilter_options = self::getSubfilterOptions($graphs, $subfilters); $subfilters = self::clearSubfilters($subfilters, $subfilter_options); $graphs = self::getGraphMatchings($graphs, $subfilters); /* * Calculate how many additional items would match the filtering results after selecting each of provided host * subfilters. So item MUST match all subfilters except the tested one. */ foreach ($graphs as $graph) { // Calculate the counters of tag existence subfilter options. $graph_tagnames = []; $selected_tagnames = array_intersect(array_keys($subfilters['tagnames']), array_column($graph['tags'], 'tag')); foreach ($graph['tags'] as $tag) { if (array_key_exists($tag['tag'], $graph_tagnames)) { continue; } $graph_tagnames[$tag['tag']] = []; if ($selected_tagnames && !in_array($tag['tag'], $selected_tagnames)) { continue; } $graph_matches = true; foreach ($graph['matching_subfilters'] as $filter_name => $match) { if ($filter_name === 'tagnames') { continue; } if (!$match) { $graph_matches = false; break; } } if ($graph_matches) { $subfilter_options['tagnames'][$tag['tag']]['count']++; } } // Calculate the same for the tag/value pair subfilter options. $selected_tagvalues = []; if ($subfilters['tags']) { foreach ($graph['tags'] as $tag) { if (array_key_exists($tag['tag'], $subfilters['tags']) && array_key_exists($tag['value'], $subfilters['tags'][$tag['tag']])) { $selected_tagvalues[$tag['tag']][$tag['value']] = []; } } } foreach ($graph['tags'] as $tag) { if ($selected_tagvalues) { if (!(array_key_exists($tag['tag'], $selected_tagvalues) && array_key_exists($tag['value'], $selected_tagvalues[$tag['tag']]))) { continue; } } $graph_matches = true; foreach ($graph['matching_subfilters'] as $filter_name => $match) { if ($filter_name === 'tags') { continue; } if (!$match) { $graph_matches = false; break; } } if ($graph_matches) { $subfilter_options['tags'][$tag['tag']][$tag['value']]['count']++; } } } return $subfilter_options; } /** * Collect available options of subfilter from existing items and hosts selected by primary filter. * All currently selected options will be included as well, regardless their presence in the retrieved data. * * @param array $graphs Host/Simple graphs selected by primary filter. * @param array $graphs[]['tags'] Item tags. * @param array $graphs[]['tags'][]['tag'] Item tag name. * @param array $graphs[]['tags'][]['value'] Item tag value. * @param array $subfilter * @param array $subfilter['tagnames'] Selected subfilter names. * @param array $subfilter['tags'] Selected subfilter tags. * * @return array */ protected static function getSubfilterOptions(array $graphs, array $subfilter): array { $subfilter_options = [ 'tagnames' => [], 'tags' => [] ]; // First, add currently selected options, regardless their presence in the retrieved data. foreach (array_keys($subfilter['tagnames']) as $tagname) { $subfilter_options['tagnames'][$tagname] = [ 'name' => $tagname, 'selected' => true, 'count' => 0 ]; } foreach ($subfilter['tags'] as $tag => $values) { foreach (array_keys($values) as $value) { $subfilter_options['tags'][$tag][$value] = [ 'name' => $value, 'selected' => true, 'count' => 0 ]; } } // Second, add options represented by the selected data. foreach ($graphs as $graph) { foreach ($graph['tags'] as $tag) { if (!array_key_exists($tag['tag'], $subfilter_options['tagnames'])) { $subfilter_options['tagnames'][$tag['tag']] = [ 'name' => $tag['tag'], 'selected' => array_key_exists($tag['tag'], $subfilter['tagnames']), 'count' => 0 ]; } $subfilter_options['tags'][$tag['tag']][$tag['value']] = [ 'name' => $tag['value'], 'selected' => (array_key_exists($tag['tag'], $subfilter['tags']) && array_key_exists($tag['value'], $subfilter['tags'][$tag['tag']]) ), 'count' => 0 ]; } } // Sort subfilters by values. CArrayHelper::sort($subfilter_options['tagnames'], ['name']); uksort($subfilter_options['tags'], 'strnatcmp'); array_walk($subfilter_options['tags'], function (&$tag_values) { CArrayHelper::sort($tag_values, ['name']); }); return $subfilter_options; } protected static function clearSubfilters(array $subfilter, array $subfilter_options): array { foreach (array_keys($subfilter['tagnames']) as $tagname) { if (!array_key_exists($tagname, $subfilter_options['tagnames'])) { unset($subfilter['tagnames'][$tagname]); } } foreach ($subfilter['tags'] as $tag => $values) { if (!array_key_exists($tag, $subfilter_options['tags'])) { unset($subfilter['tags'][$tag]); continue; } foreach (array_keys($values) as $value) { if (!array_key_exists($value, $subfilter_options['tags'][$tag])) { unset($subfilter['tags'][$tag][$value]); } } } return $subfilter; } /** * Calculate which items retrieved using the primary filter matches selected subfilter options. Results are added to * the array stored with 'matching_subfilters' key for each retrieved item. Additionally 'has_data' flag is added to * each of retrieved item to indicate either particular item has data. * * @param array $graphs * @param string $graphs[]['graphid'] Item hostid. * @param string $graphs[]['itemid'] Item itemid. * @param array $graphs[]['tags'] Items tags. * @param array $graphs[]['tags'][]['tag'] Items tag name. * @param array $graphs[]['tags'][]['value'] Items tag value. * @param array $subfilter * @param array $subfilter['tagnames'] Selected subfilter tagnames. * @param array $subfilter['tags'] Selected subfilter tags. * * @return array */ protected static function getGraphMatchings(array $graphs, array $subfilter): array { foreach ($graphs as &$graph) { $match_tagnames = (!$subfilter['tagnames'] || (bool) array_intersect_key($subfilter['tagnames'], array_flip(array_column($graph['tags'], 'tag'))) ); if ($subfilter['tags']) { $match_tags = false; foreach ($graph['tags'] as $tag) { if (array_key_exists($tag['tag'], $subfilter['tags']) && array_key_exists($tag['value'], $subfilter['tags'][$tag['tag']])) { $match_tags = true; break; } } } else { $match_tags = true; } $graph['matching_subfilters'] = [ 'tagnames' => $match_tagnames, 'tags' => $match_tags ]; } unset($graph); return $graphs; } /** * Unset items not matching selected subfilters. * * @param array $graphs * @param array $graphs['matching_subfilters'] Contains flags either items matches all selected subfilters. * * @return array */ protected static function applySubfilters(array $graphs): array { return array_filter($graphs, function ($graph) { return array_sum($graph['matching_subfilters']) == count($graph['matching_subfilters']); }); } /** * Make subset of most severe subfilters to reduce the space used by subfilter. * * @param array $subfilters * @param string $subfilters[]['name'] Option name. * @param bool $subfilters[]['selected'] Flag indicating if option is selected. * * @return array */ public static function getMostSevereSubfilters(array $subfilters): array { if (self::SUBFILTERS_VALUES_PER_ROW >= count($subfilters)) { return $subfilters; } // All selected subfilters always must be included. $most_severe = array_filter($subfilters, function ($elmnt) { return $elmnt['selected']; }); // Add first non-selected subfilter values in case if limit is not exceeded. $remaining = self::SUBFILTERS_VALUES_PER_ROW - count($most_severe); if ($remaining > 0) { $subfilters = array_diff_key($subfilters, $most_severe); $most_severe += array_slice($subfilters, 0, $remaining, true); } CArrayHelper::sort($most_severe, ['name']); return $most_severe; } /** * Make subset of most severe tag value subfilters to reduce the space used by subfilter. * * @param array $tags * @param bool $tags[][]['selected'] Flag indicating if tag value is selected. * * @return array */ public static function getMostSevereTagValueSubfilters(array $tags): array { if (self::SUBFILTERS_TAG_VALUE_ROWS >= count($tags)) { return $tags; } // All selected subfilters always must be included. $most_severe = array_filter($tags, function ($tag) { return (bool) array_sum(array_column($tag, 'selected')); }); // Add first non-selected subfilter values in case if limit is not exceeded. $remaining = self::SUBFILTERS_TAG_VALUE_ROWS - count($most_severe); if ($remaining > 0) { $tags = array_diff_key($tags, $most_severe); $most_severe += array_slice($tags, 0, $remaining, true); } uksort($most_severe, 'strnatcmp'); return $most_severe; } }