You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
972 lines
25 KiB
972 lines
25 KiB
1 year ago
|
<?php
|
||
|
/*
|
||
|
** Zabbix
|
||
|
** Copyright (C) 2001-2023 Zabbix SIA
|
||
|
**
|
||
|
** This program is free software; you can redistribute it and/or modify
|
||
|
** it under the terms of the GNU General Public License as published by
|
||
|
** the Free Software Foundation; either version 2 of the License, or
|
||
|
** (at your option) any later version.
|
||
|
**
|
||
|
** 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.
|
||
|
**/
|
||
|
|
||
|
|
||
|
function graphType($type = null) {
|
||
|
$types = [
|
||
|
GRAPH_TYPE_NORMAL => _('Normal'),
|
||
|
GRAPH_TYPE_STACKED => _('Stacked'),
|
||
|
GRAPH_TYPE_PIE => _('Pie'),
|
||
|
GRAPH_TYPE_EXPLODED => _('Exploded')
|
||
|
];
|
||
|
|
||
|
if (is_null($type)) {
|
||
|
return $types;
|
||
|
}
|
||
|
elseif (isset($types[$type])) {
|
||
|
return $types[$type];
|
||
|
}
|
||
|
else {
|
||
|
return _('Unknown');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function graph_item_drawtypes() {
|
||
|
return [
|
||
|
GRAPH_ITEM_DRAWTYPE_LINE,
|
||
|
GRAPH_ITEM_DRAWTYPE_FILLED_REGION,
|
||
|
GRAPH_ITEM_DRAWTYPE_BOLD_LINE,
|
||
|
GRAPH_ITEM_DRAWTYPE_DOT,
|
||
|
GRAPH_ITEM_DRAWTYPE_DASHED_LINE,
|
||
|
GRAPH_ITEM_DRAWTYPE_GRADIENT_LINE
|
||
|
];
|
||
|
}
|
||
|
|
||
|
function graph_item_drawtype2str($drawtype) {
|
||
|
switch ($drawtype) {
|
||
|
case GRAPH_ITEM_DRAWTYPE_LINE:
|
||
|
return _('Line');
|
||
|
case GRAPH_ITEM_DRAWTYPE_FILLED_REGION:
|
||
|
return _('Filled region');
|
||
|
case GRAPH_ITEM_DRAWTYPE_BOLD_LINE:
|
||
|
return _('Bold line');
|
||
|
case GRAPH_ITEM_DRAWTYPE_DOT:
|
||
|
return _('Dot');
|
||
|
case GRAPH_ITEM_DRAWTYPE_DASHED_LINE:
|
||
|
return _('Dashed line');
|
||
|
case GRAPH_ITEM_DRAWTYPE_GRADIENT_LINE:
|
||
|
return _('Gradient line');
|
||
|
default:
|
||
|
return _('Unknown');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function graph_item_aggr_fnc2str($calc_fnc) {
|
||
|
switch ($calc_fnc) {
|
||
|
case AGGREGATE_NONE:
|
||
|
return _('none');
|
||
|
case AGGREGATE_MIN:
|
||
|
return _('min');
|
||
|
case AGGREGATE_MAX:
|
||
|
return _('max');
|
||
|
case AGGREGATE_AVG:
|
||
|
return _('avg');
|
||
|
case AGGREGATE_COUNT:
|
||
|
return _('count');
|
||
|
case AGGREGATE_SUM:
|
||
|
return _('sum');
|
||
|
case AGGREGATE_FIRST:
|
||
|
return _('first');
|
||
|
case AGGREGATE_LAST:
|
||
|
return _('last');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function getGraphDims($graphid = null) {
|
||
|
$graphDims = [];
|
||
|
|
||
|
$graphDims['shiftYtop'] = CGraphDraw::DEFAULT_HEADER_PADDING_TOP;
|
||
|
if (is_null($graphid)) {
|
||
|
$graphDims['graphHeight'] = 200;
|
||
|
$graphDims['graphtype'] = 0;
|
||
|
|
||
|
if (GRAPH_YAXIS_SIDE_DEFAULT == 0) {
|
||
|
$graphDims['shiftXleft'] = 85;
|
||
|
$graphDims['shiftXright'] = 30;
|
||
|
}
|
||
|
else {
|
||
|
$graphDims['shiftXleft'] = 30;
|
||
|
$graphDims['shiftXright'] = 85;
|
||
|
}
|
||
|
|
||
|
return $graphDims;
|
||
|
}
|
||
|
|
||
|
// Select graph's type and height as well as which Y axes are used by graph items.
|
||
|
$dbGraphs = DBselect(
|
||
|
'SELECT MAX(g.graphtype) AS graphtype,MIN(gi.yaxisside) AS yaxissidel,MAX(gi.yaxisside) AS yaxissider,MAX(g.height) AS height'.
|
||
|
' FROM graphs g,graphs_items gi'.
|
||
|
' WHERE g.graphid='.zbx_dbstr($graphid).
|
||
|
' AND gi.graphid=g.graphid'
|
||
|
);
|
||
|
if ($graph = DBfetch($dbGraphs)) {
|
||
|
$yaxis = $graph['yaxissider'];
|
||
|
$yaxis = ($graph['yaxissidel'] == $yaxis) ? $yaxis : 2;
|
||
|
|
||
|
$graphDims['yaxis'] = $yaxis;
|
||
|
$graphDims['graphtype'] = $graph['graphtype'];
|
||
|
$graphDims['graphHeight'] = (int) $graph['height'];
|
||
|
}
|
||
|
|
||
|
if ($yaxis == 2) {
|
||
|
$graphDims['shiftXleft'] = 85;
|
||
|
$graphDims['shiftXright'] = 85;
|
||
|
}
|
||
|
elseif ($yaxis == 0) {
|
||
|
$graphDims['shiftXleft'] = 85;
|
||
|
$graphDims['shiftXright'] = 30;
|
||
|
}
|
||
|
else {
|
||
|
$graphDims['shiftXleft'] = 30;
|
||
|
$graphDims['shiftXright'] = 85;
|
||
|
}
|
||
|
|
||
|
$graphDims['graphHeight']++;
|
||
|
|
||
|
return $graphDims;
|
||
|
}
|
||
|
|
||
|
function getGraphByGraphId($graphId) {
|
||
|
$dbGraph = DBfetch(DBselect('SELECT g.* FROM graphs g WHERE g.graphid='.zbx_dbstr($graphId)));
|
||
|
|
||
|
if ($dbGraph) {
|
||
|
return $dbGraph;
|
||
|
}
|
||
|
|
||
|
error(_s('No graph item with graph ID "%1$s".', $graphId));
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get parent templates for each given graph.
|
||
|
*
|
||
|
* @param $array $graphs An array of graphs.
|
||
|
* @param string $graphs[]['graphid'] ID of a graph.
|
||
|
* @param string $graphs[]['templateid'] ID of parent template graph.
|
||
|
* @param int $flag Origin of the graph (ZBX_FLAG_DISCOVERY_NORMAL or
|
||
|
* ZBX_FLAG_DISCOVERY_PROTOTYPE).
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
function getGraphParentTemplates(array $graphs, $flag) {
|
||
|
$parent_graphids = [];
|
||
|
$data = [
|
||
|
'links' => [],
|
||
|
'templates' => []
|
||
|
];
|
||
|
|
||
|
foreach ($graphs as $graph) {
|
||
|
if ($graph['templateid'] != 0) {
|
||
|
$parent_graphids[$graph['templateid']] = true;
|
||
|
$data['links'][$graph['graphid']] = ['graphid' => $graph['templateid']];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!$parent_graphids) {
|
||
|
return $data;
|
||
|
}
|
||
|
|
||
|
$all_parent_graphids = [];
|
||
|
$hostids = [];
|
||
|
if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
|
||
|
$lld_ruleids = [];
|
||
|
}
|
||
|
|
||
|
do {
|
||
|
if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
|
||
|
$db_graphs = API::GraphPrototype()->get([
|
||
|
'output' => ['graphid', 'templateid'],
|
||
|
'selectHosts' => ['hostid'],
|
||
|
'selectDiscoveryRule' => ['itemid'],
|
||
|
'graphids' => array_keys($parent_graphids)
|
||
|
]);
|
||
|
}
|
||
|
// ZBX_FLAG_DISCOVERY_NORMAL
|
||
|
else {
|
||
|
$db_graphs = API::Graph()->get([
|
||
|
'output' => ['graphid', 'templateid'],
|
||
|
'selectHosts' => ['hostid'],
|
||
|
'graphids' => array_keys($parent_graphids)
|
||
|
]);
|
||
|
}
|
||
|
|
||
|
$all_parent_graphids += $parent_graphids;
|
||
|
$parent_graphids = [];
|
||
|
|
||
|
foreach ($db_graphs as $db_graph) {
|
||
|
$data['templates'][$db_graph['hosts'][0]['hostid']] = [];
|
||
|
$hostids[$db_graph['graphid']] = $db_graph['hosts'][0]['hostid'];
|
||
|
|
||
|
if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
|
||
|
$lld_ruleids[$db_graph['graphid']] = $db_graph['discoveryRule']['itemid'];
|
||
|
}
|
||
|
|
||
|
if ($db_graph['templateid'] != 0) {
|
||
|
if (!array_key_exists($db_graph['templateid'], $all_parent_graphids)) {
|
||
|
$parent_graphids[$db_graph['templateid']] = true;
|
||
|
}
|
||
|
|
||
|
$data['links'][$db_graph['graphid']] = ['graphid' => $db_graph['templateid']];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
while ($parent_graphids);
|
||
|
|
||
|
foreach ($data['links'] as &$parent_graph) {
|
||
|
$parent_graph['hostid'] = array_key_exists($parent_graph['graphid'], $hostids)
|
||
|
? $hostids[$parent_graph['graphid']]
|
||
|
: 0;
|
||
|
|
||
|
if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
|
||
|
$parent_graph['lld_ruleid'] = array_key_exists($parent_graph['graphid'], $lld_ruleids)
|
||
|
? $lld_ruleids[$parent_graph['graphid']]
|
||
|
: 0;
|
||
|
}
|
||
|
}
|
||
|
unset($parent_graph);
|
||
|
|
||
|
$db_templates = $data['templates']
|
||
|
? API::Template()->get([
|
||
|
'output' => ['name'],
|
||
|
'templateids' => array_keys($data['templates']),
|
||
|
'preservekeys' => true
|
||
|
])
|
||
|
: [];
|
||
|
|
||
|
$rw_templates = $db_templates
|
||
|
? API::Template()->get([
|
||
|
'output' => [],
|
||
|
'templateids' => array_keys($db_templates),
|
||
|
'editable' => true,
|
||
|
'preservekeys' => true
|
||
|
])
|
||
|
: [];
|
||
|
|
||
|
$data['templates'][0] = [];
|
||
|
|
||
|
foreach ($data['templates'] as $hostid => &$template) {
|
||
|
$template = array_key_exists($hostid, $db_templates)
|
||
|
? [
|
||
|
'hostid' => $hostid,
|
||
|
'name' => $db_templates[$hostid]['name'],
|
||
|
'permission' => array_key_exists($hostid, $rw_templates) ? PERM_READ_WRITE : PERM_READ
|
||
|
]
|
||
|
: [
|
||
|
'hostid' => $hostid,
|
||
|
'name' => _('Inaccessible template'),
|
||
|
'permission' => PERM_DENY
|
||
|
];
|
||
|
}
|
||
|
unset($template);
|
||
|
|
||
|
return $data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a template prefix for selected graph.
|
||
|
*
|
||
|
* @param string $graphid
|
||
|
* @param array $parent_templates The list of the templates, prepared by getGraphParentTemplates() function.
|
||
|
* @param int $flag Origin of the graph (ZBX_FLAG_DISCOVERY_NORMAL or ZBX_FLAG_DISCOVERY_PROTOTYPE).
|
||
|
* @param bool $provide_links If this parameter is false, prefix will not contain links.
|
||
|
*
|
||
|
* @return array|null
|
||
|
*/
|
||
|
function makeGraphTemplatePrefix($graphid, array $parent_templates, $flag, bool $provide_links) {
|
||
|
if (!array_key_exists($graphid, $parent_templates['links'])) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
while (array_key_exists($parent_templates['links'][$graphid]['graphid'], $parent_templates['links'])) {
|
||
|
$graphid = $parent_templates['links'][$graphid]['graphid'];
|
||
|
}
|
||
|
|
||
|
$template = $parent_templates['templates'][$parent_templates['links'][$graphid]['hostid']];
|
||
|
|
||
|
if ($provide_links && $template['permission'] == PERM_READ_WRITE) {
|
||
|
$url = (new CUrl('graphs.php'))->setArgument('context', 'template');
|
||
|
|
||
|
if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
|
||
|
$url->setArgument('parent_discoveryid', $parent_templates['links'][$graphid]['lld_ruleid']);
|
||
|
}
|
||
|
// ZBX_FLAG_DISCOVERY_NORMAL
|
||
|
else {
|
||
|
$url
|
||
|
->setArgument('filter_set', '1')
|
||
|
->setArgument('filter_hostids', [$template['hostid']]);
|
||
|
}
|
||
|
|
||
|
$name = (new CLink($template['name'], $url))->addClass(ZBX_STYLE_LINK_ALT);
|
||
|
}
|
||
|
else {
|
||
|
$name = new CSpan($template['name']);
|
||
|
}
|
||
|
|
||
|
return [$name->addClass(ZBX_STYLE_GREY), NAME_DELIMITER];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a list of graph templates.
|
||
|
*
|
||
|
* @param string $graphid
|
||
|
* @param array $parent_templates The list of the templates, prepared by getGraphParentTemplates() function.
|
||
|
* @param int $flag Origin of the item (ZBX_FLAG_DISCOVERY_NORMAL or ZBX_FLAG_DISCOVERY_PROTOTYPE).
|
||
|
* @param bool $provide_links If this parameter is false, prefix will not contain links.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
function makeGraphTemplatesHtml($graphid, array $parent_templates, $flag, bool $provide_links) {
|
||
|
$list = [];
|
||
|
|
||
|
while (array_key_exists($graphid, $parent_templates['links'])) {
|
||
|
$template = $parent_templates['templates'][$parent_templates['links'][$graphid]['hostid']];
|
||
|
|
||
|
if ($provide_links && $template['permission'] == PERM_READ_WRITE) {
|
||
|
$url = (new CUrl('graphs.php'))
|
||
|
->setArgument('form', 'update')
|
||
|
->setArgument('context', 'template');
|
||
|
|
||
|
if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
|
||
|
$url->setArgument('parent_discoveryid', $parent_templates['links'][$graphid]['lld_ruleid']);
|
||
|
}
|
||
|
|
||
|
$url->setArgument('graphid', $parent_templates['links'][$graphid]['graphid']);
|
||
|
|
||
|
if ($flag == ZBX_FLAG_DISCOVERY_NORMAL) {
|
||
|
$url->setArgument('hostid', $template['hostid']);
|
||
|
}
|
||
|
|
||
|
$name = new CLink($template['name'], $url);
|
||
|
}
|
||
|
else {
|
||
|
$name = (new CSpan($template['name']))->addClass(ZBX_STYLE_GREY);
|
||
|
}
|
||
|
|
||
|
array_unshift($list, $name, [NBSP(), RARR(), NBSP()]);
|
||
|
|
||
|
$graphid = $parent_templates['links'][$graphid]['graphid'];
|
||
|
}
|
||
|
|
||
|
if ($list) {
|
||
|
array_pop($list);
|
||
|
}
|
||
|
|
||
|
return $list;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Search items by same key in destination host.
|
||
|
*
|
||
|
* @param array $gitems
|
||
|
* @param string $destinationHostId
|
||
|
* @param bool $error if false error won't be thrown when item does not exist
|
||
|
* @param array $flags
|
||
|
*
|
||
|
* @return array|bool
|
||
|
*/
|
||
|
function getSameGraphItemsForHost($gitems, $destinationHostId, $error = true, array $flags = []) {
|
||
|
$result = [];
|
||
|
|
||
|
$flagsSql = $flags ? ' AND '.dbConditionInt('dest.flags', $flags) : '';
|
||
|
|
||
|
foreach ($gitems as $gitem) {
|
||
|
$dbItem = DBfetch(DBselect(
|
||
|
'SELECT dest.itemid,src.key_'.
|
||
|
' FROM items dest,items src'.
|
||
|
' WHERE dest.key_=src.key_'.
|
||
|
' AND dest.hostid='.zbx_dbstr($destinationHostId).
|
||
|
' AND src.itemid='.zbx_dbstr($gitem['itemid']).
|
||
|
$flagsSql
|
||
|
));
|
||
|
|
||
|
if ($dbItem) {
|
||
|
$gitem['itemid'] = $dbItem['itemid'];
|
||
|
$gitem['key_'] = $dbItem['key_'];
|
||
|
}
|
||
|
elseif ($error) {
|
||
|
$items = API::Item()->get([
|
||
|
'output' => ['key_'],
|
||
|
'itemids' => [$gitem['itemid']],
|
||
|
'webitems' => true
|
||
|
]);
|
||
|
|
||
|
$hosts = API::Host()->get([
|
||
|
'output' => ['host'],
|
||
|
'hostids' => [$destinationHostId],
|
||
|
'templated_hosts' => true
|
||
|
]);
|
||
|
|
||
|
error(_s('Missing key "%1$s" for host "%2$s".', $items[0]['key_'], $hosts[0]['host']));
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
else {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$result[] = $gitem;
|
||
|
}
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
function get_next_color($palettetype = 0) {
|
||
|
static $prev_color = ['dark' => true, 'color' => 0, 'grad' => 0];
|
||
|
|
||
|
switch ($palettetype) {
|
||
|
case 1:
|
||
|
$grad = [200, 150, 255, 100, 50, 0];
|
||
|
break;
|
||
|
case 2:
|
||
|
$grad = [100, 50, 200, 150, 250, 0];
|
||
|
break;
|
||
|
case 0:
|
||
|
default:
|
||
|
$grad = [255, 200, 150, 100, 50, 0];
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
$set_grad = $grad[$prev_color['grad']];
|
||
|
|
||
|
$r = $g = $b = (100 < $set_grad) ? 0 : 255;
|
||
|
|
||
|
switch ($prev_color['color']) {
|
||
|
case 0:
|
||
|
$g = $set_grad;
|
||
|
break;
|
||
|
case 1:
|
||
|
$r = $set_grad;
|
||
|
break;
|
||
|
case 2:
|
||
|
$b = $set_grad;
|
||
|
break;
|
||
|
case 3:
|
||
|
$r = $b = $set_grad;
|
||
|
break;
|
||
|
case 4:
|
||
|
$g = $b = $set_grad;
|
||
|
break;
|
||
|
case 5:
|
||
|
$r = $g = $set_grad;
|
||
|
break;
|
||
|
case 6:
|
||
|
$r = $g = $b = $set_grad;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
$prev_color['dark'] = !$prev_color['dark'];
|
||
|
if ($prev_color['color'] == 6) {
|
||
|
$prev_color['grad'] = ($prev_color['grad'] + 1) % 6;
|
||
|
}
|
||
|
$prev_color['color'] = ($prev_color['color'] + 1) % 7;
|
||
|
|
||
|
return [$r, $g, $b];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Draws a text on an image. Supports TrueType fonts.
|
||
|
*
|
||
|
* @param resource $image
|
||
|
* @param int $fontsize
|
||
|
* @param int $angle
|
||
|
* @param int|float $x
|
||
|
* @param int|float $y
|
||
|
* @param int $color a numeric color identifier from imagecolorallocate() or imagecolorallocatealpha()
|
||
|
* @param string $string
|
||
|
*/
|
||
|
function imageText($image, $fontsize, $angle, $x, $y, $color, $string) {
|
||
|
$x = (int) $x;
|
||
|
$y = (int) $y;
|
||
|
$string = strtr($string, ['&' => '&']);
|
||
|
|
||
|
if ((preg_match(ZBX_PREG_DEF_FONT_STRING, $string) && $angle != 0) || ZBX_FONT_NAME == ZBX_GRAPH_FONT_NAME) {
|
||
|
$ttf = ZBX_FONTPATH.'/'.ZBX_FONT_NAME.'.ttf';
|
||
|
imagettftext($image, $fontsize, $angle, $x, $y, $color, $ttf, $string);
|
||
|
}
|
||
|
elseif ($angle == 0) {
|
||
|
$ttf = ZBX_FONTPATH.'/'.ZBX_GRAPH_FONT_NAME.'.ttf';
|
||
|
imagettftext($image, $fontsize, $angle, $x, $y, $color, $ttf, $string);
|
||
|
}
|
||
|
else {
|
||
|
$ttf = ZBX_FONTPATH.'/'.ZBX_GRAPH_FONT_NAME.'.ttf';
|
||
|
$size = imageTextSize($fontsize, 0, $string);
|
||
|
|
||
|
$imgg = imagecreatetruecolor($size['width'] + 1, $size['height']);
|
||
|
$transparentColor = imagecolorallocatealpha($imgg, 200, 200, 200, 127);
|
||
|
imagefill($imgg, 0, 0, $transparentColor);
|
||
|
imagettftext($imgg, $fontsize, 0, 0, $size['height'], $color, $ttf, $string);
|
||
|
|
||
|
$imgg = imagerotate($imgg, $angle, $transparentColor);
|
||
|
imagealphablending($imgg, false);
|
||
|
imagesavealpha($imgg, true);
|
||
|
imagecopy($image, $imgg, $x - $size['height'], $y - $size['width'], 0, 0, $size['height'], $size['width'] + 1);
|
||
|
imagedestroy($imgg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Calculates the size of the given string.
|
||
|
*
|
||
|
* Returns the following data:
|
||
|
* - height - height of the text;
|
||
|
* - width - width of the text;
|
||
|
* - baseline - baseline Y coordinate (can only be used for horizontal text, can be negative).
|
||
|
*
|
||
|
* @param int $fontsize
|
||
|
* @param int $angle
|
||
|
* @param string $string
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
function imageTextSize($fontsize, $angle, $string) {
|
||
|
$string = strtr($string, ['&' => '&']);
|
||
|
|
||
|
if (preg_match(ZBX_PREG_DEF_FONT_STRING, $string) && $angle != 0) {
|
||
|
$ttf = ZBX_FONTPATH.'/'.ZBX_FONT_NAME.'.ttf';
|
||
|
}
|
||
|
else {
|
||
|
$ttf = ZBX_FONTPATH.'/'.ZBX_GRAPH_FONT_NAME.'.ttf';
|
||
|
}
|
||
|
|
||
|
$ar = imagettfbbox($fontsize, $angle, $ttf, $string);
|
||
|
|
||
|
return [
|
||
|
'height' => abs($ar[1] - $ar[5]),
|
||
|
'width' => abs($ar[0] - $ar[4]),
|
||
|
'baseline' => $ar[1]
|
||
|
];
|
||
|
}
|
||
|
|
||
|
function dashedLine($image, $x1, $y1, $x2, $y2, $color) {
|
||
|
// style for dashed lines
|
||
|
if (!is_array($color)) {
|
||
|
$style = [$color, $color, IMG_COLOR_TRANSPARENT, IMG_COLOR_TRANSPARENT];
|
||
|
}
|
||
|
else {
|
||
|
$style = $color;
|
||
|
}
|
||
|
|
||
|
imagesetstyle($image, $style);
|
||
|
zbx_imageline($image, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED);
|
||
|
}
|
||
|
|
||
|
function find_period_start($periods, $time) {
|
||
|
$date = getdate($time);
|
||
|
$wday = $date['wday'] == 0 ? 7 : $date['wday'];
|
||
|
$curr = $date['hours'] * 100 + $date['minutes'];
|
||
|
|
||
|
if (isset($periods[$wday])) {
|
||
|
$next_h = -1;
|
||
|
$next_m = -1;
|
||
|
foreach ($periods[$wday] as $period) {
|
||
|
$per_start = $period['start_h'] * 100 + $period['start_m'];
|
||
|
if ($per_start > $curr) {
|
||
|
if (($next_h == -1 && $next_m == -1) || ($per_start < ($next_h * 100 + $next_m))) {
|
||
|
$next_h = $period['start_h'];
|
||
|
$next_m = $period['start_m'];
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$per_end = $period['end_h'] * 100 + $period['end_m'];
|
||
|
if ($per_end <= $curr) {
|
||
|
continue;
|
||
|
}
|
||
|
return $time;
|
||
|
}
|
||
|
|
||
|
if ($next_h >= 0 && $next_m >= 0) {
|
||
|
return mktime($next_h, $next_m, 0, $date['mon'], $date['mday'], $date['year']);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for ($days = 1; $days < 7 ; ++$days) {
|
||
|
$new_wday = ($wday + $days - 1) % 7 + 1;
|
||
|
if (isset($periods[$new_wday ])) {
|
||
|
$next_h = -1;
|
||
|
$next_m = -1;
|
||
|
|
||
|
foreach ($periods[$new_wday] as $period) {
|
||
|
$per_start = $period['start_h'] * 100 + $period['start_m'];
|
||
|
|
||
|
if (($next_h == -1 && $next_m == -1) || ($per_start < ($next_h * 100 + $next_m))) {
|
||
|
$next_h = $period['start_h'];
|
||
|
$next_m = $period['start_m'];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($next_h >= 0 && $next_m >= 0) {
|
||
|
return mktime($next_h, $next_m, 0, $date['mon'], $date['mday'] + $days, $date['year']);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
function find_period_end($periods, $time, $max_time) {
|
||
|
$date = getdate($time);
|
||
|
$wday = $date['wday'] == 0 ? 7 : $date['wday'];
|
||
|
$curr = $date['hours'] * 100 + $date['minutes'];
|
||
|
|
||
|
if (isset($periods[$wday])) {
|
||
|
$next_h = -1;
|
||
|
$next_m = -1;
|
||
|
|
||
|
foreach ($periods[$wday] as $period) {
|
||
|
$per_start = $period['start_h'] * 100 + $period['start_m'];
|
||
|
$per_end = $period['end_h'] * 100 + $period['end_m'];
|
||
|
if ($per_start > $curr) {
|
||
|
continue;
|
||
|
}
|
||
|
if ($per_end < $curr) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (($next_h == -1 && $next_m == -1) || ($per_end > ($next_h * 100 + $next_m))) {
|
||
|
$next_h = $period['end_h'];
|
||
|
$next_m = $period['end_m'];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($next_h >= 0 && $next_m >= 0) {
|
||
|
$new_time = mktime($next_h, $next_m, 0, $date['mon'], $date['mday'], $date['year']);
|
||
|
|
||
|
if ($new_time == $time) {
|
||
|
return $time;
|
||
|
}
|
||
|
if ($new_time > $max_time) {
|
||
|
return $max_time;
|
||
|
}
|
||
|
|
||
|
$next_time = find_period_end($periods, $new_time, $max_time);
|
||
|
if ($next_time < 0) {
|
||
|
return $new_time;
|
||
|
}
|
||
|
else {
|
||
|
return $next_time;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Yield suitable graph scale intervals.
|
||
|
*
|
||
|
* @param float $min Minimum extreme of the scale.
|
||
|
* @param float $max Maximum extreme of the scale.
|
||
|
* @param bool $is_binary Is the scale binary (use 1024 base for units)?
|
||
|
* @param int $power Scale power.
|
||
|
* @param int $rows Number of scale rows.
|
||
|
*
|
||
|
* @return iterable
|
||
|
*/
|
||
|
function yieldGraphScaleInterval(float $min, float $max, bool $is_binary, int $power, int $rows): iterable {
|
||
|
$unit_base = $is_binary ? ZBX_KIBIBYTE : 1000;
|
||
|
|
||
|
$divisor = pow($unit_base, $power);
|
||
|
|
||
|
// Expression optimized to avoid overflow.
|
||
|
$interval = truncateFloat($max / $divisor / $rows - $min / $divisor / $rows);
|
||
|
|
||
|
while (true) {
|
||
|
if ($is_binary && $interval >= 1) {
|
||
|
$result = pow(2, ceil(log($interval, 2)));
|
||
|
}
|
||
|
else {
|
||
|
$exponent = floor(log10($interval));
|
||
|
|
||
|
foreach ([2, 5, 10] as $multiply) {
|
||
|
$candidate = truncateFloat(pow(10, $exponent) * $multiply);
|
||
|
if ($candidate >= $interval) {
|
||
|
$result = $candidate;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
yield $result * pow($unit_base, $power);
|
||
|
|
||
|
$interval = $result * 1.5;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Calculate graph scale extremes.
|
||
|
*
|
||
|
* @param float $data_min Minimum extreme of the graph.
|
||
|
* @param float $data_max Maximum extreme of the graph.
|
||
|
* @param bool $is_binary Is the scale binary (use 1024 base for units)?
|
||
|
* @param bool $calc_power Should scale power be calculated?
|
||
|
* @param bool $calc_min Should scale minimum be calculated?
|
||
|
* @param bool $calc_max Should scale maximum be calculated?
|
||
|
* @param int $rows_min Minimum number of scale rows.
|
||
|
* @param int $rows_max Maximum number of scale rows.
|
||
|
*
|
||
|
* @return array|null
|
||
|
*/
|
||
|
function calculateGraphScaleExtremes(float $data_min, float $data_max, bool $is_binary, bool $calc_power,
|
||
|
bool $calc_min, bool $calc_max, int $rows_min, int $rows_max): ?array {
|
||
|
$scale_min = truncateFloat($data_min);
|
||
|
$scale_max = truncateFloat($data_max);
|
||
|
|
||
|
if ($scale_min >= $scale_max) {
|
||
|
if ($scale_max > 0) {
|
||
|
if ($calc_min) {
|
||
|
$scale_min = 0;
|
||
|
}
|
||
|
elseif ($calc_max) {
|
||
|
$scale_max = $scale_min < 0 ? 0 : ($scale_min == 0 ? 1 : $scale_min * 1.25);
|
||
|
}
|
||
|
else {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if ($calc_max) {
|
||
|
$scale_max = $scale_min < 0 ? 0 : ($scale_min == 0 ? 1 : $scale_min * 1.25);
|
||
|
}
|
||
|
elseif ($calc_min) {
|
||
|
$scale_min = $scale_max == 0 ? -1 : $scale_max * 1.25;
|
||
|
}
|
||
|
else {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$power = $calc_power
|
||
|
? (int) min(8, max(0, floor(log(max(abs($scale_min), abs($scale_max)), $is_binary ? ZBX_KIBIBYTE : 1000))))
|
||
|
: 0;
|
||
|
|
||
|
$best_result_value = null;
|
||
|
$best_result = [
|
||
|
'min' => $scale_min,
|
||
|
'max' => $scale_max,
|
||
|
// Expression optimized to avoid overflow.
|
||
|
'interval' => $scale_max / 2 - $scale_min / 2,
|
||
|
'rows' => 2,
|
||
|
'power' => $power
|
||
|
];
|
||
|
|
||
|
for ($rows = $rows_min; $rows <= $rows_max; $rows++) {
|
||
|
$clearance_min = min(0.5, $rows * 0.05);
|
||
|
$clearance_max = min(1, $rows * 0.1);
|
||
|
|
||
|
foreach (yieldGraphScaleInterval($scale_min, $scale_max, $is_binary, $power, $rows) as $interval) {
|
||
|
if ($interval == INF) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ($calc_min) {
|
||
|
$min = floor($scale_min / $interval) * $interval;
|
||
|
if ($min != 0 && ($scale_min - $min) / $interval < $clearance_min) {
|
||
|
$min -= $interval;
|
||
|
}
|
||
|
$max = $calc_max ? $min + $interval * $rows : $scale_max;
|
||
|
}
|
||
|
elseif ($calc_max) {
|
||
|
$min = $scale_min;
|
||
|
$max = ceil($scale_min / $interval + $rows) * $interval;
|
||
|
if ($max != 0 && ($max - $scale_max) / $interval < $clearance_max) {
|
||
|
$max += $interval;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
$min = $scale_min;
|
||
|
$max = $scale_max;
|
||
|
}
|
||
|
|
||
|
$min = truncateFloat($min);
|
||
|
$max = truncateFloat($max);
|
||
|
|
||
|
if (is_infinite($min) || is_infinite($max)) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ($min > $scale_min || $max < $scale_max) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ($calc_min && $min != 0 && ($scale_min - $min) / $interval < $clearance_min) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ($calc_max && $max != 0 && ($max - $scale_max) / $interval < $clearance_max) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$result = [
|
||
|
'min' => $min,
|
||
|
'max' => $max,
|
||
|
'interval' => $interval,
|
||
|
'rows' => $rows,
|
||
|
'power' => $power
|
||
|
];
|
||
|
|
||
|
// Expression optimized to avoid overflow.
|
||
|
$result_value = ($scale_min - $min) / $interval + ($max - $scale_max) / $interval;
|
||
|
|
||
|
if ($best_result_value === null || $result_value < $best_result_value) {
|
||
|
$best_result_value = $result_value;
|
||
|
$best_result = $result;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $best_result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Calculate graph scale intermediate values.
|
||
|
*
|
||
|
* @param float $min Minimum extreme of the scale.
|
||
|
* @param float $max Maximum extreme of the scale.
|
||
|
* @param bool $min_calculated Is minimum extreme of the scale calculated?
|
||
|
* @param bool $max_calculated Is maximum extreme of the scale calculated?
|
||
|
* @param float $interval Scale interval.
|
||
|
* @param string $units Scale units.
|
||
|
* @param bool $is_binary Is the scale binary (use 1024 base for units)?
|
||
|
* @param int $power Scale power.
|
||
|
* @param int $precision_max Maximum precision to use for the scale.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
function calculateGraphScaleValues(float $min, float $max, bool $min_calculated, bool $max_calculated, float $interval,
|
||
|
string $units, bool $is_binary, int $power, int $precision_max): array {
|
||
|
$unit_base = $is_binary ? ZBX_KIBIBYTE : 1000;
|
||
|
|
||
|
$units_length = ($units !== '' && $units !== '!')
|
||
|
? ($power > 0 ? 1 : 0) + mb_strlen($units) + ($units[0] !== '!' ? 1 : 0)
|
||
|
: ($power > 0 ? 1 : 0);
|
||
|
|
||
|
$precision = max(3, $units_length == 0 ? $precision_max : ($precision_max - $units_length - ($min < 0 ? 1 : 0)));
|
||
|
|
||
|
$decimals = min(ZBX_UNITS_ROUNDOFF_SUFFIXED, $precision - 1);
|
||
|
$decimals_exact = false;
|
||
|
|
||
|
$power_interval = $interval / pow($unit_base, $power);
|
||
|
|
||
|
if ($power_interval < 1) {
|
||
|
$decimals = getNumDecimals($power_interval);
|
||
|
$decimals_exact = true;
|
||
|
|
||
|
if ($decimals > $precision - 1) {
|
||
|
$decimals = $precision - 1;
|
||
|
$decimals_exact = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$rows = [];
|
||
|
|
||
|
$clearance = 0.5;
|
||
|
for ($row_index = 0;; $row_index++) {
|
||
|
$value = ceil($min / $interval + $row_index + $clearance) * $interval;
|
||
|
|
||
|
if ($value > $max - $interval * $clearance) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
$rows[] = $value;
|
||
|
}
|
||
|
|
||
|
$ignore_milliseconds = ($min <= -1 || $max >= 1);
|
||
|
|
||
|
$options = [
|
||
|
'units' => $units,
|
||
|
'unit_base' => $unit_base,
|
||
|
'convert' => ITEM_CONVERT_NO_UNITS,
|
||
|
'power' => $power,
|
||
|
'ignore_milliseconds' => $ignore_milliseconds
|
||
|
];
|
||
|
$options_fixed = $options + [
|
||
|
'precision' => $precision,
|
||
|
'decimals' => $precision - 1,
|
||
|
'decimals_exact' => false
|
||
|
];
|
||
|
$options_calculated = $options + [
|
||
|
'precision' => $precision,
|
||
|
'decimals' => $decimals,
|
||
|
'decimals_exact' => $decimals_exact
|
||
|
];
|
||
|
|
||
|
$scale_values = [];
|
||
|
|
||
|
$scale_values[] = [
|
||
|
'relative_pos' => 0,
|
||
|
'value' => convertUnits([
|
||
|
'value' => $min
|
||
|
] + ($min_calculated ? $options_calculated : $options_fixed))
|
||
|
];
|
||
|
|
||
|
foreach ($rows as $value) {
|
||
|
$scale_values[] = [
|
||
|
'relative_pos' => (abs($max - $min) == INF)
|
||
|
? ($value / 10 - $min / 10) / ($max / 10 - $min / 10)
|
||
|
: ($value - $min) / ($max - $min),
|
||
|
'value' => convertUnits([
|
||
|
'value' => $value
|
||
|
] + $options_calculated)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
$scale_values[] = [
|
||
|
'relative_pos' => 1,
|
||
|
'value' => convertUnits([
|
||
|
'value' => $max
|
||
|
] + ($max_calculated ? $options_calculated : $options_fixed))
|
||
|
];
|
||
|
|
||
|
return $scale_values;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $short_item Comma separated <short_field_name>:<value> pairs.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
function expandShortGraphItem($short_item) {
|
||
|
$map = [
|
||
|
'gi' => 'gitemid',
|
||
|
'it' => 'itemid',
|
||
|
'so' => 'sortorder',
|
||
|
'fl' => 'flags',
|
||
|
'ty' => 'type',
|
||
|
'dr' => 'drawtype',
|
||
|
'ya' => 'yaxisside',
|
||
|
'ca' => 'calc_fnc',
|
||
|
'co' => 'color'
|
||
|
];
|
||
|
|
||
|
$item = [];
|
||
|
|
||
|
foreach (explode(',', $short_item) as $short_field) {
|
||
|
list($short_name, $value) = explode(':', $short_field);
|
||
|
$item[$map[$short_name]] = $value;
|
||
|
}
|
||
|
|
||
|
return $item;
|
||
|
}
|