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/app/controllers/CControllerPopupItemTestEdi...

438 lines
16 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 build preprocessing test dialog.
*/
class CControllerPopupItemTestEdit extends CControllerPopupItemTest {
protected function init() {
$this->disableCsrfValidation();
}
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]),
'data' => 'array',
'delay' => 'string',
'get_value' => 'in 0,1',
'headers' => 'array',
'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',
'follow_redirects' => 'in 0,1',
'key' => 'string',
'interfaceid' => 'db interface.interfaceid',
'ipmi_sensor' => 'string',
'itemid' => 'db items.itemid',
'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',
'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',
'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',
'step_obj' => 'required|int32',
'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]),
'timeout' => 'string',
'username' => 'string',
'url' => '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' => 'int32',
'verify_host' => 'in 0,1',
'verify_peer' => 'in 0,1'
];
if (getRequest('interfaceid') == INTERFACE_TYPE_OPT) {
unset($fields['interfaceid']);
unset($_REQUEST['interfaceid']);
}
$ret = $this->validateInput($fields);
if ($ret) {
$testable_item_types = self::getTestableItemTypes($this->getInput('hostid', '0'));
$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);
// Check if key is valid for item types it's mandatory.
if (in_array($this->item_type, $this->item_types_has_key_mandatory)) {
$item_key_parser = new CItemKey();
if ($item_key_parser->parse($this->getInput('key', '')) != CParser::PARSE_SUCCESS) {
error(_s('Incorrect value for field "%1$s": %2$s.', 'key_', $item_key_parser->getError()));
$ret = false;
}
}
/*
* Either the item must be testable or at least one preprocessing test must be passed ("Test" button should
* be disabled otherwise).
*/
$steps = $this->getInput('steps', []);
if ($ret && $steps) {
$steps = normalizeItemPreprocessingSteps($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;
}
}
elseif ($ret && !$this->is_item_testable) {
error(_s('Test of "%1$s" items is not supported.', item_type2str($this->item_type)));
$ret = false;
}
}
if (!$ret) {
$this->setResponse(
(new CControllerResponseData(['main_block' => json_encode([
'error' => [
'messages' => array_column(get_and_clear_messages(), 'message')
]
])]))->disableView()
);
}
return $ret;
}
protected function doAction() {
// VMware and icmpping simple checks are not supported.
$key = $this->hasInput('key') ? $this->getInput('key') : '';
if ($this->item_type == ITEM_TYPE_SIMPLE
&& (substr($key, 0, 7) === 'vmware.' || substr($key, 0, 8) === 'icmpping')) {
$this->is_item_testable = false;
}
// Get item and host properties and values from cache.
$data = $this->getInput('data', []);
$inputs = $this->getItemTestProperties($this->getInputAll());
// Work with preprocessing steps.
$preprocessing_steps_input = $this->getInput('steps', []);
$preprocessing_steps_input = normalizeItemPreprocessingSteps($preprocessing_steps_input);
$preprocessing_steps = [];
foreach ($preprocessing_steps_input as $preproc) {
if ($preproc['type'] == ZBX_PREPROC_VALIDATE_NOT_SUPPORTED) {
array_unshift($preprocessing_steps, $preproc);
}
else {
$preprocessing_steps[] = $preproc;
}
}
$preprocessing_types = zbx_objectValues($preprocessing_steps, 'type');
$preprocessing_names = get_preprocessing_types(null, false, $preprocessing_types);
$support_lldmacros = ($this->test_type == self::ZBX_TEST_TYPE_ITEM_PROTOTYPE);
$show_prev = (count(array_intersect($preprocessing_types, self::$preproc_steps_using_prev_value)) > 0);
// Collect item texts and macros to later check their usage.
$texts_support_macros = [];
$texts_support_user_macros = [];
$texts_support_lld_macros = [];
$supported_macros = [];
foreach (array_keys(array_intersect_key($inputs, $this->macros_by_item_props)) as $field) {
// Special processing for calculated item formula.
if ($field === 'params_f') {
$expression_parser = new CExpressionParser([
'usermacros' => true,
'lldmacros' => $support_lldmacros,
'calculated' => true,
'host_macro' => true,
'empty_host' => true
]);
if ($expression_parser->parse($inputs[$field]) == CParser::PARSE_SUCCESS) {
$tokens = $expression_parser->getResult()->getTokensOfTypes([
CExpressionParserResult::TOKEN_TYPE_USER_MACRO,
CExpressionParserResult::TOKEN_TYPE_LLD_MACRO,
CExpressionParserResult::TOKEN_TYPE_STRING,
CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION
]);
foreach ($tokens as $token) {
switch ($token['type']) {
case CExpressionParserResult::TOKEN_TYPE_USER_MACRO:
$texts_support_user_macros[] = $token['match'];
break;
case CExpressionParserResult::TOKEN_TYPE_LLD_MACRO:
$texts_support_lld_macros[] = $token['match'];
break;
case CExpressionParserResult::TOKEN_TYPE_STRING:
$text = CExpressionParser::unquoteString($token['match']);
$texts_support_user_macros[] = $text;
$texts_support_lld_macros[] = $text;
break;
case CExpressionParserResult::TOKEN_TYPE_HIST_FUNCTION:
foreach ($token['data']['parameters'] as $parameter) {
switch ($parameter['type']) {
case CHistFunctionParser::PARAM_TYPE_QUERY:
foreach ($parameter['data']['filter']['tokens'] as $filter_token) {
switch ($filter_token['type']) {
case CFilterParser::TOKEN_TYPE_USER_MACRO:
$texts_support_user_macros[] = $filter_token['match'];
break;
case CFilterParser::TOKEN_TYPE_LLD_MACRO:
$texts_support_lld_macros[] = $filter_token['match'];
break;
case CFilterParser::TOKEN_TYPE_STRING:
$text = CFilterParser::unquoteString($filter_token['match']);
$texts_support_user_macros[] = $text;
$texts_support_lld_macros[] = $text;
break;
}
}
break;
case CHistFunctionParser::PARAM_TYPE_PERIOD:
case CHistFunctionParser::PARAM_TYPE_QUOTED:
case CHistFunctionParser::PARAM_TYPE_UNQUOTED:
$texts_support_user_macros[] = $parameter['match'];
$texts_support_lld_macros[] = $parameter['match'] ;
break;
}
}
break;
}
}
}
continue;
}
$macros = $this->macros_by_item_props[$field];
unset($macros['support_lld_macros'], $macros['support_user_macros']);
if ($field === 'query_fields' || $field === 'headers' || $field === 'parameters') {
if (!array_key_exists($field, $inputs) || !$inputs[$field]) {
continue;
}
foreach (['name', 'value'] as $key) {
$texts_having_macros = array_filter($inputs[$field][$key], function($str) {
return (strstr($str, '{') !== false);
});
if ($texts_having_macros) {
$supported_macros = array_merge_recursive($supported_macros, $macros);
$texts_support_macros = array_merge($texts_support_macros, $texts_having_macros);
$texts_support_user_macros = array_merge($texts_support_user_macros, $texts_having_macros);
if ($support_lldmacros) {
$texts_support_lld_macros = array_merge($texts_support_lld_macros, $texts_having_macros);
}
}
}
}
elseif (strstr($inputs[$field], '{') !== false) {
if ($field === 'key') {
$item_key_parser = new CItemKey();
$texts_having_macros = $item_key_parser->parse($key) == CParser::PARSE_SUCCESS
? CMacrosResolverGeneral::getItemKeyParameters($item_key_parser->getParamsRaw())
: [];
}
else {
$texts_having_macros = [$inputs[$field]];
}
// Field support macros like {HOST.*}, {ITEM.*} etc.
if ($macros) {
$supported_macros = array_merge_recursive($supported_macros, $macros);
$texts_support_macros = array_merge($texts_support_macros, $texts_having_macros);
}
// Check if LLD macros are supported in field.
if ($support_lldmacros && $this->macros_by_item_props[$field]['support_lld_macros']) {
$texts_support_lld_macros = array_merge($texts_support_lld_macros, $texts_having_macros);
}
// Check if user macros are supported in field.
if ($this->macros_by_item_props[$field]['support_user_macros']) {
$texts_support_user_macros = array_merge($texts_support_user_macros, $texts_having_macros);
}
}
}
// Unset duplicate macros.
foreach ($supported_macros as &$item_macros_type) {
$item_macros_type = array_unique($item_macros_type);
}
unset($item_macros_type);
// Extract macros and apply effective values for each of them.
$usermacros = CMacrosResolverHelper::extractItemTestMacros([
'steps' => $preprocessing_steps,
'delay' => $show_prev ? $this->getInput('delay', ZBX_ITEM_DELAY_DEFAULT) : '',
'supported_macros' => $supported_macros,
'support_lldmacros' => $support_lldmacros,
'texts_support_macros' => $texts_support_macros,
'texts_support_user_macros' => $texts_support_user_macros,
'texts_support_lld_macros' => $texts_support_lld_macros,
'hostid' => $this->host ? $this->host['hostid'] : 0,
'macros_values' => $this->getSupportedMacros($inputs + ['interfaceid' => $this->getInput('interfaceid', 0)])
]);
$show_warning = false;
if (array_key_exists('interface', $inputs)) {
if (array_key_exists('address', $inputs['interface'])
&& strstr($inputs['interface']['address'], ZBX_SECRET_MASK) !== false) {
$inputs['interface']['address'] = '';
$show_warning = true;
}
if (array_key_exists('port', $inputs['interface']) && $inputs['interface']['port'] === ZBX_SECRET_MASK) {
$inputs['interface']['port'] = '';
$show_warning = true;
}
if (array_key_exists('details', $inputs['interface'])) {
foreach ($inputs['interface']['details'] as $field => $value) {
if (strstr($value, ZBX_SECRET_MASK) !== false) {
$inputs['interface']['details'][$field] = '';
$show_warning = true;
}
}
}
}
// Set resolved macros to previously specified values.
foreach (array_keys($usermacros['macros']) as $macro_name) {
if ($usermacros['macros'] && array_key_exists('macros', $data) && is_array($data['macros'])
&& array_key_exists($macro_name, $data['macros'])) {
// Macro values were set by user. Which means those could be intentional asterisks or empty fields.
$usermacros['macros'][$macro_name] = $data['macros'][$macro_name];
}
elseif ($usermacros['macros'][$macro_name] === ZBX_SECRET_MASK) {
/*
* Macro values were not set by user, so this means form was opened for the first time. So in this
* case check if there are secret macros. If there are, clear the values and show warning message box.
*/
$usermacros['macros'][$macro_name] = '';
$show_warning = true;
}
}
// Get previous value and time.
$prev_value = '';
$prev_time = '';
if ($show_prev && array_key_exists('prev_value', $data) && $data['prev_value'] !== '') {
$prev_value = $data['prev_value'];
// Get previous value time.
if (array_key_exists('prev_time', $data)) {
$prev_time = $data['prev_time'];
}
else {
$delay = timeUnitToSeconds($usermacros['delay']);
$prev_time = ($delay !== null && $delay > 0)
? 'now-'.$usermacros['delay']
: 'now';
}
}
// Sort macros.
ksort($usermacros['macros']);
// Add step number and name for each preprocessing step.
$num = 0;
foreach ($preprocessing_steps as &$step) {
$step['name'] = $preprocessing_names[$step['type']];
$step['num'] = ++$num;
}
unset($step);
$this->setResponse(new CControllerResponseData([
'title' => _('Test item'),
'steps' => $preprocessing_steps,
'value' => array_key_exists('value', $data) ? $data['value'] : '',
'eol' => array_key_exists('eol', $data) ? (int) $data['eol'] : ZBX_EOL_LF,
'macros' => $usermacros['macros'],
'show_prev' => $show_prev,
'prev_value' => $prev_value,
'prev_time' => $prev_time,
'hostid' => $this->getInput('hostid'),
'interfaceid' => $this->getInput('interfaceid', 0),
'test_type' => $this->test_type,
'step_obj' => $this->getInput('step_obj'),
'show_final_result' => $this->getInput('show_final_result'),
'valuemapid' => $this->getInput('valuemapid', 0),
'get_value' => array_key_exists('get_value', $data)
? $data['get_value']
: $this->getInput('get_value', 0),
'is_item_testable' => $this->is_item_testable,
'inputs' => $inputs,
'proxies' => in_array($this->item_type, $this->items_support_proxy) ? $this->getHostProxies() : [],
'proxies_enabled' => in_array($this->item_type, $this->items_support_proxy),
'interface_address_enabled' => (array_key_exists($this->item_type, $this->items_require_interface)
&& $this->items_require_interface[$this->item_type]['address']
),
'interface_port_enabled' => (array_key_exists($this->item_type, $this->items_require_interface)
&& $this->items_require_interface[$this->item_type]['port']
),
'show_snmp_form' => ($this->item_type == ITEM_TYPE_SNMP),
'show_warning' => $show_warning,
'user' => [
'debug_mode' => $this->getDebugMode()
]
]));
}
}