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.
zabbix/ui/tests/integration/testGoAgentDataCollection.php

658 lines
18 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.
**/
require_once dirname(__FILE__).'/../include/CIntegrationTest.php';
/**
* Test suite for agent2 (GO agent) metric collection.
*
* @backup history
*/
class testGoAgentDataCollection extends CIntegrationTest {
const COMPARE_AVERAGE = 0;
const COMPARE_LAST = 1;
const OFFSET_MAX = 20;
private static $hostids = [];
private static $itemids = [];
// List of items to check.
private static $items = [
[
'key' => 'agent.ping',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'kernel.maxfiles',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'kernel.maxproc',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'net.dns[,zabbix.com]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'net.dns.record[,zabbix.com]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'net.if.discovery',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'net.tcp.listen[80]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'net.tcp.port[,80]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'net.tcp.service[http]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'net.udp.listen[21]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'net.udp.service[ntp]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'proc.num[zabbix_server]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'system.cpu.discovery',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'system.cpu.num',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'system.hw.cpu[all,model]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'system.hw.devices',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'system.hw.macaddr',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'system.sw.arch',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'system.sw.os',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'system.sw.packages',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'system.uname',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'system.users.num',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'vfs.dir.count[/mnt]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'vfs.dir.size[/mnt]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'vfs.file.contents[/etc/hosts]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'vfs.file.exists[/etc/hosts]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'vfs.file.md5sum[/etc/hosts]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'vfs.file.size[/etc/hosts]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'vfs.file.time[/etc/hosts]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'vfs.file.regexp[/etc/hosts,localhost]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'vfs.file.cksum[/etc/hosts]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'vfs.fs.discovery',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'web.page.regexp[localhost/invalid_link_returns_404,,,404,2]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'system.run[uname]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'log['.PHPUNIT_COMPONENT_DIR.'zabbix_server.log]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_LOG
],
[
'key' => 'log.count['.PHPUNIT_COMPONENT_DIR.'zabbix_server.log, server ]',
'type' => ITEM_TYPE_ZABBIX_ACTIVE,
'valueType' => ITEM_VALUE_TYPE_TEXT
],
[
'key' => 'system.cpu.util[,,avg1]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
'threshold' => 0.9
],
[
'key' => 'system.cpu.load[,avg1]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
'threshold' => 0.9
],
[
'key' => 'vfs.dev.read[,operations]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_UINT64,
'threshold' => 1000
],
[
'key' => 'vfs.dev.write[,operations]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_UINT64,
'threshold' => 10000
],
[
'key' => 'proc.cpu.util[,,,,avg1]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
'threshold' => 90.0,
'compareType' => self::COMPARE_AVERAGE
],
[
'key' => 'system.swap.in[,pages]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_UINT64,
'threshold' => 100,
'compareType' => self::COMPARE_AVERAGE
],
[
'key' => 'system.swap.out[,pages]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_UINT64,
'threshold' => 10000,
'compareType' => self::COMPARE_AVERAGE
],
[
'key' => 'proc.mem[zabbix_server,zabbix,avg]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
'threshold' => 10000.0
],
[
'key' => 'web.page.perf[http://localhost]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
'threshold' => 100.0,
'compareType' => self::COMPARE_AVERAGE
],
[
'key' => 'net.tcp.service.perf[ssh]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
'threshold' => 5.00
],
[
'key' => 'net.udp.service.perf[ntp]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
'threshold' => 5.00
],
[
'key' => 'system.swap.size[,total]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_UINT64,
'threshold' => 10000,
'compareType' => self::COMPARE_AVERAGE
],
[
'key' => 'vfs.fs.inode[/,pfree]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_FLOAT,
'threshold' => 0.9,
'compareType' => self::COMPARE_AVERAGE
],
[
'key' => 'vfs.fs.size[/tmp,free]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_UINT64,
'threshold' => 100000000,
'compareType' => self::COMPARE_AVERAGE
],
[
'key' => 'vm.memory.size[free]',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_UINT64,
'threshold' => 100000000,
'compareType' => self::COMPARE_AVERAGE
],
[// Should be treated as a special case, since this metric returns JSON object.
// Maybe, it should e pulled to separate test suite. At this point we just compare it as string.
'key' => 'zabbix.stats[127.0.0.1,'.PHPUNIT_PORT_PREFIX.self::SERVER_PORT_SUFFIX.']',
'type' => ITEM_TYPE_ZABBIX,
'valueType' => ITEM_VALUE_TYPE_TEXT,
'threshold' => 500,
// Removing initial block ->
// {"response":"success","data":{"boottime":1683011633,"uptime":113,<- ,
// since uptime is flaky (e.g. values "113" and "114" on agent 1 and 2 can appear)
// Assuming the uptime takes 3 digits. Update threshold if uptime number of digits changes.
'threshold_before' => 65
]
];
/**
* @inheritdoc
*/
public function prepareData() {
// Create host "agentd" and "agent2".
$hosts = [];
foreach ([self::COMPONENT_AGENT => self::AGENT_PORT_SUFFIX, self::COMPONENT_AGENT2 =>
self::AGENT2_PORT_SUFFIX] as $component => $port) {
$hosts[] = [
'host' => $component,
'interfaces' => [
[
'type' => 1,
'main' => 1,
'useip' => 1,
'ip' => '127.0.0.1',
'dns' => '',
'port' => PHPUNIT_PORT_PREFIX.$port
]
],
'groups' => [
[
'groupid' => 4
]
],
'status' => HOST_STATUS_NOT_MONITORED
];
}
$response = $this->call('host.create', $hosts);
$this->assertArrayHasKey('hostids', $response['result']);
foreach ([self::COMPONENT_AGENT, self::COMPONENT_AGENT2] as $i => $name) {
$this->assertArrayHasKey($i, $response['result']['hostids']);
self::$hostids[$name] = $response['result']['hostids'][$i];
}
// Get host interface ids.
$response = $this->call('host.get', [
'output' => ['host'],
'hostids' => array_values(self::$hostids),
'selectInterfaces' => ['interfaceid']
]);
$interfaceids = [];
foreach ($response['result'] as $host) {
$interfaceids[$host['host']] = $host['interfaces'][0]['interfaceid'];
}
// Create items.
$items = [];
foreach (self::$items as $item) {
$data = [
'name' => $item['key'],
'key_' => $item['key'],
'type' => $item['type'],
'value_type' => $item['valueType'],
'delay' => '1s'
];
foreach ([self::COMPONENT_AGENT, self::COMPONENT_AGENT2] as $component) {
$host_if_props = [
'hostid' => self::$hostids[$component]
];
if ($data['type'] == ITEM_TYPE_ZABBIX_ACTIVE) {
$host_if_props['interfaceid'] = 0;
} else {
$host_if_props['interfaceid'] = $interfaceids[$component];
}
$items[] = array_merge($data, $host_if_props);
}
}
$response = $this->call('item.create', $items);
$this->assertArrayHasKey('itemids', $response['result']);
$this->assertEquals(count($items), count($response['result']['itemids']));
// Get item IDs
$itemids = $response['result']['itemids'];
foreach (self::$items as $i => $value) {
$name = $value['key'];
self::$itemids[self::COMPONENT_AGENT.':'.$name] = $itemids[$i * 2];
self::$itemids[self::COMPONENT_AGENT2.':'.$name] = $itemids[($i * 2) + 1];
}
return true;
}
/**
* Component configuration provider for agent related tests.
*
* @return array
*/
public function agentConfigurationProvider() {
return [
self::COMPONENT_SERVER => [
'UnreachablePeriod' => 25,
'UnavailableDelay' => 15,
'UnreachableDelay' => 5
],
self::COMPONENT_AGENT => [
'Hostname' => self::COMPONENT_AGENT,
'ServerActive' => '127.0.0.1:'.self::getConfigurationValue(self::COMPONENT_SERVER, 'ListenPort'),
'AllowKey' => 'system.run[*]'
],
self::COMPONENT_AGENT2 => [
'Hostname' => self::COMPONENT_AGENT2,
'ServerActive' => '127.0.0.1:'.self::getConfigurationValue(self::COMPONENT_SERVER, 'ListenPort'),
'ListenPort' => PHPUNIT_PORT_PREFIX.self::AGENT2_PORT_SUFFIX,
'AllowKey' => 'system.run[*]',
'Plugins.Uptime.Capacity' => '10'
]
];
}
/**
* Test if both active and passive go agent checks are processed.
*
* @required-components server, agent, agent2
* @configurationDataProvider agentConfigurationProvider
* @hosts agentd, agent2
*/
public function testGoAgentDataCollection_checkDataCollection() {
foreach ([self::COMPONENT_AGENT, self::COMPONENT_AGENT2] as $component) {
$this->waitForLogLineToBePresent(self::COMPONENT_SERVER,
'enabling Zabbix agent checks on host "'.$component.'": interface became available', false
);
}
// Delay to ensure that all metrics were collected.
sleep(110);
}
/**
* Item data provider.
*
* @return array
*/
public function getItems() {
$items = [];
foreach (self::$items as $item) {
$items[] = [$item];
}
return $items;
}
/**
* Get values of all items and store them in static variable.
*
* @return array
*/
public function getItemData() {
static $data = null;
if ($data === null) {
$itemids = [];
foreach (self::$items as $item) {
$itemids[$item['valueType']][] = self::$itemids[self::COMPONENT_AGENT.':'.$item['key']];
$itemids[$item['valueType']][] = self::$itemids[self::COMPONENT_AGENT2.':'.$item['key']];
}
$values = [];
foreach ($itemids as $type => $ids) {
$result = $this->call('history.get', [
'output' => ['itemid', 'value', 'clock', 'ns'],
'itemids' => $ids,
'history' => $type
]);
$this->sort($result['result'], ['itemid', 'clock', 'ns']);
foreach ($result['result'] as $item) {
$values[$item['itemid']][] = $item['value'];
}
}
$data = [];
foreach (self::$items as $item) {
$data[$item['key']] = [];
foreach ([self::COMPONENT_AGENT, self::COMPONENT_AGENT2] as $component) {
$itemid = self::$itemids[$component.':'.$item['key']];
if (array_key_exists($itemid, $values)) {
$data[$item['key']][$component] = $values[$itemid];
}
}
}
}
return $data;
}
/**
* Test if both active and passive go agent checks are processed.
*
* @depends testGoAgentDataCollection_checkDataCollection
* @dataProvider getItems
*/
public function testGoAgentDataCollection_checkData($item) {
$data = $this->getItemData();
if (!array_key_exists($item['key'], $data)) {
$this->fail('No metrics for item "'.$item['key'].'"');
}
$values = $data[$item['key']];
foreach ([self::COMPONENT_AGENT, self::COMPONENT_AGENT2] as $component) {
if (!array_key_exists($component, $values)) {
$this->fail('No metrics for item "'.$component.':'.$item['key'].'"');
}
}
switch ($item['valueType']) {
case ITEM_VALUE_TYPE_LOG:
$count = min([count($values[self::COMPONENT_AGENT]), count($values[self::COMPONENT_AGENT2])]);
$values_a = array_slice($values[self::COMPONENT_AGENT], 0, $count);
$values_b = array_slice($values[self::COMPONENT_AGENT2], 0, $count);
$this->assertSame($values_a, $values_b, 'Strings do not match for '.$item['key']);
break;
case ITEM_VALUE_TYPE_TEXT:
$a = end($values[self::COMPONENT_AGENT]);
$b = end($values[self::COMPONENT_AGENT2]);
if (array_key_exists('threshold', $item) && $item['threshold'] !== 0) {
$a = substr($a, 0, $item['threshold']);
$b = substr($b, 0, $item['threshold']);
}
if (array_key_exists('threshold_before', $item) && $item['threshold_before'] !== 0) {
$a = substr($a, $item['threshold_before']);
$b = substr($b, $item['threshold_before']);
}
$this->assertEquals($a, $b, 'Strings do not match for '.$item['key']);
break;
case ITEM_VALUE_TYPE_FLOAT:
case ITEM_VALUE_TYPE_UINT64:
$diff_values = [];
if (CTestArrayHelper::get($item, 'compareType', self::COMPARE_LAST) === self::COMPARE_AVERAGE) {
$value = [];
foreach ([self::COMPONENT_AGENT, self::COMPONENT_AGENT2] as $component) {
// Calculate offset between Agent and Agent2 result arrays
for ($i = 0; $i < self::OFFSET_MAX; $i++) {
$value[$component][$i] = 0;
if (self::COMPONENT_AGENT == $component) {
$j = $i;
} else {
$j = 0;
}
$slice = array_slice($values[$component], $j);
$records = count($slice);
if ($records > 0) {
$value[$component][$i] = array_sum($slice) / $records;
}
}
}
for ($i = 0; $i < self::OFFSET_MAX; $i++) {
$a = $value[self::COMPONENT_AGENT][$i];
$b = $value[self::COMPONENT_AGENT2][$i];
$diff_values[$i] = abs($a - $b);
}
$offset = array_search(min($diff_values), $diff_values);
$a = $value[self::COMPONENT_AGENT][$offset];
$b = $value[self::COMPONENT_AGENT2][$offset];
$diff = abs($a - $b);
}
else {
$records = count($values[self::COMPONENT_AGENT]);
for ($i = 0; $i < self::OFFSET_MAX; $i++) {
$slice = array_slice($values[self::COMPONENT_AGENT], 0, $records - $i);
$a = end($slice);
$b = end($values[self::COMPONENT_AGENT2]);
$diff_values[$i] = abs($a - $b);
}
$diff = min($diff_values);
}
$this->assertTrue($diff < $item['threshold'], 'Difference for '.$item['key'].
' is more than defined threshold '.$diff.' > '.$item['threshold']
);
break;
}
}
/**
* Sort array by multiple fields.
*
* @static
*
* @param array $array array to sort passed by reference
* @param array $fields fields to sort, can be either string with field name or array with 'field' and 'order' keys
*/
public static function sort(array &$array, array $fields) {
foreach ($fields as $fid => $field) {
if (!is_array($field)) {
$fields[$fid] = ['field' => $field, 'order' => ZBX_SORT_UP];
}
}
uasort($array, function($a, $b) use ($fields) {
foreach ($fields as $field) {
$cmp = strnatcasecmp($a[$field['field']], $b[$field['field']]);
if ($cmp != 0) {
return $cmp * ($field['order'] == ZBX_SORT_UP ? 1 : -1);
}
}
return 0;
});
}
}