<?php /* ** Zabbix ** Copyright (C) 2001-2022 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'; define('COMPARE_AVERAGE', 0); define('COMPARE_LAST', 1); /** * Test suite for agent2 (GO agent) metric collection. * * @backup history */ class testGoAgentDataCollection extends CIntegrationTest { 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.5 ], [ 'key' => 'system.cpu.load[,avg1]', 'type' => ITEM_TYPE_ZABBIX, 'valueType' => ITEM_VALUE_TYPE_FLOAT, 'threshold' => 0.5 ], [ 'key' => 'vfs.dev.read[,operations]', 'type' => ITEM_TYPE_ZABBIX, 'valueType' => ITEM_VALUE_TYPE_UINT64, 'threshold' => 10 ], [ 'key' => 'vfs.dev.write[,operations]', 'type' => ITEM_TYPE_ZABBIX, 'valueType' => ITEM_VALUE_TYPE_UINT64, 'threshold' => 500 ], [ 'key' => 'proc.cpu.util[,,,,avg1]', 'type' => ITEM_TYPE_ZABBIX, 'valueType' => ITEM_VALUE_TYPE_FLOAT, 'threshold' => 10.0, 'compareType' => COMPARE_AVERAGE ], [ 'key' => 'system.swap.in[,pages]', 'type' => ITEM_TYPE_ZABBIX, 'valueType' => ITEM_VALUE_TYPE_UINT64, 'threshold' => 100 ], [ 'key' => 'system.swap.out[,pages]', 'type' => ITEM_TYPE_ZABBIX, 'valueType' => ITEM_VALUE_TYPE_UINT64, 'threshold' => 100 ], [ 'key' => 'proc.mem[zabbix_server,zabbix,avg]', 'type' => ITEM_TYPE_ZABBIX, 'valueType' => ITEM_VALUE_TYPE_FLOAT, 'threshold' => 100.0 ], [ 'key' => 'web.page.perf[http://localhost]', 'type' => ITEM_TYPE_ZABBIX, 'valueType' => ITEM_VALUE_TYPE_FLOAT, 'threshold' => 1.0, 'compareType' => COMPARE_AVERAGE ], [ 'key' => 'net.tcp.service.perf[ssh]', 'type' => ITEM_TYPE_ZABBIX, 'valueType' => ITEM_VALUE_TYPE_FLOAT, 'threshold' => 0.05 ], [ 'key' => 'net.udp.service.perf[ntp]', 'type' => ITEM_TYPE_ZABBIX, 'valueType' => ITEM_VALUE_TYPE_FLOAT, 'threshold' => 0.05 ], [ 'key' => 'system.swap.size[,total]', 'type' => ITEM_TYPE_ZABBIX, 'valueType' => ITEM_VALUE_TYPE_UINT64, 'threshold' => 100 ], [ 'key' => 'vfs.fs.inode[/,pfree]', 'type' => ITEM_TYPE_ZABBIX, 'valueType' => ITEM_VALUE_TYPE_FLOAT, 'threshold' => 0.1 ], [ 'key' => 'vfs.fs.size[/tmp,free]', 'type' => ITEM_TYPE_ZABBIX, 'valueType' => ITEM_VALUE_TYPE_UINT64, 'threshold' => 262144 ], [ 'key' => 'vm.memory.size[free]', 'type' => ITEM_TYPE_ZABBIX, 'valueType' => ITEM_VALUE_TYPE_UINT64, 'threshold' => 10000000 ], [// 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' => 50 ] ]; /** * @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) { $items[] = array_merge($data, [ 'hostid' => self::$hostids[$component], 'interfaceid' => $interfaceids[$component] ]); } } $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.'": host became available', false ); } // Delay to ensure that all metrics were collected. sleep(90); } /** * 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']); } $this->assertEquals($a, $b, 'Strings do not match for '.$item['key']); break; case ITEM_VALUE_TYPE_FLOAT: case ITEM_VALUE_TYPE_UINT64: if (CTestArrayHelper::get($item, 'compareType', COMPARE_LAST) === COMPARE_AVERAGE) { $value = []; foreach ([self::COMPONENT_AGENT, self::COMPONENT_AGENT2] as $component) { $value[$component] = 0; $records = count($values[$component]); if ($records > 0) { $value[$component] = array_sum($values[$component]) / $records; } } $a = $value[self::COMPONENT_AGENT]; $b = $value[self::COMPONENT_AGENT2]; } else { $a = end($values[self::COMPONENT_AGENT]); $b = end($values[self::COMPONENT_AGENT2]); } $diff = abs(abs($a) - abs($b)); $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; }); } }