'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; }); } }