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