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.
499 lines
17 KiB
499 lines
17 KiB
<?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.
|
|
**/
|
|
|
|
|
|
/**
|
|
* Controller to perform preprocessing test or 'get item value from host' test or both.
|
|
*/
|
|
class CControllerPopupItemTestSend extends CControllerPopupItemTest {
|
|
|
|
/**
|
|
* Show final result in item test dialog.
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected $show_final_result;
|
|
|
|
/**
|
|
* Use previous value for preprocessing test.
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected $use_prev_value;
|
|
|
|
/**
|
|
* Retrieve value from host.
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected $get_value_from_host;
|
|
|
|
/**
|
|
* Time suffixes supported by Zabbix server.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected static $supported_time_suffixes = ['w', 'd', 'h', 'm', 's'];
|
|
|
|
protected function checkInput() {
|
|
$fields = [
|
|
'authtype' => 'in '.implode(',', [ZBX_HTTP_AUTH_NONE, ZBX_HTTP_AUTH_BASIC, ZBX_HTTP_AUTH_NTLM, ZBX_HTTP_AUTH_KERBEROS, ZBX_HTTP_AUTH_DIGEST, ITEM_AUTHTYPE_PASSWORD, ITEM_AUTHTYPE_PUBLICKEY]),
|
|
'get_value' => 'in 0,1',
|
|
'eol' => 'in '.implode(',', [ZBX_EOL_LF, ZBX_EOL_CRLF]),
|
|
'headers' => 'array',
|
|
'proxyid' => 'id',
|
|
'hostid' => 'db hosts.hostid',
|
|
'http_authtype' => 'in '.implode(',', [ZBX_HTTP_AUTH_NONE, ZBX_HTTP_AUTH_BASIC, ZBX_HTTP_AUTH_NTLM, ZBX_HTTP_AUTH_KERBEROS, ZBX_HTTP_AUTH_DIGEST, ITEM_AUTHTYPE_PASSWORD, ITEM_AUTHTYPE_PUBLICKEY]),
|
|
'http_password' => 'string',
|
|
'http_proxy' => 'string',
|
|
'http_username' => 'string',
|
|
'flags' => 'in '. implode(',', [ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE, ZBX_FLAG_DISCOVERY_PROTOTYPE, ZBX_FLAG_DISCOVERY_CREATED]),
|
|
'follow_redirects' => 'in 0,1',
|
|
'key' => 'string',
|
|
'interface' => 'array',
|
|
'ipmi_sensor' => 'string',
|
|
'item_type' => 'in '.implode(',', [ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE, ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE, ITEM_TYPE_HTTPTEST, ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH, ITEM_TYPE_TELNET, ITEM_TYPE_CALCULATED, ITEM_TYPE_JMX, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT]),
|
|
'jmx_endpoint' => 'string',
|
|
'macros' => 'array',
|
|
'output_format' => 'in '.implode(',', [HTTPCHECK_STORE_RAW, HTTPCHECK_STORE_JSON]),
|
|
'params_ap' => 'string',
|
|
'params_es' => 'string',
|
|
'params_f' => 'string',
|
|
'script' => 'string',
|
|
'password' => 'string',
|
|
'post_type' => 'in '.implode(',', [ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML]),
|
|
'posts' => 'string',
|
|
'prev_time' => 'string',
|
|
'prev_value' => 'string',
|
|
'privatekey' => 'string',
|
|
'publickey' => 'string',
|
|
'query_fields' => 'array',
|
|
'parameters' => 'array',
|
|
'request_method' => 'in '.implode(',', [HTTPCHECK_REQUEST_GET, HTTPCHECK_REQUEST_POST, HTTPCHECK_REQUEST_PUT, HTTPCHECK_REQUEST_HEAD]),
|
|
'retrieve_mode' => 'in '.implode(',', [HTTPTEST_STEP_RETRIEVE_MODE_CONTENT, HTTPTEST_STEP_RETRIEVE_MODE_HEADERS, HTTPTEST_STEP_RETRIEVE_MODE_BOTH]),
|
|
'show_final_result' => 'in 0,1',
|
|
'snmp_oid' => 'string',
|
|
'steps' => 'array',
|
|
'ssl_cert_file' => 'string',
|
|
'ssl_key_file' => 'string',
|
|
'ssl_key_password' => 'string',
|
|
'status_codes' => 'string',
|
|
'test_type' => 'required|in '.implode(',', [self::ZBX_TEST_TYPE_ITEM, self::ZBX_TEST_TYPE_ITEM_PROTOTYPE, self::ZBX_TEST_TYPE_LLD]),
|
|
'time_change' => 'int32',
|
|
'timeout' => 'string',
|
|
'username' => 'string',
|
|
'url' => 'string',
|
|
'value' => 'string',
|
|
'value_type' => 'in '.implode(',', [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT, ITEM_VALUE_TYPE_BINARY]),
|
|
'valuemapid' => 'id',
|
|
'verify_host' => 'in 0,1',
|
|
'verify_peer' => 'in 0,1',
|
|
'not_supported' => 'in 1'
|
|
];
|
|
|
|
$ret = $this->validateInput($fields);
|
|
|
|
if ($ret) {
|
|
$testable_item_types = self::getTestableItemTypes($this->getInput('hostid', '0'));
|
|
$this->get_value_from_host = (bool) $this->getInput('get_value');
|
|
$this->item_type = $this->hasInput('item_type') ? $this->getInput('item_type') : -1;
|
|
$this->test_type = $this->getInput('test_type');
|
|
$this->is_item_testable = in_array($this->item_type, $testable_item_types);
|
|
|
|
$interface = $this->getInput('interface', []);
|
|
$steps = $this->getInput('steps', []);
|
|
$prepr_types = zbx_objectValues($steps, 'type');
|
|
$this->use_prev_value = (count(array_intersect($prepr_types, self::$preproc_steps_using_prev_value)) > 0);
|
|
$this->show_final_result = ($this->getInput('show_final_result') == 1);
|
|
|
|
// If 'get value from host' is checked, check if key is valid for item types it's mandatory.
|
|
if ($this->get_value_from_host && in_array($this->item_type, $this->item_types_has_key_mandatory)) {
|
|
$key = $this->getInput('key', '');
|
|
|
|
/*
|
|
* VMware and icmpping simple checks are not supported.
|
|
* This normally cannot be achieved from UI so no need for error message.
|
|
*/
|
|
if ($this->item_type == ITEM_TYPE_SIMPLE
|
|
&& (substr($key, 0, 7) === 'vmware.' || substr($key, 0, 8) === 'icmpping')) {
|
|
$this->get_value_from_host = false;
|
|
$ret = false;
|
|
}
|
|
else {
|
|
$item_key_parser = new CItemKey();
|
|
|
|
if ($item_key_parser->parse($key) != CParser::PARSE_SUCCESS) {
|
|
error(_s('Incorrect value for field "%1$s": %2$s.', 'key_', $item_key_parser->getError()));
|
|
$ret = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test if item is testable and check interface properties.
|
|
if ($this->get_value_from_host && !$this->is_item_testable) {
|
|
error(_s('Test of "%1$s" items is not supported.', item_type2str($this->item_type)));
|
|
$ret = false;
|
|
}
|
|
elseif ($this->get_value_from_host && array_key_exists($this->item_type, $this->items_require_interface)) {
|
|
if (!$this->validateInterface($interface)) {
|
|
$ret = false;
|
|
}
|
|
}
|
|
|
|
// Check preprocessing steps.
|
|
if ($steps) {
|
|
switch ($this->test_type) {
|
|
case self::ZBX_TEST_TYPE_ITEM:
|
|
$api_input_rules = CItem::getPreprocessingValidationRules();
|
|
break;
|
|
|
|
case self::ZBX_TEST_TYPE_ITEM_PROTOTYPE:
|
|
$api_input_rules = CItemPrototype::getPreprocessingValidationRules();
|
|
break;
|
|
|
|
case self::ZBX_TEST_TYPE_LLD:
|
|
$api_input_rules = CDiscoveryRule::getPreprocessingValidationRules();
|
|
break;
|
|
}
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $steps, '/', $error)) {
|
|
error($error);
|
|
$ret = false;
|
|
}
|
|
}
|
|
|
|
// Check previous time.
|
|
if ($this->use_prev_value && $this->getInput('prev_value', '') !== '') {
|
|
$prev_time = $this->getInput('prev_time', '');
|
|
|
|
$relative_time_parser = new CRelativeTimeParser();
|
|
if ($relative_time_parser->parse($prev_time) != CParser::PARSE_SUCCESS) {
|
|
error(_s('Incorrect value for field "%1$s": %2$s.', _('Prev. time'),
|
|
_('a relative time is expected')
|
|
));
|
|
$ret = false;
|
|
}
|
|
else {
|
|
$tokens = $relative_time_parser->getTokens();
|
|
|
|
if (count($tokens) > 1) {
|
|
error(_s('Incorrect value for field "%1$s": %2$s.', _('Prev. time'),
|
|
_('only one time unit is allowed')
|
|
));
|
|
}
|
|
elseif ($tokens && $tokens[0]['type'] == CRelativeTimeParser::ZBX_TOKEN_PRECISION) {
|
|
error(_s('Incorrect value for field "%1$s": %2$s.', _('Prev. time'),
|
|
_('a relative time is expected')
|
|
));
|
|
}
|
|
elseif ($tokens && !in_array($tokens[0]['suffix'], self::$supported_time_suffixes)) {
|
|
error(_s('Incorrect value for field "%1$s": %2$s.', _('Prev. time'),
|
|
_('unsupported time suffix')
|
|
));
|
|
}
|
|
elseif ($tokens && $tokens[0]['sign'] !== '-') {
|
|
error(_s('Incorrect value for field "%1$s": %2$s.', _('Prev. time'),
|
|
_('should be less than current time')
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($this->item_type == ITEM_TYPE_CALCULATED) {
|
|
$expression_parser = new CExpressionParser([
|
|
'usermacros' => true,
|
|
'lldmacros' => ($this->getInput('test_type') == self::ZBX_TEST_TYPE_ITEM_PROTOTYPE),
|
|
'calculated' => true,
|
|
'host_macro' => true,
|
|
'empty_host' => true
|
|
]);
|
|
|
|
if ($expression_parser->parse($this->getInput('params_f')) != CParser::PARSE_SUCCESS) {
|
|
error(_s('Incorrect value for field "%1$s": %2$s.', _('Formula'),
|
|
$expression_parser->getError()
|
|
));
|
|
}
|
|
else {
|
|
$expression_validator = new CExpressionValidator([
|
|
'usermacros' => true,
|
|
'lldmacros' => ($this->getInput('test_type') == self::ZBX_TEST_TYPE_ITEM_PROTOTYPE),
|
|
'calculated' => true
|
|
]);
|
|
|
|
if (!$expression_validator->validate($expression_parser->getResult()->getTokens())) {
|
|
error(_s('Incorrect value for field "%1$s": %2$s.', _('Formula'),
|
|
$expression_validator->getError()
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($messages = array_column(get_and_clear_messages(), 'message')) {
|
|
$this->setResponse(
|
|
new CControllerResponseData(['main_block' => json_encode([
|
|
'error' => [
|
|
'messages' => $messages
|
|
]
|
|
])])
|
|
);
|
|
|
|
$ret = false;
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
protected function doAction() {
|
|
global $ZBX_SERVER, $ZBX_SERVER_PORT;
|
|
|
|
/*
|
|
* Define values used to test preprocessing steps.
|
|
* Steps array can be empty if only value conversion is tested.
|
|
*/
|
|
$preproc_test_data = [
|
|
'value' => $this->getInput('value', ''),
|
|
'steps' => $this->getInput('steps', []),
|
|
'single' => !$this->show_final_result,
|
|
'state' => 0
|
|
];
|
|
|
|
// Get previous value and time.
|
|
if ($this->use_prev_value) {
|
|
$prev_value = $this->getInput('prev_value', '');
|
|
$prev_time = $this->getInput('prev_time', '');
|
|
|
|
if ($prev_value !== '' || $prev_time !== '') {
|
|
$preproc_test_data['history'] = [
|
|
'value' => $prev_value,
|
|
'timestamp' => $prev_time
|
|
];
|
|
}
|
|
}
|
|
|
|
$output = [
|
|
'steps' => [],
|
|
'user' => [
|
|
'debug_mode' => $this->getDebugMode()
|
|
]
|
|
];
|
|
|
|
$valuemap = ($this->getInput('valuemapid', 0) != 0)
|
|
? API::ValueMap()->get([
|
|
'output' => [],
|
|
'selectMappings' => ['type', 'newvalue', 'value'],
|
|
'valuemapids' => $this->getInput('valuemapid')
|
|
])[0]
|
|
: [];
|
|
|
|
// Get value from host.
|
|
if ($this->get_value_from_host) {
|
|
// Get post data for particular item type.
|
|
$item_test_data = $this->getItemTestProperties($this->getInputAll(), true);
|
|
|
|
// Apply effective macros values to properties.
|
|
$item_test_data = $this->resolveItemPropertyMacros($item_test_data);
|
|
|
|
// Rename fields according protocol.
|
|
$item_test_data = CArrayHelper::renameKeys($item_test_data, [
|
|
'params_ap' => 'params',
|
|
'params_es' => 'params',
|
|
'params_f' => 'params',
|
|
'script' => 'params',
|
|
'http_username' => 'username',
|
|
'http_password' => 'password',
|
|
'http_authtype' => 'authtype',
|
|
'item_type' => 'type'
|
|
]);
|
|
|
|
if (array_key_exists('headers', $item_test_data)) {
|
|
$item_test_data['headers'] = $this->transformHeaderFields($item_test_data['headers']);
|
|
}
|
|
|
|
if (array_key_exists('query_fields', $item_test_data)) {
|
|
$item_test_data['query_fields'] = $this->transformQueryFields($item_test_data['query_fields']);
|
|
}
|
|
|
|
if (array_key_exists('parameters', $item_test_data)) {
|
|
$item_test_data['parameters'] = $this->transformParametersFields($item_test_data['parameters']);
|
|
}
|
|
|
|
if ($item_test_data['type'] == ITEM_TYPE_CALCULATED) {
|
|
$item_test_data['host']['hostid'] = $this->getInput('hostid');
|
|
}
|
|
|
|
// Only non-empty fields need to be sent to server.
|
|
$item_test_data = $this->unsetEmptyValues($item_test_data);
|
|
|
|
/*
|
|
* Server will turn off status code check if field value is empty. If field is not present, then server will
|
|
* default to check if status code is 200.
|
|
*/
|
|
if ($this->item_type == ITEM_TYPE_HTTPAGENT && !array_key_exists('status_codes', $item_test_data)) {
|
|
$item_test_data['status_codes'] = '';
|
|
}
|
|
|
|
if ($this->item_type != ITEM_TYPE_CALCULATED) {
|
|
unset($item_test_data['value_type']);
|
|
}
|
|
|
|
// Send test to be executed on Zabbix server.
|
|
$server = new CZabbixServer($ZBX_SERVER, $ZBX_SERVER_PORT,
|
|
timeUnitToSeconds(CSettingsHelper::get(CSettingsHelper::CONNECT_TIMEOUT)),
|
|
timeUnitToSeconds(CSettingsHelper::get(CSettingsHelper::ITEM_TEST_TIMEOUT)), ZBX_SOCKET_BYTES_LIMIT
|
|
);
|
|
$result = $server->testItem($item_test_data, CSessionHelper::getId());
|
|
|
|
// Handle the response.
|
|
if ($result === false) {
|
|
error($server->getError());
|
|
}
|
|
elseif (is_array($result)) {
|
|
if (array_key_exists('result', $result)) {
|
|
// Move current value to previous value field.
|
|
if ($this->use_prev_value && $preproc_test_data['value'] !== '') {
|
|
$preproc_test_data['history']['value'] = $preproc_test_data['value'];
|
|
$preproc_test_data['history']['timestamp'] = $this->getPrevTime();
|
|
|
|
$output['prev_value'] = $preproc_test_data['value'];
|
|
$output['prev_time'] = $preproc_test_data['history']['timestamp'];
|
|
}
|
|
|
|
// Apply new value to preprocessing test.
|
|
$preproc_test_data['value'] = $result['result'];
|
|
$output['value'] = $result['result'];
|
|
$output['eol'] = (strstr($result['result'], "\r\n") === false) ? ZBX_EOL_LF : ZBX_EOL_CRLF;
|
|
}
|
|
|
|
if (array_key_exists('error', $result) && $result['error'] !== '') {
|
|
if ($preproc_test_data['steps']
|
|
&& $preproc_test_data['steps'][0]['type'] == ZBX_PREPROC_VALIDATE_NOT_SUPPORTED) {
|
|
$preproc_test_data['state'] = 1;
|
|
}
|
|
else {
|
|
error($result['error']);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($messages = get_and_clear_messages()) {
|
|
$output['error']['messages'] = array_column($messages, 'message');
|
|
}
|
|
}
|
|
else {
|
|
$preproc_test_data['state'] = $this->getInput('not_supported', 0);
|
|
}
|
|
|
|
// Test preprocessing steps.
|
|
$this->eol = parent::getInput('eol', ZBX_EOL_LF);
|
|
|
|
if (!array_key_exists('error', $output)) {
|
|
$preproc_test_data['value_type'] = $this->getInput('value_type', ITEM_VALUE_TYPE_STR);
|
|
|
|
$preproc_test_data['steps'] = $this->resolvePreprocessingStepMacros($preproc_test_data['steps']);
|
|
|
|
// Send test details to Zabbix server.
|
|
$server = new CZabbixServer($ZBX_SERVER, $ZBX_SERVER_PORT,
|
|
timeUnitToSeconds(CSettingsHelper::get(CSettingsHelper::CONNECT_TIMEOUT)),
|
|
timeUnitToSeconds(CSettingsHelper::get(CSettingsHelper::ITEM_TEST_TIMEOUT)), ZBX_SOCKET_BYTES_LIMIT
|
|
);
|
|
$result = $server->testPreprocessingSteps($preproc_test_data, CSessionHelper::getId());
|
|
|
|
if ($result === false) {
|
|
error($server->getError());
|
|
}
|
|
elseif (is_array($result)) {
|
|
$test_failed = false;
|
|
$test_outcome = null;
|
|
|
|
foreach ($preproc_test_data['steps'] as $i => &$step) {
|
|
if ($test_failed) {
|
|
// If test is failed, processing steps are skipped from results.
|
|
unset($preproc_test_data['steps'][$i]);
|
|
continue;
|
|
}
|
|
elseif (array_key_exists($i, $result['steps'])) {
|
|
$step += $result['steps'][$i];
|
|
|
|
if (array_key_exists('error', $step)) {
|
|
// If error happened and no value is set, frontend shows label 'No value'.
|
|
if (!array_key_exists('action', $step) || $step['action'] != ZBX_PREPROC_FAIL_SET_VALUE) {
|
|
unset($step['result']);
|
|
$test_failed = true;
|
|
}
|
|
}
|
|
elseif ($step['type'] == ZBX_PREPROC_VALIDATE_NOT_SUPPORTED) {
|
|
$step['result'] = $preproc_test_data['value'];
|
|
}
|
|
}
|
|
|
|
unset($step['type'], $step['params'], $step['error_handler'], $step['error_handler_params']);
|
|
|
|
// Latest executed step due to the error or end of preprocessing.
|
|
$test_outcome = $step + ['action' => ZBX_PREPROC_FAIL_DEFAULT];
|
|
}
|
|
unset($step);
|
|
|
|
if (array_key_exists('error', $result)) {
|
|
error($result['error']);
|
|
}
|
|
elseif ($this->show_final_result) {
|
|
if (array_key_exists('result', $result)) {
|
|
$output['final'] = [
|
|
'action' => _s('Result converted to %1$s',
|
|
itemValueTypeString($preproc_test_data['value_type'])
|
|
),
|
|
'result' => $result['result']
|
|
];
|
|
|
|
if ($valuemap) {
|
|
$output['mapped_value'] = CValueMapHelper::applyValueMap($preproc_test_data['value_type'],
|
|
$result['result'], $valuemap
|
|
);
|
|
}
|
|
}
|
|
elseif (array_key_exists('error', $result)) {
|
|
$output['final'] = [
|
|
'action' => ($test_outcome['action'] == ZBX_PREPROC_FAIL_SET_ERROR)
|
|
? _('Set error to')
|
|
: '',
|
|
'error' => $result['error']
|
|
];
|
|
}
|
|
|
|
if ($output['final']['action'] !== '') {
|
|
$output['final']['action'] = (new CSpan($output['final']['action']))
|
|
->addClass(ZBX_STYLE_GREY)
|
|
->toString();
|
|
}
|
|
}
|
|
|
|
$output['steps'] = $preproc_test_data['steps'];
|
|
}
|
|
|
|
if ($messages = get_and_clear_messages()) {
|
|
$output['error']['messages'] = array_column($messages, 'message');
|
|
}
|
|
}
|
|
|
|
$this->setResponse(new CControllerResponseData(['main_block' => json_encode($output)]));
|
|
}
|
|
}
|