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.

1017 lines
28 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 'vendor/autoload.php';
require_once dirname(__FILE__).'/CAPITest.php';
require_once dirname(__FILE__).'/CZabbixClient.php';
require_once dirname(__FILE__).'/helpers/CLogHelper.php';
/**
* Base class for integration tests.
*/
class CIntegrationTest extends CAPITest {
// Default iteration count for wait operations.
const WAIT_ITERATIONS = 60;
// Default delays (in seconds):
const WAIT_ITERATION_DELAY = 1; // Wait iteration delay.
const WAIT_ITERATION_DELAY_FOR_SHUTDOWN = 3; // Shutdown may legitimately take a lot of time
const CACHE_RELOAD_DELAY = 5; // Configuration cache reload delay.
const HOUSEKEEPER_EXEC_DELAY = 5; // Housekeeper execution delay.
const DATA_PROCESSING_DELAY = 5; // Data processing delay.
// Zabbix component constants.
const COMPONENT_SERVER = 'server';
const COMPONENT_SERVER_HANODE1 = 'server_ha1';
const COMPONENT_PROXY = 'proxy';
const COMPONENT_AGENT = 'agentd';
const COMPONENT_AGENT2 = 'agent2';
// Zabbix component port constants.
const AGENT_PORT_SUFFIX = '50';
const SERVER_PORT_SUFFIX = '51';
const PROXY_PORT_SUFFIX = '52';
const SERVER_HANODE1_PORT_SUFFIX = '61';
const AGENT2_PORT_SUFFIX = '53';
/**
* Components required by test suite.
*
* @var array
*/
private static $suite_components = [];
/**
* Hosts to be enabled for test suite.
*
* @var array
*/
private static $suite_hosts = [];
/**
* Configuration provider for test suite.
*
* @var array
*/
private static $suite_configuration = [];
/**
* Components required by test case.
*
* @var array
*/
private $case_components = [];
/**
* Hosts to be enabled for test case.
*
* @var array
*/
private $case_hosts = [];
/**
* Configuration provider for test case.
*
* @var array
*/
protected static $case_configuration = [];
/**
* Process annotations defined on suite / case level.
*
* @param string $type annotation type ('class' or 'method')
*
* @throws Exception on invalid configuration provider
*/
protected function processAnnotations($type) {
$annotations = $this->getAnnotationsByType($this->annotations, $type);
$result = [
'components' => [],
'hosts' => [],
'configuration' => []
];
// Get required components.
foreach ($this->getAnnotationTokensByName($annotations, 'required-components') as $component) {
if ($component === 'agent') {
$component = self::COMPONENT_AGENT;
}
self::validateComponent($component);
$result['components'][$component] = true;
}
$result['components'] = array_keys($result['components']);
// Get hosts to enable.
foreach ($this->getAnnotationTokensByName($annotations, 'hosts') as $host) {
$result['hosts'][$host] = true;
}
$result['hosts'] = array_keys($result['hosts']);
// Get configuration from configuration data provider.
foreach ($this->getAnnotationTokensByName($annotations, 'configurationDataProvider') as $provider) {
if (!method_exists($this, $provider) || !is_array($config = call_user_func([$this, $provider]))) {
throw new Exception('Configuration data provider "'.$provider.'" is not valid.');
}
$result['configuration'] = array_merge($result['configuration'], $config);
}
return $result;
}
/**
* Set status for hosts.
*
* @param array $hosts array of hostids or host names
* @param integer $status status to be set
*/
protected static function setHostStatus($hosts, $status) {
if (is_scalar($hosts)) {
$hosts = [$hosts];
}
if ($hosts && is_array($hosts)) {
$filters = [];
$criteria = [];
foreach ($hosts as $host) {
$filters[(is_numeric($host) ? 'hostid' : 'host')][] = zbx_dbstr($host);
}
foreach ($filters as $key => $values) {
$criteria[] = $key.' in ('.implode(',', $values).')';
}
DBexecute('UPDATE hosts SET status='.zbx_dbstr($status).' WHERE '.implode(' OR ', $criteria));
}
}
/**
* @inheritdoc
*/
protected function onBeforeTestSuite() {
parent::onBeforeTestSuite();
$result = $this->processAnnotations('class');
self::$suite_components = $result['components'];
self::$suite_hosts = $result['hosts'];
self::$suite_configuration = self::getDefaultComponentConfiguration();
foreach (self::getComponents() as $component) {
if (!array_key_exists($component, $result['configuration'])) {
continue;
}
self::$suite_configuration[$component]
= array_merge(self::$suite_configuration[$component], $result['configuration'][$component]);
}
try {
if ($this->prepareData() === false) {
throw new Exception('Failed to prepare data for test suite.');
}
} catch (Exception $exception) {
self::markTestSuiteSkipped();
throw $exception;
}
self::setHostStatus(self::$suite_hosts, HOST_STATUS_MONITORED);
}
/**
* Prepare data for test suite.
*/
public function prepareData() {
// Code is not missing here.
return true;
}
/**
* Callback executed before every test case.
*
* @before
*/
public function onBeforeTestCase() {
parent::onBeforeTestCase();
$result = $this->processAnnotations('method');
$this->case_components = array_diff($result['components'], self::$suite_components);
$this->case_hosts = array_diff($result['hosts'], self::$suite_hosts);
self::$case_configuration = self::$suite_configuration;
foreach (self::getComponents() as $component) {
if (!array_key_exists($component, $result['configuration'])) {
continue;
}
self::$case_configuration[$component] = array_merge(self::$case_configuration[$component],
$result['configuration'][$component]
);
}
self::setHostStatus($this->case_hosts, HOST_STATUS_MONITORED);
foreach ($this->case_components as $component) {
if (in_array($component, self::$suite_components)) {
throw new Exception('Component "'.$component.'" already started on suite level.');
}
}
$components = array_merge(self::$suite_components, $this->case_components);
foreach ($components as $component) {
self::prepareComponentConfiguration($component, self::$case_configuration);
self::startComponent($component);
}
}
/**
* Callback executed after every test case.
*
* @after
*/
public function onAfterTestCase() {
$components = array_merge(self::$suite_components, $this->case_components);
foreach ($components as $component) {
self::stopComponent($component);
}
self::setHostStatus($this->case_hosts, HOST_STATUS_NOT_MONITORED);
parent::onAfterTestCase();
self::$case_configuration = [];
}
/**
* Callback executed after every test suite.
*
* @afterClass
*/
public static function onAfterTestSuite() {
foreach (self::$suite_components as $component) {
self::stopComponent($component);
}
if (self::$suite_hosts) {
global $DB;
DBconnect($error);
self::setHostStatus(self::$suite_hosts, HOST_STATUS_NOT_MONITORED);
DBclose();
}
parent::onAfterTestSuite();
}
/**
* Get list of possible component names.
*
* @return array
*/
private static function getComponents() {
return [
self::COMPONENT_SERVER, self::COMPONENT_PROXY, self::COMPONENT_AGENT, self::COMPONENT_AGENT2,
self::COMPONENT_SERVER_HANODE1
];
}
/**
* Validate component name.
*
* @param string $component component name to be validated.
*
* @throws Exception on invalid component name
*/
private static function validateComponent($component) {
if (!in_array($component, self::getComponents())) {
throw new Exception('Unknown component name "'.$component.'".');
}
}
/**
* Wait for component to start.
*
* @param string $component component name
* @param string $waitLogLineOverride already log line to use to consider component as running
* @param bool $skip_pid skip PID check
*
* @throws Exception on failed wait operation
*/
protected static function waitForStartup($component, $waitLogLineOverride = '', $skip_pid = false) {
self::validateComponent($component);
for ($r = 0; $r < self::WAIT_ITERATIONS; $r++) {
$pid = @file_get_contents(self::getPidPath($component));
if ($skip_pid == true || ($pid && is_numeric($pid) && posix_kill($pid, 0))) {
switch ($component) {
case self::COMPONENT_SERVER_HANODE1:
self::waitForLogLineToBePresent($component, 'HA manager started', false, 5, 1);
break;
case self::COMPONENT_SERVER:
case self::COMPONENT_PROXY:
$line = empty($waitLogLineOverride) ? 'started [trapper #1]' : $waitLogLineOverride;
self::waitForLogLineToBePresent($component, $line, false, 10, 1);
break;
case self::COMPONENT_AGENT:
self::waitForLogLineToBePresent($component, 'started [listener #1]', false, 5, 1);
break;
case self::COMPONENT_AGENT2:
self::waitForLogLineToBePresent($component, 'Zabbix Agent2 hostname', false, 5, 1);
break;
}
return;
}
sleep(self::WAIT_ITERATION_DELAY);
}
var_dump(file_get_contents(self::getLogPath(self::COMPONENT_SERVER)));
throw new Exception('Failed to wait for component "'.$component.'" to start.');
}
/**
* Checks absence of pid file after kill.
*
* @param string $component component name
*
*/
private static function checkPidKilled($component) {
for ($r = 0; $r < self::WAIT_ITERATIONS; $r++) {
if (!file_exists(self::getPidPath($component))) {
return true;
}
sleep(self::WAIT_ITERATION_DELAY_FOR_SHUTDOWN);
}
return false;
}
/**
* Wait for component to stop.
*
* @param string $component
* @param array $child_pids
*
* @throws Exception on failed wait operation
*/
protected static function waitForShutdown($component, array $child_pids) {
if (!self::checkPidKilled($component)) {
throw new Exception('Failed to wait for component "'.$component.'" to stop.');
}
$failed_pids = [];
foreach ($child_pids as $child_pid) {
if (ctype_digit($child_pid) && posix_kill($child_pid, 0)) {
posix_kill($child_pid, SIGKILL);
$failed_pids[] = $child_pid;
}
}
if (!$failed_pids) {
return;
}
$log = CLogHelper::readLog(self::getLogPath($component), false);
throw new Exception('Multiple child processes for component "'.$component.'" did not stop gracefully:'."\n".
implode(', ', $failed_pids)."\n".
'Log file contents: '."\n".$log."\n");
}
/**
* Execute command and the execution result.
*
* @param string $command command to be executed
* @param array $params parameters to be passed
* @param bool $background run command in background
*
* @return string
*
* @throws Exception on execution error
*/
protected static function executeCommand(string $command, array $params, bool $background = false) {
if ($params) {
foreach ($params as &$param) {
$param = escapeshellarg($param);
}
unset($param);
$params = ' '.implode(' ', $params);
}
else {
$params = '';
}
$command .= $params.($background ? ' > /dev/null 2>&1 &' : ' 2>&1');
exec($command, $output, $return);
if ($return !== 0) {
$output = $output ? "\n".'Output:'."\n".implode("\n", $output) : '';
throw new Exception('Failed to execute command "'.$command.'".'.$output);
}
return $output;
}
/**
* Get default configuration of components.
*
* @return array
*/
protected static function getDefaultComponentConfiguration() {
global $DB;
$db = [
'DBName' => $DB['DATABASE'],
'DBUser' => $DB['USER'],
'DBPassword' => $DB['PASSWORD']
];
if ($DB['SERVER'] !== 'localhost' && $DB['SERVER'] !== '127.0.0.1') {
$db['DBHost'] = $DB['SERVER'];
}
if ($DB['PORT'] != 0) {
$db['DBPort'] = $DB['PORT'];
}
if ($DB['SCHEMA']) {
$db['DBSchema'] = $DB['SCHEMA'];
}
$configuration = [
self::COMPONENT_SERVER => array_merge($db, [
'LogFile' => PHPUNIT_COMPONENT_DIR.'zabbix_server.log',
'PidFile' => PHPUNIT_COMPONENT_DIR.'zabbix_server.pid',
'SocketDir' => PHPUNIT_COMPONENT_DIR,
'ListenPort' => PHPUNIT_PORT_PREFIX.self::SERVER_PORT_SUFFIX
]),
self::COMPONENT_SERVER_HANODE1 => array_merge($db, [
'LogFile' => PHPUNIT_COMPONENT_DIR.'zabbix_server_ha1.log',
'PidFile' => PHPUNIT_COMPONENT_DIR.'zabbix_server_ha1.pid',
'SocketDir' => PHPUNIT_COMPONENT_DIR.'ha1/',
'ListenPort' => PHPUNIT_PORT_PREFIX.self::SERVER_HANODE1_PORT_SUFFIX
]),
self::COMPONENT_PROXY => array_merge($db, [
'LogFile' => PHPUNIT_COMPONENT_DIR.'zabbix_proxy.log',
'PidFile' => PHPUNIT_COMPONENT_DIR.'zabbix_proxy.pid',
'SocketDir' => PHPUNIT_COMPONENT_DIR,
'ListenPort' => PHPUNIT_PORT_PREFIX.self::PROXY_PORT_SUFFIX
]),
self::COMPONENT_AGENT => [
'LogFile' => PHPUNIT_COMPONENT_DIR.'zabbix_agent.log',
'PidFile' => PHPUNIT_COMPONENT_DIR.'zabbix_agent.pid',
'ListenPort' => PHPUNIT_PORT_PREFIX.self::AGENT_PORT_SUFFIX
],
self::COMPONENT_AGENT2 => [
'LogFile' => PHPUNIT_COMPONENT_DIR.'zabbix_agent2.log',
'PidFile' => PHPUNIT_COMPONENT_DIR.'zabbix_agent2.pid',
'ControlSocket' => PHPUNIT_COMPONENT_DIR.'zabbix_agent2.sock',
'ListenPort' => PHPUNIT_PORT_PREFIX.self::AGENT2_PORT_SUFFIX
]
];
$configuration[self::COMPONENT_PROXY]['DBName'] .= '_proxy';
return $configuration;
}
/**
* Create configuration file for component.
*
* @param string $component component name
* @param array $values configuration array
*
* @throws Exception on failed configuration file write
*/
protected static function prepareComponentConfiguration($component, $values) {
self::validateComponent($component);
if ($component == self::COMPONENT_SERVER_HANODE1) {
$path = PHPUNIT_CONFIG_SOURCE_DIR.'zabbix_'.self::COMPONENT_SERVER.'.conf';
}
else {
$path = PHPUNIT_CONFIG_SOURCE_DIR.'zabbix_'.$component.'.conf';
}
if (!file_exists($path) || ($config = @file_get_contents($path)) === false) {
throw new Exception('There is no configuration file for component "'.$component.'": '.$path.'.');
}
if (array_key_exists($component, $values) && $values[$component] && is_array($values[$component])) {
foreach ($values[$component] as $key => $value) {
$config = preg_replace('/^(\s*'.$key.'\s*=.*)$/m', '#\1', $config);
foreach ((array) $value as $val) {
$config .= "\n".$key.'='.$val;
}
}
}
if (file_put_contents(PHPUNIT_CONFIG_DIR.'zabbix_'.$component.'.conf', $config) === false) {
throw new Exception('Failed to create configuration file for component "'.$component.'": '.
PHPUNIT_CONFIG_DIR.'zabbix_'.$component.'.conf.'
);
}
}
/**
* Start component.
*
* @param string $component component name
* @param string $waitLogLineOverride already log line to use to consider component as running
* @param bool $skip_pid skip PID check
*
* @throws Exception on missing configuration or failed start
*/
protected function startComponent($component, $waitLogLineOverride = '', $skip_pid = false) {
self::validateComponent($component);
$config = PHPUNIT_CONFIG_DIR.'zabbix_'.$component.'.conf';
if (!file_exists($config)) {
throw new Exception('There is no configuration file for component "'.$component.'".');
}
self::clearLog($component);
$background = ($component === self::COMPONENT_AGENT2);
$bin_path = $component === self::COMPONENT_SERVER_HANODE1
? PHPUNIT_BINARY_DIR.'zabbix_'.self::COMPONENT_SERVER
: PHPUNIT_BINARY_DIR.'zabbix_'.$component;
self::executeCommand($bin_path, ['-c', $config], $background);
self::waitForStartup($component, $waitLogLineOverride, $skip_pid );
}
/**
* Stop component.
*
* @param string $component component name
*
* @throws Exception on missing configuration or failed stop
*/
protected static function stopComponent($component) {
self::validateComponent($component);
$child_pids = [];
$pid = @file_get_contents(self::getPidPath($component));
if ($pid !== false && is_numeric($pid)) {
$output = shell_exec('pgrep -P '.$pid);
if ($output !== false && $output !== null) {
$child_pids = explode("\n", $output);
}
posix_kill($pid, SIGTERM);
}
self::waitForShutdown($component, $child_pids);
}
/**
* Stop component by using SIGKILL signal.
*
* @param string $component component name
*
* @throws Exception on missing configuration or failed stop
*/
protected static function killComponent($component) {
self::validateComponent($component);
$child_pids = [];
$pid_path = self::getPidPath($component);
$pid = @file_get_contents($pid_path);
if ($pid !== false && is_numeric($pid)) {
$output = shell_exec('pgrep -P '.$pid);
if ($output !== false && $output !== null) {
$child_pids = explode("\n", $output);
foreach ($child_pids as $child_pid) {
if (ctype_digit($child_pid) && posix_kill($child_pid, 0)) {
posix_kill($child_pid, SIGKILL);
}
}
}
posix_kill($pid, SIGKILL);
}
unlink($pid_path);
}
/**
* Get client for component.
*
* @param string $component component name
*
* @throws Exception on invalid component type
*/
protected function getClient($component) {
self::validateComponent($component);
if ($component === self::COMPONENT_AGENT || $component === self::COMPONENT_AGENT2) {
throw new Exception('There is no client available for Zabbix Agent.');
}
return new CZabbixClient('localhost', self::getConfigurationValue($component, 'ListenPort', 10051), 3, 3,
ZBX_SOCKET_BYTES_LIMIT
);
}
/**
* Get name of active component used in test.
*
* @return string
*/
protected function getActiveComponent() {
$components = [];
foreach (array_merge(self::$suite_components, $this->case_components) as $component) {
if ($component !== self::COMPONENT_AGENT && $component !== self::COMPONENT_AGENT2) {
$components[] = $component;
}
}
if (count($components) === 1) {
return $components[0];
}
else {
return self::COMPONENT_SERVER;
}
}
/**
* Send value for items to server.
*
* @param string $type data type
* @param array $values item values
* @param string $component component name or null for active component
*
* @return array processing result
*/
protected function sendDataValues($type, $values, $component = null) {
if ($component === null) {
$component = $this->getActiveComponent();
}
$client = $this->getClient($component);
$result = $client->sendDataValues($type, $values);
// Check that data was sent successfully.
$this->assertTrue(($result !== false),
sprintf('Component "%s" failed to receive data: %s', $component, $client->getError())
);
// Check that discovery data was sent.
$this->assertTrue(array_key_exists('processed', $result), 'Result doesn\'t contain "processed" count.');
$this->assertEquals(count($values), $result['processed'],
'Processed value count doesn\'t match sent value count.'
);
sleep(self::DATA_PROCESSING_DELAY);
return $result;
}
/**
* Send single item value.
*
* @param string $type data type
* @param string $host host name
* @param string $key item key
* @param mixed $value item value
* @param string $component component name or null for active component
*
* @return array processing result
*/
protected function sendDataValue($type, $host, $key, $value, $component = null) {
if (!is_scalar($value)) {
$value = json_encode($value);
}
$data = [
'host' => $host,
'key' => $key,
'value' => $value
];
return $this->sendDataValues($type, [$data], $component);
}
/**
* Send values to trapper items.
*
* @param array $values item values
* @param string $component component name or null for active component
*
* @return array processing result
*/
protected function sendSenderValues($values, $component = null) {
return $this->sendDataValues('sender', $values, $component);
}
/**
* Send single value for trapper item.
*
* @param string $host host name
* @param string $key item key
* @param mixed $value item value
* @param string $component component name or null for active component
*
* @return array processing result
*/
protected function sendSenderValue($host, $key, $value, $component = null) {
return $this->sendDataValue('sender', $host, $key, $value, $component);
}
/**
* Send values to active agent items.
*
* @param array $values item values
* @param string $component component name or null for active component
*
* @return array processing result
*/
protected function sendAgentValues($values, $component = null) {
return $this->sendDataValues('agent', $values, $component);
}
/**
* Send single value for active agent item.
*
* @param string $host host name
* @param string $key item key
* @param mixed $value item value
* @param string $component component name or null for active component
*
* @return array processing result
*/
protected function sendAgentValue($host, $key, $value, $component = null) {
return $this->sendDataValue('agent', $host, $key, $value, $component);
}
/**
* Get list of active checks for host.
*
* @param string $host host name
* @param string $component component name or null for active component
*
* @return array
*/
protected function getActiveAgentChecks($host, $component = null) {
if ($component === null) {
$component = $this->getActiveComponent();
}
$client = $this->getClient($component);
$checks = $client->getActiveChecks($host);
if (!is_array($checks)) {
$this->fail('Cannot retrieve active checks for host "'.$host.'": '.$client->getError().'.');
}
return $checks;
}
/**
* Reload configuration cache.
*
* @param string $component component name or null for active component
*/
protected function reloadConfigurationCache($component = null) {
if ($component === null) {
$component = $this->getActiveComponent();
}
self::executeCommand(PHPUNIT_BINARY_DIR.'zabbix_'.$component, ['--runtime-control', 'config_cache_reload']);
sleep(self::CACHE_RELOAD_DELAY);
}
/**
* @param string $component component name or null for active component
*/
protected function executeHousekeeper($component = null) {
if ($component === null) {
$component = $this->getActiveComponent();
}
self::executeCommand(PHPUNIT_BINARY_DIR.'zabbix_'.$component, ['--runtime-control', 'housekeeper_execute']);
sleep(self::HOUSEKEEPER_EXEC_DELAY);
}
/**
* Request data from API until data is present (@see call).
*
* @param string $method API method to be called
* @param mixed $params API call params
* @param integer $iterations iteration count
* @param integer $delay iteration delay
* @param callable $callback Callback function to test if API response is valid.
*
* @return array
*/
public function callUntilDataIsPresent($method, $params, $iterations = null, $delay = null, $callback = null) {
if ($iterations === null) {
$iterations = self::WAIT_ITERATIONS;
}
if ($delay === null) {
$delay = self::WAIT_ITERATION_DELAY;
}
$exception = null;
for ($i = 0; $i < $iterations; $i++) {
try {
$response = $this->call($method, $params);
if (is_array($response['result']) && count($response['result']) > 0
&& ($callback === null || call_user_func($callback, $response))) {
return $response;
}
} catch (Exception $e) {
$exception = $e;
}
sleep($delay);
}
if ($exception !== null) {
throw $exception;
}
$this->fail('Data requested from '.$method.' API is not present within specified interval. Params used:'.
"\n".json_encode($params)
);
}
/**
* Get path of the log file for component.
*
* @param string $component name of the component
*
* @return string
*/
protected static function getLogPath($component) {
self::validateComponent($component);
return self::getConfigurationValue($component, 'LogFile', '/tmp/zabbix_'.$component.'.log');
}
/**
* Get path of the pid file for component.
*
* @param string $component name of the component
*
* @return string
*/
protected static function getPidPath($component) {
self::validateComponent($component);
return self::getConfigurationValue($component, 'PidFile', '/tmp/zabbix_'.$component.'.pid');
}
/**
* Get current configuration value.
*
* @param string $component name of the component
* @param string $key name of the configuration parameter
* @param mixed $default default value
*
* @return mixed
*/
protected static function getConfigurationValue($component, $key, $default = null) {
$configuration = (self::$case_configuration) ? self::$case_configuration : self::$suite_configuration;
if (array_key_exists($component, $configuration) && array_key_exists($key, $configuration[$component])) {
return $configuration[$component][$key];
}
return $default;
}
/**
* Clear contents of log.
*
* @param string $component name of the component
*/
protected static function clearLog($component) {
CLogHelper::clearLog(self::getLogPath($component));
}
/**
* Check if line is present.
*
* @param string $component name of the component
* @param string|array $lines line(s) to look for
* @param boolean $incremental flag to be used to enable incremental read
* @param boolean $match_regex flag to be used to match line by regex
*
* @return boolean
*/
protected static function isLogLinePresent($component, $lines, $incremental = true, $match_regex = false) {
return CLogHelper::isLogLinePresent(self::getLogPath($component), $lines, $incremental, $match_regex);
}
/**
* Wait until line is present in log.
*
* @param string $component name of the component
* @param string|array $lines line(s) to look for
* @param boolean $incremental flag to be used to enable incremental read
* @param integer $iterations iteration count
* @param integer $delay iteration delay
* @param boolean $match_regex flag to be used to match line by regex
*
* @throws Exception on failed wait operation
*/
protected static function waitForLogLineToBePresent($component, $lines, $incremental = true, $iterations = null, $delay = null, $match_regex = false) {
if ($iterations === null) {
$iterations = self::WAIT_ITERATIONS;
}
if ($delay === null) {
$delay = self::WAIT_ITERATION_DELAY;
}
for ($r = 0; $r < $iterations; $r++) {
if (self::isLogLinePresent($component, $lines, $incremental, $match_regex)) {
return true;
}
sleep($delay);
}
if (is_array($lines)) {
$quoted = [];
foreach ($lines as $line) {
$quoted[] = '"'.$line.'"';
}
$description = 'any of the lines ['.implode(', ', $quoted).']';
}
else {
$description = 'line "'.$lines.'"';
}
$c = CLogHelper::readLog(self::getLogPath($component), false);
if (file_exists(self::getLogPath(self::COMPONENT_AGENT))) {
$c2 = @CLogHelper::readLog(self::getLogPath(self::COMPONENT_AGENT), false);
}
else {
$c2 = '';
}
throw new Exception('Failed to wait for '.$description.' to be present in '.$component .
'log file path:'.self::getLogPath($component).' and server log file contents: ' .
$c . "\n and agent log file contents: " . $c2);
}
/**
* Check if line is present.
*
* @param string $component name of the component
* @param string|array $cmd command
*
* @throws Exception on execution error
*/
protected function executeRuntimeControlCommand($component, $cmd) {
if (!is_array($cmd)) {
$cmd = [$cmd];
}
$params = ['-c', PHPUNIT_CONFIG_DIR.'zabbix_'.$component.'.conf', '--runtime-control'];
$args = array_merge($params, $cmd);
self::executeCommand(PHPUNIT_BINARY_DIR.'zabbix_'.$component, $args, '> /dev/null 2>&1');
}
}