diff options
26 files changed, 2233 insertions, 320 deletions
diff --git a/.gitignore b/.gitignore index 2a9ea39461b..090ec11cd35 100644 --- a/.gitignore +++ b/.gitignore @@ -152,7 +152,12 @@ tests/libs/zbxsysinfo/parse_item_key tests/libs/zbxsysinfo/process tests/libs/zbxcommon/zbx_user_macro_parse tests/libs/zbxdbcache/is_item_processed_by_server +<<<<<<< HEAD +======= +tests/libs/zbxjson/zbx_jsonpath_next +>>>>>>> ........S. [ZBX-15827] added tests for preprocessing testing tests/zabbix_server/preprocessor/item_preproc_xpath tests/zabbix_server/preprocessor/zbx_item_preproc +tests/zabbix_server/trapper/zbx_trapper_preproc_test_run /zabbix-*.tar.gz .vscode diff --git a/ChangeLog b/ChangeLog index 4faaea61bbd..617a2bb5b1e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -17,6 +17,7 @@ Bug fixes: Changes for 4.2.4rc1 New features: +..F.....S. [ZBXNEXT-5124,ZBX-15827] extended preprocessing steps with final result row; improved input validation in preprocessing steps (miks, wiper) .......PS. [ZBXNEXT-2880] added access to vmware datastore at vmware vCenter level (MVekslers) ..F....... [ZBXNEXT-5163] added display of maintenance information in Configuration section for hosts in maitenance (vasilijs) ..F....... [ZBXNEXT-5036] added preloader for popup menus (ashubin) diff --git a/conf_tests.m4 b/conf_tests.m4 index 8b1f6067a8d..9351563e589 100644 --- a/conf_tests.m4 +++ b/conf_tests.m4 @@ -37,6 +37,7 @@ AC_DEFUN([CONF_TESTS],[ tests/zabbix_server/Makefile tests/zabbix_server/preprocessor/Makefile tests/libs/zbxcomms/Makefile + tests/zabbix_server/trapper/Makefile ]) AC_DEFINE([HAVE_TESTS], [1], ["Define to 1 if tests directory is present"]) ]) diff --git a/frontends/php/app/controllers/CControllerPopupPreprocTestEdit.php b/frontends/php/app/controllers/CControllerPopupPreprocTestEdit.php index 0dd5ab89dc4..4e9c26f7b87 100644 --- a/frontends/php/app/controllers/CControllerPopupPreprocTestEdit.php +++ b/frontends/php/app/controllers/CControllerPopupPreprocTestEdit.php @@ -32,7 +32,8 @@ class CControllerPopupPreprocTestEdit extends CControllerPopupPreprocTest { 'steps' => 'required|array', 'delay' => 'string', 'data' => 'array', - 'step_obj' => 'required|int32' + 'step_obj' => 'required|int32', + 'show_final_result' => 'in 0,1' ]; $ret = $this->validateInput($fields); @@ -118,6 +119,7 @@ class CControllerPopupPreprocTestEdit extends CControllerPopupPreprocTest { 'value_type' => $this->getInput('value_type'), 'test_type' => $this->getInput('test_type'), 'step_obj' => $this->getInput('step_obj'), + 'show_final_result' => $this->getInput('show_final_result'), 'user' => [ 'debug_mode' => $this->getDebugMode() ] diff --git a/frontends/php/app/controllers/CControllerPopupPreprocTestSend.php b/frontends/php/app/controllers/CControllerPopupPreprocTestSend.php index f2e3c2106dd..2d1e66e0c83 100644 --- a/frontends/php/app/controllers/CControllerPopupPreprocTestSend.php +++ b/frontends/php/app/controllers/CControllerPopupPreprocTestSend.php @@ -27,6 +27,11 @@ class CControllerPopupPreprocTestSend extends CControllerPopupPreprocTest { /** * @var bool */ + protected $show_final_result; + + /** + * @var bool + */ protected $use_prev_value; /** @@ -46,7 +51,8 @@ class CControllerPopupPreprocTestSend extends CControllerPopupPreprocTest { 'macros' => 'array', 'value' => 'string', 'prev_value' => 'string', - 'prev_time' => 'string' + 'prev_time' => 'string', + 'show_final_result' => 'in 0,1' ]; $ret = $this->validateInput($fields); @@ -56,6 +62,7 @@ class CControllerPopupPreprocTestSend extends CControllerPopupPreprocTest { $prepr_types = zbx_objectValues($steps, 'type'); $this->preproc_item = self::getPreprocessingItemType($this->getInput('test_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); // Check preprocessing steps. if (($error = $this->preproc_item->validateItemPreprocessingSteps($steps)) !== true) { @@ -123,7 +130,8 @@ class CControllerPopupPreprocTestSend extends CControllerPopupPreprocTest { $data = [ 'value' => $this->getInput('value', ''), 'value_type' => $this->getInput('value_type', ITEM_VALUE_TYPE_STR), - 'steps' => $this->getInput('steps') + 'steps' => $this->getInput('steps'), + 'single' => !$this->show_final_result ]; // Resolve macros used in parameter fields. @@ -178,14 +186,15 @@ class CControllerPopupPreprocTestSend extends CControllerPopupPreprocTest { } elseif (is_array($result)) { $test_failed = false; + $test_outcome = null; foreach ($data['steps'] as $i => &$step) { if ($test_failed) { // If test is failed, proceesing steps are skipped from results. unset($data['steps'][$i]); continue; } - elseif (array_key_exists($i, $result)) { - $step += $result[$i]; + 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'. @@ -200,9 +209,38 @@ class CControllerPopupPreprocTestSend extends CControllerPopupPreprocTest { unset($step['params']); unset($step['error_handler']); unset($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('previous', $result) && $result['previous'] === true) { + error(_s('Incorrect value for "%1$s" field.', _('Previous value'))); + } + elseif ($this->show_final_result) { + if (array_key_exists('result', $result)) { + $output['final'] = [ + 'action' => _s('Result converted to %1$s', itemValueTypeString($data['value_type'])), + 'result' => $result['result'] + ]; + } + 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'] = $data['steps']; } diff --git a/frontends/php/app/views/popup.preproctestedit.view.js.php b/frontends/php/app/views/popup.preproctestedit.view.js.php index 3d60dd2c551..72655153e4e 100644 --- a/frontends/php/app/views/popup.preproctestedit.view.js.php +++ b/frontends/php/app/views/popup.preproctestedit.view.js.php @@ -22,6 +22,35 @@ ob_start(); ?> /** + * Make step result UI element. + * + * @param array step Step object returned from server. + * + * @return jQuery + */ +function makeStepResult(step) { + if (typeof step.error !== 'undefined') { + return jQuery(new Template(jQuery('#preprocessing-step-error-icon').html()).evaluate( + {error: step.error || <?= CJs::encodeJson(_('<empty string>')) ?>} + )); + } + else if (typeof step.result === 'undefined' || step.result === null) { + return jQuery('<span>', {'class': '<?= ZBX_STYLE_GREY ?>'}).text(<?= CJs::encodeJson(_('No value')) ?>); + } + else if (step.result === '') { + return jQuery('<span>', {'class': '<?= ZBX_STYLE_GREY ?>'}).text(<?= CJs::encodeJson(_('<empty string>')) ?>); + } + else if (step.result.indexOf("\n") != -1 || step.result.length > 25) { + return jQuery(new Template(jQuery('#preprocessing-step-result').html()).evaluate( + jQuery.extend({result: step.result}) + )); + } + else { + return jQuery('<span>').text(step.result); + } +} + +/** * Send item preprocessing test details and display results in table. * * @param string formid Selector for form to send. @@ -55,6 +84,10 @@ function itemPreprocessingTest(form) { // Clean previous results. jQuery('[id^="preproc-test-step-"][id$="-result"]').empty(); jQuery('[id^="preproc-test-step-"][id$="-name"] > div').remove(); + jQuery('#final-result') + .hide() + .find('.table-forms-td-right') + .empty(); }, success: function(ret) { jQuery(form).parent().find('.msg-bad, .msg-good').remove(); @@ -63,7 +96,19 @@ function itemPreprocessingTest(form) { if (typeof ret.messages !== 'undefined') { jQuery(ret.messages).insertBefore(jQuery(form)); - jQuery(form).parent().find('.link-action').click(); + } + + if (typeof ret.final !== 'undefined') { + var result = makeStepResult(ret.final); + if (result !== null) { + $result = jQuery(result).css('float', 'right'); + } + + jQuery('#final-result') + .show() + .find('.table-forms-td-right') + .append(ret.final.action) + .append($result); } jQuery('#value, #time, [name^=macros]').prop('disabled', false); @@ -87,10 +132,8 @@ function itemPreprocessingTest(form) { * @param array steps Array of objects containing details about each preprocessing step test results. */ function processItemPreprocessingTestResults(steps) { - var tmpl_err_icon = new Template(jQuery('#preprocessing-step-error-icon').html()), - tmpl_gray_label = new Template(jQuery('#preprocessing-gray-label').html()), - tmpl_act_done = new Template(jQuery('#preprocessing-step-action-done').html()), - tmpl_step_result = new Template(jQuery('#preprocessing-step-result').html()); + var tmpl_gray_label = new Template(jQuery('#preprocessing-gray-label').html()), + tmpl_act_done = new Template(jQuery('#preprocessing-step-action-done').html()); steps.each(function(step, i) { if (typeof step.action !== 'undefined') { @@ -119,24 +162,7 @@ function processItemPreprocessingTestResults(steps) { } } - if (typeof step.error !== 'undefined') { - step.result = jQuery( - tmpl_err_icon.evaluate({error: step.error || <?= CJs::encodeJson(_('<empty string>')) ?>}) - ); - } - else if (typeof step.result === 'undefined' || step.result === null) { - step.result = jQuery('<span>', - {'class': '<?= ZBX_STYLE_GREY ?>'} - ).text(<?= CJs::encodeJson(_('No value')) ?>); - } - else if (step.result === '') { - step.result = jQuery('<span>', - {'class': '<?= ZBX_STYLE_GREY ?>'} - ).text(<?= CJs::encodeJson(_('<empty string>')) ?>); - } - else if (step.result.indexOf("\n") != -1 || step.result.length > 25) { - step.result = jQuery(tmpl_step_result.evaluate(jQuery.extend({result: step.result}))); - } + step.result = makeStepResult(step); if (typeof step.action !== 'undefined' && step.action !== null) { jQuery('#preproc-test-step-'+i+'-name').append(jQuery(tmpl_gray_label.evaluate(<?= CJs::encodeJson([ @@ -177,6 +203,8 @@ function savePreprocessingTestInputs() { } jQuery(document).ready(function($) { + $('#final-result').hide(); + $('#value').multilineInput({ placeholder: <?= CJs::encodeJson(_('value')) ?>, value: <?= CJs::encodeJson($data['value']) ?>, diff --git a/frontends/php/app/views/popup.preproctestedit.view.php b/frontends/php/app/views/popup.preproctestedit.view.php index f761bb7ca50..22984b6e10f 100644 --- a/frontends/php/app/views/popup.preproctestedit.view.php +++ b/frontends/php/app/views/popup.preproctestedit.view.php @@ -25,6 +25,7 @@ $form = (new CForm()) ->addVar('hostid', $data['hostid']) ->addVar('value_type', $data['value_type']) ->addVar('test_type', $data['test_type']) + ->addVar('show_final_result', $data['show_final_result']) ->setId('preprocessing-test-form'); // Create macros table. @@ -117,6 +118,14 @@ $form_list->addRow( ->addStyle('width: 100%;') ); +if ($data['show_final_result']) { + $form_list->addRow( + _('Result'), + new CDiv(), + 'final-result' + ); +} + $form ->addItem($form_list) ->addItem((new CInput('submit', 'submit'))->addStyle('display: none;')); diff --git a/frontends/php/include/forms.inc.php b/frontends/php/include/forms.inc.php index 011e262e7ad..2e48cc01850 100644 --- a/frontends/php/include/forms.inc.php +++ b/frontends/php/include/forms.inc.php @@ -1627,7 +1627,7 @@ function getItemPreprocessing(CForm $form, array $preprocessing, $readonly, arra (new CDiv( (new CButton('preproc_test_all', _('Test all steps'))) ->addClass(ZBX_STYLE_BTN_LINK) - ->addStyle(($i > 1) ? null : 'display: none') + ->addStyle(($i > 0) ? null : 'display: none') ))->addClass('step-action') ]))->addClass('preprocessing-list-foot') ); diff --git a/frontends/php/include/views/js/item.preprocessing.js.php b/frontends/php/include/views/js/item.preprocessing.js.php index 936b5eb8d6f..00a66beb940 100644 --- a/frontends/php/include/views/js/item.preprocessing.js.php +++ b/frontends/php/include/views/js/item.preprocessing.js.php @@ -112,10 +112,11 @@ /** * Creates preprocessing test modal window. * - * @param {array} step_nums List of step numbers to collect. - * @param {object} trigger_elmnt UI element triggered function. + * @param {array} step_nums List of step numbers to collect. + * @param {bool} show_final_result Either the final result should be displayed. + * @param {object} trigger_elmnt UI element triggered function. */ - function openPreprocessingTestDialog(step_nums, trigger_elmnt) { + function openPreprocessingTestDialog(step_nums, show_final_result, trigger_elmnt) { var $step_obj = $(trigger_elmnt).closest('.preprocessing-list-item, .preprocessing-list-foot'); PopUp('popup.preproctest.edit', $.extend({ @@ -124,7 +125,8 @@ steps: getPreprocessingSteps(step_nums), hostid: <?= $data['hostid'] ?>, test_type: <?= $data['preprocessing_test_type'] ?>, - step_obj: $step_obj.attr('data-step') || -1 + step_obj: $step_obj.attr('data-step') || -1, + show_final_result: show_final_result ? 1 : 0 }, {'data': $step_obj.data('test-data') || []}), 'preprocessing-test', trigger_elmnt); } @@ -252,12 +254,12 @@ var sortable_count = $preprocessing.find('li.sortable').length; if (sortable_count == 1) { + $('#preproc_test_all').show(); $preprocessing .sortable('disable') .find('div.<?= ZBX_STYLE_DRAG_ICON ?>').addClass('<?= ZBX_STYLE_DISABLED ?>'); } else if (sortable_count > 1) { - $('#preproc_test_all').show(); $preprocessing .sortable('enable') .find('div.<?= ZBX_STYLE_DRAG_ICON ?>').removeClass('<?= ZBX_STYLE_DISABLED ?>'); @@ -272,13 +274,13 @@ step_nums.push(str.substr(14, str.length - 21)); }); - openPreprocessingTestDialog(step_nums, this); + openPreprocessingTestDialog(step_nums, true, this); }) .on('click', '.preprocessing-step-test', function() { var str = $(this).attr('name'), num = str.substr(14, str.length - 21); - openPreprocessingTestDialog([num], this); + openPreprocessingTestDialog([num], false, this); }) .on('click', '.element-table-remove', function() { $(this).closest('li.sortable').remove(); @@ -286,10 +288,10 @@ var sortable_count = $preprocessing.find('li.sortable').length; if (sortable_count == 0) { + $('#preproc_test_all').hide(); $('.preprocessing-list-head').hide(); } else if (sortable_count == 1) { - $('#preproc_test_all').hide(); $preprocessing .sortable('disable') .find('div.<?= ZBX_STYLE_DRAG_ICON ?>').addClass('<?= ZBX_STYLE_DISABLED ?>'); diff --git a/include/common.h b/include/common.h index 7cab4448323..2ee7a94ddcc 100644 --- a/include/common.h +++ b/include/common.h @@ -1547,4 +1547,6 @@ char *zbx_create_token(zbx_uint64_t seed); #define ZBX_PROBLEM_SUPPRESSED_FALSE 0 #define ZBX_PROBLEM_SUPPRESSED_TRUE 1 +int zbx_variant_to_value_type(zbx_variant_t *value, unsigned char value_type, char **errmsg); + #endif diff --git a/include/preproc.h b/include/preproc.h index e43536a5f24..450822ae4bc 100644 --- a/include/preproc.h +++ b/include/preproc.h @@ -43,7 +43,8 @@ zbx_uint64_t zbx_preprocessor_get_queue_size(void); void zbx_preproc_op_free(zbx_preproc_op_t *op); void zbx_preproc_result_free(zbx_preproc_result_t *result); -int zbx_preprocessor_test(unsigned char value_type, const char *value, const char *last_value, const char *last_ts, - const zbx_vector_ptr_t *steps, zbx_vector_ptr_t *results, char **preproc_error, char **error); +int zbx_preprocessor_test(unsigned char value_type, const char *value, const zbx_timespec_t *ts, + const zbx_vector_ptr_t *steps, zbx_vector_ptr_t *results, zbx_vector_ptr_t *history, + char **preproc_error, char **error); #endif /* ZABBIX_PREPROC_H */ diff --git a/include/zbxjson.h b/include/zbxjson.h index 8e450d19c23..7b37f98b5ae 100644 --- a/include/zbxjson.h +++ b/include/zbxjson.h @@ -109,13 +109,15 @@ #define ZBX_PROTO_TAG_ACTION "action" #define ZBX_PROTO_TAG_FAILED "failed" #define ZBX_PROTO_TAG_RESULT "result" -#define ZBX_PROTO_TAG_LINE_RAW "line_raw" -#define ZBX_PROTO_TAG_LABELS "labels" -#define ZBX_PROTO_TAG_HELP "help" -#define ZBX_PROTO_TAG_MEDIATYPEID "mediatypeid" -#define ZBX_PROTO_TAG_SENDTO "sendto" -#define ZBX_PROTO_TAG_SUBJECT "subject" -#define ZBX_PROTO_TAG_MESSAGE "message" +#define ZBX_PROTO_TAG_LINE_RAW "line_raw" +#define ZBX_PROTO_TAG_LABELS "labels" +#define ZBX_PROTO_TAG_HELP "help" +#define ZBX_PROTO_TAG_MEDIATYPEID "mediatypeid" +#define ZBX_PROTO_TAG_SENDTO "sendto" +#define ZBX_PROTO_TAG_SUBJECT "subject" +#define ZBX_PROTO_TAG_MESSAGE "message" +#define ZBX_PROTO_TAG_PREVIOUS "previous" +#define ZBX_PROTO_TAG_SINGLE "single" #define ZBX_PROTO_VALUE_FAILED "failed" #define ZBX_PROTO_VALUE_SUCCESS "success" diff --git a/src/libs/zbxcommon/misc.c b/src/libs/zbxcommon/misc.c index 0f7a15830aa..c2974bd057b 100644 --- a/src/libs/zbxcommon/misc.c +++ b/src/libs/zbxcommon/misc.c @@ -3735,3 +3735,60 @@ void zbx_update_env(double time_now) #endif } } + +/****************************************************************************** + * * + * Function: zbx_variant_to_value_type * + * * + * Purpose: converts variant value to type compatible with requested value * + * type * + * * + * Parameters: value - [IN/OUT] the value to convert * + * value_type - [IN] the target value type * + * errmsg - [OUT] the error message * + * * + * Return value: SUCCEED - Value conversion was successful. * + * FAIL - Otherwise * + * * + ******************************************************************************/ +int zbx_variant_to_value_type(zbx_variant_t *value, unsigned char value_type, char **errmsg) +{ + int ret; + + zbx_free(*errmsg); + + switch (value_type) + { + case ITEM_VALUE_TYPE_FLOAT: + if (SUCCEED == (ret = zbx_variant_convert(value, ZBX_VARIANT_DBL))) + { + if (FAIL == (ret = zbx_validate_value_dbl(value->data.dbl))) + { + *errmsg = zbx_dsprintf(NULL, "Value " ZBX_FS_DBL " is too small or too large.", + value->data.dbl); + } + } + break; + case ITEM_VALUE_TYPE_UINT64: + ret = zbx_variant_convert(value, ZBX_VARIANT_UI64); + break; + case ITEM_VALUE_TYPE_STR: + case ITEM_VALUE_TYPE_TEXT: + case ITEM_VALUE_TYPE_LOG: + ret = zbx_variant_convert(value, ZBX_VARIANT_STR); + break; + default: + *errmsg = zbx_dsprintf(NULL, "Unknown value type \"%d\"", value_type); + THIS_SHOULD_NEVER_HAPPEN; + ret = FAIL; + } + + if (FAIL == ret && NULL == *errmsg) + { + *errmsg = zbx_dsprintf(NULL, "Value \"%s\" of type \"%s\" is not suitable for value type \"%s\"", + zbx_variant_value_desc(value), zbx_variant_type_desc(value), + zbx_item_value_type_string(value_type)); + } + + return ret; +} diff --git a/src/libs/zbxdbcache/dbcache.c b/src/libs/zbxdbcache/dbcache.c index 6e3c36e6dbe..5109179c6a5 100644 --- a/src/libs/zbxdbcache/dbcache.c +++ b/src/libs/zbxdbcache/dbcache.c @@ -1727,51 +1727,15 @@ static void dc_history_set_error(ZBX_DC_HISTORY *hdata, char *errmsg) * value_type - [IN] the item value type * * value - [IN] the value to set * * * - * Return value: SUCCEED - Value conversion was successful. * - * FAIL - Otherwise * - * * ******************************************************************************/ -static int dc_history_set_value(ZBX_DC_HISTORY *hdata, unsigned char value_type, zbx_variant_t *value) +static void dc_history_set_value(ZBX_DC_HISTORY *hdata, unsigned char value_type, zbx_variant_t *value) { - int ret; char *errmsg = NULL; - switch (value_type) - { - case ITEM_VALUE_TYPE_FLOAT: - if (SUCCEED == (ret = zbx_variant_convert(value, ZBX_VARIANT_DBL))) - { - if (FAIL == (ret = zbx_validate_value_dbl(value->data.dbl))) - { - errmsg = zbx_dsprintf(NULL, "Value " ZBX_FS_DBL " is too small or too large.", - value->data.dbl); - } - } - break; - case ITEM_VALUE_TYPE_UINT64: - ret = zbx_variant_convert(value, ZBX_VARIANT_UI64); - break; - case ITEM_VALUE_TYPE_STR: - case ITEM_VALUE_TYPE_TEXT: - case ITEM_VALUE_TYPE_LOG: - ret = zbx_variant_convert(value, ZBX_VARIANT_STR); - break; - default: - THIS_SHOULD_NEVER_HAPPEN; - return FAIL; - } - - if (FAIL == ret) + if (FAIL == zbx_variant_to_value_type(value, value_type, &errmsg)) { - if (NULL == errmsg) - { - errmsg = zbx_dsprintf(NULL, "Value \"%s\" of type \"%s\" is not suitable for" - " value type \"%s\"", zbx_variant_value_desc(value), - zbx_variant_type_desc(value), zbx_item_value_type_string(value_type)); - } - dc_history_set_error(hdata, errmsg); - return FAIL; + return; } switch (value_type) @@ -1807,8 +1771,6 @@ static int dc_history_set_value(ZBX_DC_HISTORY *hdata, unsigned char value_type, hdata->value_type = value_type; zbx_variant_set_none(value); - - return ret; } /****************************************************************************** @@ -1821,25 +1783,17 @@ static int dc_history_set_value(ZBX_DC_HISTORY *hdata, unsigned char value_type, * Parameters: item - [IN] the item * * hdata - [IN/OUT] the historical data to process * * * - * Return value: SUCCEED - Normalization was successful. * - * FAIL - Otherwise - ZBX_DC_FLAG_UNDEF will be set and item * - * state changed to ZBX_NOTSUPPORTED. * - * * ******************************************************************************/ -static int normalize_item_value(const DC_ITEM *item, ZBX_DC_HISTORY *hdata) +static void normalize_item_value(const DC_ITEM *item, ZBX_DC_HISTORY *hdata) { - int ret = FAIL; char *logvalue; zbx_variant_t value_var; if (0 != (hdata->flags & ZBX_DC_FLAG_NOVALUE)) - { - ret = SUCCEED; - goto out; - } + return; if (ITEM_STATE_NOTSUPPORTED == hdata->state) - goto out; + return; if (0 == (hdata->flags & ZBX_DC_FLAG_NOHISTORY)) hdata->ttl = item->history_sec; @@ -1864,11 +1818,10 @@ static int normalize_item_value(const DC_ITEM *item, ZBX_DC_HISTORY *hdata) { dc_history_set_error(hdata, zbx_dsprintf(NULL, "Value " ZBX_FS_DBL " is too small or too large.", hdata->value.dbl)); - return FAIL; } break; } - return SUCCEED; + return; } switch (hdata->value_type) @@ -1890,10 +1843,8 @@ static int normalize_item_value(const DC_ITEM *item, ZBX_DC_HISTORY *hdata) break; } - ret = dc_history_set_value(hdata, item->value_type, &value_var); + dc_history_set_value(hdata, item->value_type, &value_var); zbx_variant_clear(&value_var); -out: - return ret; } /****************************************************************************** diff --git a/src/zabbix_server/preprocessor/item_preproc.c b/src/zabbix_server/preprocessor/item_preproc.c index 0a5ceb92268..bae5fd0db44 100644 --- a/src/zabbix_server/preprocessor/item_preproc.c +++ b/src/zabbix_server/preprocessor/item_preproc.c @@ -31,6 +31,7 @@ #include "zbxjson.h" #include "zbxembed.h" #include "zbxprometheus.h" +#include "preproc_history.h" #include "item_preproc.h" @@ -1875,6 +1876,78 @@ int zbx_item_preproc_handle_error(zbx_variant_t *value, const zbx_preproc_op_t * return FAIL; } } + +/****************************************************************************** + * * + * Function: zbx_item_preproc_test * + * * + * Purpose: test preprocessing steps * + * * + * Parameters: value_type - [IN] the item value type * + * value - [IN/OUT] the value to process * + * ts - [IN] the value timestamp * + * steps - [IN] the preprocessing steps to execute * + * steps_num - [IN] the number of preprocessing steps * + * history_in - [IN] the preprocessing history * + * history_out - [OUT] the new preprocessing history * + * results - [OUT] the preprocessing step results * + * results_num - [OUT] the number of step results * + * error - [OUT] error message * + * * + * Return value: SUCCEED - the preprocessing steps finished successfully * + * FAIL - otherwise, error contains the error message * + * * + ******************************************************************************/ +int zbx_item_preproc_test(unsigned char value_type, zbx_variant_t *value, const zbx_timespec_t *ts, + zbx_preproc_op_t *steps, int steps_num, zbx_vector_ptr_t *history_in, zbx_vector_ptr_t *history_out, + zbx_preproc_result_t *results, int *results_num, char **error) +{ + int i, ret = SUCCEED; + + for (i = 0; i < steps_num; i++) + { + zbx_preproc_op_t *op = &steps[i]; + zbx_variant_t history_value; + zbx_timespec_t history_ts; + + zbx_preproc_history_pop_value(history_in, i, &history_value, &history_ts); + + if (FAIL == (ret = zbx_item_preproc(value_type, value, ts, op, &history_value, &history_ts, error))) + { + results[i].action = op->error_handler; + results[i].error = zbx_strdup(NULL, *error); + ret = zbx_item_preproc_handle_error(value, op, error); + } + else + { + results[i].action = ZBX_PREPROC_FAIL_DEFAULT; + results[i].error = NULL; + } + + if (SUCCEED != ret) + { + zbx_variant_set_none(&results[i].value); + zbx_variant_clear(&history_value); + break; + } + + zbx_variant_set_variant(&results[i].value, value); + + if (ZBX_VARIANT_NONE != history_value.type) + { + /* the value is byte copied to history_out vector and doesn't have to be cleared */ + zbx_preproc_history_add_value(history_out, i, &history_value, &history_ts); + } + + if (ZBX_VARIANT_NONE == value->type) + break; + } + + *results_num = (i == steps_num ? i : i + 1); + + return ret; +} + #ifdef HAVE_TESTS # include "../../../tests/zabbix_server/preprocessor/item_preproc_test.c" #endif diff --git a/src/zabbix_server/preprocessor/item_preproc.h b/src/zabbix_server/preprocessor/item_preproc.h index b1252aa1a31..27199ed8505 100644 --- a/src/zabbix_server/preprocessor/item_preproc.h +++ b/src/zabbix_server/preprocessor/item_preproc.h @@ -21,6 +21,7 @@ #define ZABBIX_ITEM_PREPROC_H #include "dbcache.h" +#include "preproc.h" int zbx_item_preproc(unsigned char value_type, zbx_variant_t *value, const zbx_timespec_t *ts, const zbx_preproc_op_t *op, zbx_variant_t *history_value, zbx_timespec_t *history_ts, char **error); @@ -30,4 +31,8 @@ int zbx_item_preproc_handle_error(zbx_variant_t *value, const zbx_preproc_op_t * int zbx_item_preproc_convert_value_to_numeric(zbx_variant_t *value_num, const zbx_variant_t *value, unsigned char value_type, char **errmsg); +int zbx_item_preproc_test(unsigned char value_type, zbx_variant_t *value, const zbx_timespec_t *ts, + zbx_preproc_op_t *steps, int steps_num, zbx_vector_ptr_t *history_in, zbx_vector_ptr_t *history_out, + zbx_preproc_result_t *results, int *results_num, char **error); + #endif diff --git a/src/zabbix_server/preprocessor/preproc_worker.c b/src/zabbix_server/preprocessor/preproc_worker.c index bf9397d0b71..64ef91e0234 100644 --- a/src/zabbix_server/preprocessor/preproc_worker.c +++ b/src/zabbix_server/preprocessor/preproc_worker.c @@ -358,80 +358,6 @@ static void worker_preprocess_value(zbx_ipc_socket_t *socket, zbx_ipc_message_t /****************************************************************************** * * - * Function: worker_item_preproc_test * - * * - * Purpose: test preprocessing steps * - * * - * Parameters: value_type - [IN] the item value type * - * value - [IN/OUT] the value to process * - * ts - [IN] the value timestamp * - * steps - [IN] the preprocessing steps to execute * - * steps_num - [IN] the number of preprocessing steps * - * history_in - [IN] the preprocessing history * - * history_out - [OUT] the new preprocessing history * - * results - [OUT] the preprocessing step results * - * results_num - [OUT] the number of step results * - * error - [OUT] error message * - * * - * Return value: SUCCEED - the preprocessing steps finished successfully * - * FAIL - otherwise, error contains the error message * - * * - ******************************************************************************/ -static int worker_item_preproc_test(unsigned char value_type, zbx_variant_t *value, const zbx_timespec_t *ts, - zbx_preproc_op_t *steps, int steps_num, zbx_vector_ptr_t *history_in, zbx_vector_ptr_t *history_out, - zbx_preproc_result_t *results, int *results_num, char **error) -{ - int i, ret = SUCCEED; - - for (i = 0; i < steps_num; i++) - { - zbx_preproc_op_t *op = &steps[i]; - zbx_variant_t history_value; - zbx_timespec_t history_ts; - - zbx_preproc_history_pop_value(history_in, i, &history_value, &history_ts); - - if (FAIL == (ret = zbx_item_preproc(value_type, value, ts, op, &history_value, &history_ts, error))) - { - results[i].action = op->error_handler; - ret = zbx_item_preproc_handle_error(value, op, error); - } - else - results[i].action = ZBX_PREPROC_FAIL_DEFAULT; - - if (NULL != *error) - { - results[i].error = zbx_strdup(NULL, *error); - ret = FAIL; - } - - if (SUCCEED != ret) - { - zbx_variant_set_none(&results[i].value); - zbx_variant_clear(&history_value); - break; - } - - zbx_variant_set_variant(&results[i].value, value); - results[i].error = NULL; - - if (ZBX_VARIANT_NONE != history_value.type) - { - /* the value is byte copied to history_out vector and doesn't have to be cleared */ - zbx_preproc_history_add_value(history_out, i, &history_value, &history_ts); - } - - if (ZBX_VARIANT_NONE == value->type) - break; - } - - *results_num = (i == steps_num ? i : i + 1); - - return ret; -} - -/****************************************************************************** - * * * Function: worker_test_value * * * * Purpose: handle item value test preprocessing task * @@ -464,7 +390,7 @@ static void worker_test_value(zbx_ipc_socket_t *socket, zbx_ipc_message_t *messa results = (zbx_preproc_result_t *)zbx_malloc(NULL, sizeof(zbx_preproc_result_t) * steps_num); memset(results, 0, sizeof(zbx_preproc_result_t) * steps_num); - worker_item_preproc_test(value_type, &value, &ts, steps, steps_num, &history_in, &history_out, results, + zbx_item_preproc_test(value_type, &value, &ts, steps, steps_num, &history_in, &history_out, results, &results_num, &error); size = zbx_preprocessor_pack_test_result(&data, results, results_num, &history_out, error); diff --git a/src/zabbix_server/preprocessor/preprocessing.c b/src/zabbix_server/preprocessor/preprocessing.c index baf62573da1..c815c3b2baa 100644 --- a/src/zabbix_server/preprocessor/preprocessing.c +++ b/src/zabbix_server/preprocessor/preprocessing.c @@ -998,76 +998,30 @@ void zbx_preprocessor_unpack_test_request(unsigned char *value_type, char **valu * Purpose: tests item preprocessing with the specified input value and steps * * * ******************************************************************************/ -int zbx_preprocessor_test(unsigned char value_type, const char *value, const char *last_value, const char *last_ts, - const zbx_vector_ptr_t *steps, zbx_vector_ptr_t *results, char **preproc_error, char **error) +int zbx_preprocessor_test(unsigned char value_type, const char *value, const zbx_timespec_t *ts, + const zbx_vector_ptr_t *steps, zbx_vector_ptr_t *results, zbx_vector_ptr_t *history, + char **preproc_error, char **error) { - unsigned char *data = NULL; - zbx_uint32_t size; - zbx_timespec_t ts, timestamps[2]; - int ret = FAIL, i, values_num = 0; - zbx_vector_ptr_t history; - const char *values[2]; + unsigned char *data = NULL; + zbx_uint32_t size; + int ret = FAIL; + unsigned char *result; - zbx_timespec(&ts); - zbx_vector_ptr_create(&history); + size = preprocessor_pack_test_request(&data, value_type, value, ts, history, steps); - if (NULL != last_value) + if (SUCCEED != zbx_ipc_async_exchange(ZBX_IPC_SERVICE_PREPROCESSING, ZBX_IPC_PREPROCESSOR_TEST_REQUEST, + SEC_PER_MIN, data, size, &result, error)) { - const char *ptr; - int delay; - - values[values_num] = last_value; - timestamps[values_num] = ts; - - if (0 != strncmp(last_ts, "now", ZBX_CONST_STRLEN("now"))) - { - *error = zbx_dsprintf(NULL, "invalid history value timestamp: %s", last_ts); - goto out; - } - - ptr = last_ts + ZBX_CONST_STRLEN("now"); - - if ('\0' != *ptr) - { - if ('-' != *ptr || FAIL == is_time_suffix(ptr + 1, &delay, strlen(ptr + 1))) - { - *error = zbx_dsprintf(NULL, "invalid history value timestamp: %s", last_ts); - goto out; - } - - timestamps[values_num].sec -= delay; - } - values_num++; + goto out; } - values[values_num] = value; - timestamps[values_num++] = ts; - - for (i = 0; i < values_num; i++) - { - unsigned char *result; - - zbx_free(data); - size = preprocessor_pack_test_request(&data, value_type, values[i], ×tamps[i], &history, steps); - - if (SUCCEED != zbx_ipc_async_exchange(ZBX_IPC_SERVICE_PREPROCESSING, ZBX_IPC_PREPROCESSOR_TEST_REQUEST, - SEC_PER_MIN, data, size, &result, error)) - { - goto out; - } - - zbx_vector_ptr_clear_ext(results, (zbx_clean_func_t)zbx_preproc_result_free); - zbx_free(*preproc_error); - zbx_preprocessor_unpack_test_result(results, &history, preproc_error, result); - zbx_free(result); - } + zbx_preprocessor_unpack_test_result(results, history, preproc_error, result); + zbx_free(result); ret = SUCCEED; out: - zbx_vector_ptr_clear_ext(&history, (zbx_clean_func_t)zbx_preproc_op_history_free); - zbx_vector_ptr_destroy(&history); - zbx_free(data); return ret; } + diff --git a/src/zabbix_server/trapper/trapper_preproc.c b/src/zabbix_server/trapper/trapper_preproc.c index c680323ae57..f3db01c10a3 100644 --- a/src/zabbix_server/trapper/trapper_preproc.c +++ b/src/zabbix_server/trapper/trapper_preproc.c @@ -23,112 +23,138 @@ #include "zbxalgo.h" #include "preproc.h" #include "trapper_preproc.h" +#include "../preprocessor/preproc_history.h" /****************************************************************************** * * - * Function: zbx_trapper_preproc_test * + * Function: trapper_parse_preproc_test * * * - * Purpose: processes preprocessing test request * + * Purpose: parses preprocessing test request * * * - * Parameters: sock - [IN] the request source socket (frontend) * - * jp - [IN] the request * + * Parameters: jp - [IN] the request * + * values - [OUT] the values to test optional * + * (history + current) * + * ts - [OUT] value timestamps * + * values_num - [OUT] the number of values * + * value_type - [OUT] the value type * + * steps - [OUT] the preprocessing steps * + * error - [OUT] the error message * * * - * Return value: SUCCEED - the request was processed successfully * + * Return value: SUCCEED - the request was parsed successfully * * FAIL - otherwise * * * - * Comments: This function will send proper (success/fail) response to the * - * request socket. * - * Preprocessing failure (error returned by a preprocessing step) * - * is counted as successful test and will return success response. * - * * ******************************************************************************/ -int zbx_trapper_preproc_test(zbx_socket_t *sock, const struct zbx_json_parse *jp) +static int trapper_parse_preproc_test(const struct zbx_json_parse *jp, char **values, zbx_timespec_t *ts, + int *values_num, unsigned char *value_type, zbx_vector_ptr_t *steps, int *single, char **error) { - char buffer[MAX_STRING_LEN], *error = NULL, *value = NULL, *last_value = NULL, - *last_ts = NULL, *step_params = NULL, *error_handler_params = NULL, - *preproc_error = NULL; + char buffer[MAX_STRING_LEN], *step_params = NULL, *error_handler_params = NULL; + const char *ptr; zbx_user_t user; - int ret = FAIL, i; + int ret = FAIL; struct zbx_json_parse jp_data, jp_history, jp_steps, jp_step; size_t size; - unsigned char value_type, step_type, error_handler; - zbx_vector_ptr_t steps, results; - const char *pnext; - struct zbx_json json; - - zbx_vector_ptr_create(&steps); - zbx_vector_ptr_create(&results); + zbx_timespec_t ts_now; if (FAIL == zbx_json_value_by_name(jp, ZBX_PROTO_TAG_SID, buffer, sizeof(buffer)) || SUCCEED != DBget_user_by_active_session(buffer, &user) || USER_TYPE_ZABBIX_ADMIN > user.type) { - error = zbx_strdup(NULL, "Permission denied."); + *error = zbx_strdup(NULL, "Permission denied."); goto out; } if (FAIL == zbx_json_brackets_by_name(jp, ZBX_PROTO_TAG_DATA, &jp_data)) { - error = zbx_strdup(NULL, "Missing data field."); - goto out; - } - - size = 0; - if (FAIL == zbx_json_value_by_name_dyn(&jp_data, ZBX_PROTO_TAG_VALUE, &value, &size)) - { - error = zbx_strdup(NULL, "Missing value field."); + *error = zbx_strdup(NULL, "Missing data field."); goto out; } if (FAIL == zbx_json_value_by_name(&jp_data, ZBX_PROTO_TAG_VALUE_TYPE, buffer, sizeof(buffer))) { - error = zbx_strdup(NULL, "Missing value type field."); + *error = zbx_strdup(NULL, "Missing value type field."); goto out; } - value_type = atoi(buffer); + *value_type = atoi(buffer); + + if (FAIL == zbx_json_value_by_name(&jp_data, ZBX_PROTO_TAG_SINGLE, buffer, sizeof(buffer))) + *single = 0; + else + *single = (0 == strcmp(buffer, "true") ? 1 : 0); + zbx_timespec(&ts_now); if (SUCCEED == zbx_json_brackets_by_name(&jp_data, ZBX_PROTO_TAG_HISTORY, &jp_history)) { size = 0; - if (FAIL == zbx_json_value_by_name_dyn(&jp_history, ZBX_PROTO_TAG_VALUE, &last_value, &size)) + if (FAIL == zbx_json_value_by_name_dyn(&jp_history, ZBX_PROTO_TAG_VALUE, values, &size)) { - error = zbx_strdup(NULL, "Missing history value field."); + *error = zbx_strdup(NULL, "Missing history value field."); goto out; } + (*values_num)++; - size = 0; - if (FAIL == zbx_json_value_by_name_dyn(&jp_history, ZBX_PROTO_TAG_TIMESTAMP, &last_ts, &size)) + if (FAIL == zbx_json_value_by_name(&jp_history, ZBX_PROTO_TAG_TIMESTAMP, buffer, sizeof(buffer))) + { + *error = zbx_strdup(NULL, "Missing history timestamp field."); + goto out; + } + + if (0 != strncmp(buffer, "now", ZBX_CONST_STRLEN("now"))) { - error = zbx_strdup(NULL, "Missing history timestamp field."); + *error = zbx_dsprintf(NULL, "invalid history value timestamp: %s", buffer); goto out; } + + ts[0] = ts_now; + ptr = buffer + ZBX_CONST_STRLEN("now"); + + if ('\0' != *ptr) + { + int delay; + + if ('-' != *ptr || FAIL == is_time_suffix(ptr + 1, &delay, strlen(ptr + 1))) + { + *error = zbx_dsprintf(NULL, "invalid history value timestamp: %s", buffer); + goto out; + } + + ts[0].sec -= delay; + } + } + + size = 0; + if (FAIL == zbx_json_value_by_name_dyn(&jp_data, ZBX_PROTO_TAG_VALUE, &values[*values_num], &size)) + { + *error = zbx_strdup(NULL, "Missing value field."); + goto out; } + ts[(*values_num)++] = ts_now; if (FAIL == zbx_json_brackets_by_name(&jp_data, ZBX_PROTO_TAG_STEPS, &jp_steps)) { - error = zbx_strdup(NULL, "Missing preprocessing steps field."); + *error = zbx_strdup(NULL, "Missing preprocessing steps field."); goto out; } - for (pnext = NULL; NULL != (pnext = zbx_json_next(&jp_steps, pnext));) + for (ptr = NULL; NULL != (ptr = zbx_json_next(&jp_steps, ptr));) { zbx_preproc_op_t *step; + unsigned char step_type, error_handler; - if (FAIL == zbx_json_brackets_open(pnext, &jp_step)) + if (FAIL == zbx_json_brackets_open(ptr, &jp_step)) { - error = zbx_strdup(NULL, "Cannot parse preprocessing step."); + *error = zbx_strdup(NULL, "Cannot parse preprocessing step."); goto out; } if (FAIL == zbx_json_value_by_name(&jp_step, ZBX_PROTO_TAG_TYPE, buffer, sizeof(buffer))) { - error = zbx_strdup(NULL, "Missing preprocessing step type field."); + *error = zbx_strdup(NULL, "Missing preprocessing step type field."); goto out; } step_type = atoi(buffer); if (FAIL == zbx_json_value_by_name(&jp_step, ZBX_PROTO_TAG_ERROR_HANDLER, buffer, sizeof(buffer))) { - error = zbx_strdup(NULL, "Missing preprocessing step type error handler field."); + *error = zbx_strdup(NULL, "Missing preprocessing step type error handler field."); goto out; } error_handler = atoi(buffer); @@ -136,7 +162,7 @@ int zbx_trapper_preproc_test(zbx_socket_t *sock, const struct zbx_json_parse *jp size = 0; if (FAIL == zbx_json_value_by_name_dyn(&jp_step, ZBX_PROTO_TAG_PARAMS, &step_params, &size)) { - error = zbx_strdup(NULL, "Missing preprocessing step type params field."); + *error = zbx_strdup(NULL, "Missing preprocessing step type params field."); goto out; } @@ -144,7 +170,7 @@ int zbx_trapper_preproc_test(zbx_socket_t *sock, const struct zbx_json_parse *jp if (FAIL == zbx_json_value_by_name_dyn(&jp_step, ZBX_PROTO_TAG_ERROR_HANDLER_PARAMS, &error_handler_params, &size)) { - error = zbx_strdup(NULL, "Missing preprocessing step type error handler params field."); + *error = zbx_strdup(NULL, "Missing preprocessing step type error handler params field."); goto out; } @@ -153,74 +179,146 @@ int zbx_trapper_preproc_test(zbx_socket_t *sock, const struct zbx_json_parse *jp step->params = step_params; step->error_handler = error_handler; step->error_handler_params = error_handler_params; - zbx_vector_ptr_append(&steps, step); + zbx_vector_ptr_append(steps, step); step_params = NULL; error_handler_params = NULL; } - if (FAIL == zbx_preprocessor_test(value_type, value, last_value, last_ts, &steps, &results, &preproc_error, - &error)) + ret = SUCCEED; +out: + if (FAIL == ret) { + zbx_vector_ptr_clear_ext(steps, (zbx_clean_func_t)zbx_preproc_op_free); + zbx_free(values[0]); + zbx_free(values[1]); + } + + zbx_free(step_params); + zbx_free(error_handler_params); + + return ret; +} + +/****************************************************************************** + * * + * Function: zbx_trapper_preproc_test_execute * + * * + * Purpose: executes preprocessing test request * + * * + * Parameters: jp - [IN] the request * + * json - [OUT] the output json * + * error - [OUT] the error message * + * * + * Return value: SUCCEED - the request was executed successfully * + * FAIL - otherwise * + * * + * Comments: This function will fail if the request format is not valid or * + * there was connection (to preprocessing manager) error. * + * Any errors in the preprocessing itself are reported in output * + * json and success is returned. * + * * + ******************************************************************************/ +int zbx_trapper_preproc_test_run(const struct zbx_json_parse *jp, struct zbx_json *json, char **error) +{ + char *values[2] = {NULL, NULL}, *preproc_error = NULL; + int ret = FAIL, i, values_num = 0, single; + unsigned char value_type; + zbx_vector_ptr_t steps, results, history; + zbx_timespec_t ts[2]; + zbx_preproc_result_t *result; + + zbx_vector_ptr_create(&steps); + zbx_vector_ptr_create(&results); + zbx_vector_ptr_create(&history); + + if (FAIL == trapper_parse_preproc_test(jp, values, ts, &values_num, &value_type, &steps, &single, error)) goto out; + + for (i = 0; i < values_num; i++) + { + zbx_vector_ptr_clear_ext(&results, (zbx_clean_func_t)zbx_preproc_result_free); + if (FAIL == zbx_preprocessor_test(value_type, values[i], &ts[i], &steps, &results, &history, + &preproc_error, error)) + { + goto out; + } + + if (NULL != preproc_error) + break; + + if (0 == single) + { + result = (zbx_preproc_result_t *)results.values[results.values_num - 1]; + if (ZBX_VARIANT_NONE != result->value.type && + FAIL == zbx_variant_to_value_type(&result->value, value_type, &preproc_error)) + { + break; + } + } } - zbx_json_init(&json, results.values_num * 256); + zbx_json_addstring(json, ZBX_PROTO_TAG_RESPONSE, "success", ZBX_JSON_TYPE_STRING); + zbx_json_addobject(json, ZBX_PROTO_TAG_DATA); - zbx_json_addstring(&json, ZBX_PROTO_TAG_RESPONSE, "success", ZBX_JSON_TYPE_STRING); - zbx_json_addarray(&json, ZBX_PROTO_TAG_DATA); + if (i + 1 < values_num) + zbx_json_addstring(json, ZBX_PROTO_TAG_PREVIOUS, "true", ZBX_JSON_TYPE_INT); + zbx_json_addarray(json, ZBX_PROTO_TAG_STEPS); for (i = 0; i < results.values_num; i++) { - zbx_preproc_result_t *result = (zbx_preproc_result_t *)results.values[i]; + result = (zbx_preproc_result_t *)results.values[i]; - zbx_json_addobject(&json, NULL); + zbx_json_addobject(json, NULL); if (NULL != result->error) - zbx_json_addstring(&json, ZBX_PROTO_TAG_ERROR, result->error, ZBX_JSON_TYPE_STRING); + zbx_json_addstring(json, ZBX_PROTO_TAG_ERROR, result->error, ZBX_JSON_TYPE_STRING); if (ZBX_PREPROC_FAIL_DEFAULT != result->action) - zbx_json_adduint64(&json, ZBX_PROTO_TAG_ACTION, result->action); + zbx_json_adduint64(json, ZBX_PROTO_TAG_ACTION, result->action); - if (i == results.values_num - 1 && NULL != preproc_error) + if (i == results.values_num - 1 && NULL != result->error) { if (ZBX_PREPROC_FAIL_SET_ERROR == result->action) - zbx_json_addstring(&json, ZBX_PROTO_TAG_FAILED, preproc_error, ZBX_JSON_TYPE_STRING); + zbx_json_addstring(json, ZBX_PROTO_TAG_FAILED, preproc_error, ZBX_JSON_TYPE_STRING); } - else + + if (ZBX_VARIANT_NONE != result->value.type) { - if (ZBX_VARIANT_NONE != result->value.type) - { - zbx_json_addstring(&json, ZBX_PROTO_TAG_RESULT, zbx_variant_value_desc(&result->value), - ZBX_JSON_TYPE_STRING); - } - else - zbx_json_addstring(&json, ZBX_PROTO_TAG_RESULT, NULL, ZBX_JSON_TYPE_NULL); + zbx_json_addstring(json, ZBX_PROTO_TAG_RESULT, zbx_variant_value_desc(&result->value), + ZBX_JSON_TYPE_STRING); } + else if (NULL == result->error || ZBX_PREPROC_FAIL_DISCARD_VALUE == result->action) + zbx_json_addstring(json, ZBX_PROTO_TAG_RESULT, NULL, ZBX_JSON_TYPE_NULL); - zbx_json_close(&json); + zbx_json_close(json); } + zbx_json_close(json); - zbx_tcp_send_bytes_to(sock, json.buffer, json.buffer_size, CONFIG_TIMEOUT); + if (NULL == preproc_error) + { + result = (zbx_preproc_result_t *)results.values[results.values_num - 1]; - zbx_json_free(&json); + if (ZBX_VARIANT_NONE != result->value.type) + { + zbx_json_addstring(json, ZBX_PROTO_TAG_RESULT, zbx_variant_value_desc(&result->value), + ZBX_JSON_TYPE_STRING); + } + else + zbx_json_addstring(json, ZBX_PROTO_TAG_RESULT, NULL, ZBX_JSON_TYPE_NULL); + } + else + zbx_json_addstring(json, ZBX_PROTO_TAG_ERROR, preproc_error, ZBX_JSON_TYPE_STRING); ret = SUCCEED; out: - if (FAIL == ret) - { - zbx_send_response(sock, ret, error, CONFIG_TIMEOUT); - zbx_free(error); - } + for (i = 0; i < values_num; i++) + zbx_free(values[i]); zbx_free(preproc_error); - zbx_free(error); - zbx_free(error_handler_params); - zbx_free(step_params); - zbx_free(last_ts); - zbx_free(last_value); - zbx_free(value); + zbx_vector_ptr_clear_ext(&history, (zbx_clean_func_t)zbx_preproc_op_history_free); + zbx_vector_ptr_destroy(&history); zbx_vector_ptr_clear_ext(&results, (zbx_clean_func_t)zbx_preproc_result_free); zbx_vector_ptr_destroy(&results); zbx_vector_ptr_clear_ext(&steps, (zbx_clean_func_t)zbx_preproc_op_free); @@ -228,3 +326,45 @@ out: return ret; } + +/****************************************************************************** + * * + * Function: zbx_trapper_preproc_test * + * * + * Purpose: processes preprocessing test request * + * * + * Parameters: sock - [IN] the request source socket (frontend) * + * jp - [IN] the request * + * * + * Return value: SUCCEED - the request was processed successfully * + * FAIL - otherwise * + * * + * Comments: This function will send proper (success/fail) response to the * + * request socket. * + * Preprocessing failure (error returned by a preprocessing step) * + * is counted as successful test and will return success response. * + * * + ******************************************************************************/ +int zbx_trapper_preproc_test(zbx_socket_t *sock, const struct zbx_json_parse *jp) +{ + char *error = NULL; + int ret; + struct zbx_json json; + + zbx_json_init(&json, 1024); + + if (SUCCEED == (ret = zbx_trapper_preproc_test_run(jp, &json, &error))) + { + zbx_tcp_send_bytes_to(sock, json.buffer, json.buffer_size, CONFIG_TIMEOUT); + } + else + { + zbx_send_response(sock, ret, error, CONFIG_TIMEOUT); + zbx_free(error); + } + + zbx_json_free(&json); + + return ret; +} + diff --git a/tests/zabbix_server/Makefile.am b/tests/zabbix_server/Makefile.am index 48defec4f66..013cdf6f410 100644 --- a/tests/zabbix_server/Makefile.am +++ b/tests/zabbix_server/Makefile.am @@ -1,2 +1,3 @@ SUBDIRS = \ - preprocessor + preprocessor \ + trapper diff --git a/tests/zabbix_server/preprocessor/Makefile.am b/tests/zabbix_server/preprocessor/Makefile.am index 6b7719e104c..032f2cbd215 100644 --- a/tests/zabbix_server/preprocessor/Makefile.am +++ b/tests/zabbix_server/preprocessor/Makefile.am @@ -13,6 +13,7 @@ COMMON_SRC_FILES = \ JSON_LIBS = \ $(top_srcdir)/tests/libzbxmocktest.a \ $(top_srcdir)/tests/libzbxmockdata.a \ + $(top_srcdir)/src/zabbix_server/preprocessor/libpreprocessor.a \ $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ @@ -32,7 +33,6 @@ JSON_LIBS = \ $(top_srcdir)/tests/libzbxmockdata.a zbx_item_preproc_SOURCES = \ - ../../../src/zabbix_server/preprocessor/item_preproc.c \ zbx_item_preproc.c zbx_item_preproc_LDADD = $(JSON_LIBS) diff --git a/tests/zabbix_server/trapper/Makefile.am b/tests/zabbix_server/trapper/Makefile.am new file mode 100644 index 00000000000..22650e761d4 --- /dev/null +++ b/tests/zabbix_server/trapper/Makefile.am @@ -0,0 +1,49 @@ +if SERVER +SERVER_tests = zbx_trapper_preproc_test_run + +noinst_PROGRAMS = $(SERVER_tests) + +COMMON_SRC_FILES = \ + ../../zbxmocktest.h + +TRAPPER_LIBS = \ + $(top_srcdir)/tests/libzbxmocktest.a \ + $(top_srcdir)/tests/libzbxmockdata.a \ + $(top_srcdir)/src/zabbix_server/preprocessor/libpreprocessor.a \ + $(top_srcdir)/src/libs/zbxipcservice/libzbxipcservice.a \ + $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ + $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ + $(top_srcdir)/src/libs/zbxjson/libzbxjson.a \ + $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ + $(top_srcdir)/src/libs/zbxcomms/libzbxcomms.a \ + $(top_srcdir)/src/libs/zbxcommshigh/libzbxcommshigh.a \ + $(top_srcdir)/src/libs/zbxcompress/libzbxcompress.a \ + $(top_srcdir)/src/libs/zbxcommon/libzbxcommon.a \ + $(top_srcdir)/src/libs/zbxregexp/libzbxregexp.a \ + $(top_srcdir)/src/libs/zbxnix/libzbxnix.a \ + $(top_srcdir)/src/libs/zbxcrypto/libzbxcrypto.a \ + $(top_srcdir)/src/libs/zbxsys/libzbxsys.a \ + $(top_srcdir)/src/libs/zbxlog/libzbxlog.a \ + $(top_srcdir)/src/libs/zbxsys/libzbxsys.a \ + $(top_srcdir)/src/libs/zbxconf/libzbxconf.a \ + $(top_srcdir)/src/libs/zbxembed/libzbxembed.a \ + $(top_srcdir)/src/libs/zbxprometheus/libzbxprometheus.a \ + $(top_srcdir)/src/libs/zbxalgo/libzbxalgo.a \ + $(top_srcdir)/tests/libzbxmockdata.a + +zbx_trapper_preproc_test_run_SOURCES = \ + zbx_trapper_preproc_test_run.c \ + ../../../src/zabbix_server/trapper/trapper_preproc.c \ + ../../zbxmockjson.c + +zbx_trapper_preproc_test_run_LDADD = $(TRAPPER_LIBS) +zbx_trapper_preproc_test_run_LDADD += @SERVER_LIBS@ +zbx_trapper_preproc_test_run_LDFLAGS = @SERVER_LDFLAGS@ + +zbx_trapper_preproc_test_run_CFLAGS = \ + -I@top_srcdir@/tests @LIBXML2_CFLAGS@ \ + -Wl,--wrap=zbx_preprocessor_test \ + -Wl,--wrap=DBget_user_by_active_session + +endif + diff --git a/tests/zabbix_server/trapper/zbx_trapper_preproc_test_run.c b/tests/zabbix_server/trapper/zbx_trapper_preproc_test_run.c new file mode 100644 index 00000000000..822f55b9cdf --- /dev/null +++ b/tests/zabbix_server/trapper/zbx_trapper_preproc_test_run.c @@ -0,0 +1,128 @@ +/* +** Zabbix +** Copyright (C) 2001-2019 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. +**/ + +#include "zbxmocktest.h" +#include "zbxmockdata.h" +#include "zbxmockassert.h" +#include "zbxmockutil.h" +#include "zbxmockjson.h" + +#include "common.h" +#include "zbxjson.h" +#include "dbcache.h" +#include "zbxembed.h" +#include "log.h" +#include "preproc.h" +#include "../../../src/zabbix_server/preprocessor/item_preproc.h" +#include "../../../src/zabbix_server/preprocessor/preproc_history.h" + +zbx_es_t es_engine; + +int zbx_trapper_preproc_test_run(const struct zbx_json_parse *jp, struct zbx_json *json, char **error); + +int __wrap_zbx_preprocessor_test(unsigned char value_type, const char *value, const zbx_timespec_t *ts, + const zbx_vector_ptr_t *steps, zbx_vector_ptr_t *results, zbx_vector_ptr_t *history, + char **preproc_error, char **error) +{ + int i, results_num; + zbx_preproc_op_t *steps_array; + zbx_preproc_result_t *results_array, *result; + zbx_vector_ptr_t history_out; + zbx_variant_t value_var; + + ZBX_UNUSED(error); + + zbx_vector_ptr_create(&history_out); + zbx_variant_set_str(&value_var, zbx_strdup(NULL, value)); + + steps_array = (zbx_preproc_op_t *)zbx_malloc(NULL, steps->values_num * sizeof(zbx_preproc_op_t)); + for (i = 0; i < steps->values_num; i++) + steps_array[i] = *(zbx_preproc_op_t *)steps->values[i]; + + results_array = (zbx_preproc_result_t *)zbx_malloc(NULL, sizeof(zbx_preproc_result_t) * steps->values_num); + memset(results_array, 0, sizeof(zbx_preproc_result_t) * steps->values_num); + + zbx_item_preproc_test(value_type, &value_var, ts, steps_array, steps->values_num, history, &history_out, + results_array, &results_num, preproc_error); + + /* copy output history */ + zbx_vector_ptr_clear_ext(history, (zbx_clean_func_t)zbx_preproc_op_history_free); + + if (0 != history_out.values_num) + zbx_vector_ptr_append_array(history, history_out.values, history_out.values_num); + + /* copy results */ + for (i = 0; i < results_num; i++) + { + result = (zbx_preproc_result_t *)zbx_malloc(NULL, sizeof(zbx_preproc_result_t)); + *result = results_array[i]; + zbx_vector_ptr_append(results, result); + } + + zbx_variant_clear(&value_var); + zbx_free(steps_array); + zbx_free(results_array); + zbx_vector_ptr_destroy(&history_out); + + return SUCCEED; +} + +int __wrap_DBget_user_by_active_session(const char *sessionid, zbx_user_t *user) +{ + ZBX_UNUSED(sessionid); + + user->type = USER_TYPE_ZABBIX_ADMIN; + user->userid = 0; + + return SUCCEED; +} + +void zbx_mock_test_entry(void **state) +{ + const char *request; + char *error = NULL; + struct zbx_json_parse jp; + struct zbx_json out; + int returned_ret, expected_ret; + + ZBX_UNUSED(state); + + zbx_json_init(&out, 1024); + + request = zbx_mock_get_parameter_string("in.request"); + if (FAIL == zbx_json_open(request, &jp)) + fail_msg("Invalid request format: %s", zbx_json_strerror()); + + returned_ret = zbx_trapper_preproc_test_run(&jp, &out, &error); + if (FAIL == returned_ret) + printf("zbx_trapper_preproc_test_run error: %s\n", error); + else + printf("zbx_trapper_preproc_test_run output: %s\n", out.buffer); + + expected_ret = zbx_mock_str_to_return_code(zbx_mock_get_parameter_string("out.return")); + zbx_mock_assert_result_eq("Return value", expected_ret, returned_ret); + + if (FAIL == returned_ret) + zbx_mock_assert_ptr_ne("Error pointer", NULL, error); + else + zbx_mock_assert_json_eq("Output", zbx_mock_get_parameter_string("out.response"), out.buffer); + + zbx_free(error); + zbx_json_free(&out); +} diff --git a/tests/zabbix_server/trapper/zbx_trapper_preproc_test_run.yaml b/tests/zabbix_server/trapper/zbx_trapper_preproc_test_run.yaml new file mode 100644 index 00000000000..b8403833e92 --- /dev/null +++ b/tests/zabbix_server/trapper/zbx_trapper_preproc_test_run.yaml @@ -0,0 +1,1250 @@ +--- +test case: Empty request +in: + request: | + { + } +out: + return: FAIL +--- +test case: Missing sessionid field +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + } + ], + "value_type": 0, + "value": "1" + }, + "request": "preprocessing.test" + } +out: + return: FAIL +--- +test case: Missing data field +in: + request: | + { + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: FAIL +--- +test case: Missing data.steps field +in: + request: | + { + "data": { + "value_type": 0, + "value": "1" + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: FAIL +--- +test case: Missing data.value_type field +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + } + ], + "value": "1" + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: FAIL +--- +test case: Missing data.value field +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + } + ], + "value_type": 0 + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: FAIL +--- +test case: Missing data.steps[0].error_handler_params field +in: + request: | + { + "data": { + "steps": [ + { + "params": "2", + "error_handler": 0, + "type": 1 + } + ], + "value_type": 0, + "value": "1" + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: FAIL +--- +test case: Missing data.steps[0].params field +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "error_handler": 0, + "type": 1 + } + ], + "value_type": 0, + "value": "1" + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: FAIL +--- +test case: Missing data.steps[0].error_handler field +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "2", + "type": 1 + } + ], + "value_type": 0, + "value": "1" + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: FAIL +--- +test case: Missing data.steps[0].type field +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "2", + "error_handler": 0 + } + ], + "value_type": 0, + "value": "1" + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: FAIL +--- +test case: Missing data.history.timestamp field +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + } + ], + "value_type": 0, + "value": "1", + "history": { + "value": "0" + } + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: FAIL +--- +test case: Missing data.history.value field +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + } + ], + "value_type": 0, + "value": "1", + "history": { + "timestamp": "now-1m" + } + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: FAIL +--- +test case: 'Apply trim($) + regsub("data: *(.*)", \1) + mult(2) to data: 4$' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "$", + "error_handler": 0, + "type": 4 + }, + { + "error_handler_params": "", + "params": "data: *(.*)\n\\1", + "error_handler": 0, + "type": 5 + }, + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + } + ], + "value_type": 0, + "value": "data: 4$" + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "result": "data: 4" + }, + { + "result": "4" + }, + { + "result": "8" + } + ], + "result": "8" + } + } +--- +test case: 'Apply mult(2) + trim($) + regsub("data: *(.*)", \1) to data: 4$' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + }, + { + "error_handler_params": "", + "params": "$", + "error_handler": 0, + "type": 4 + }, + { + "error_handler_params": "", + "params": "data: *(.*)\n\\1", + "error_handler": 0, + "type": 5 + } + ], + "value_type": 0, + "value": "data: 4$" + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "error": "/.*" + } + ], + "error": "/.*" + } + } +--- +test case: 'Apply trim($) + mult(2) + regsub("data: *(.*)", \1) to data: 4$' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "$", + "error_handler": 0, + "type": 4 + }, + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + }, + { + "error_handler_params": "", + "params": "data: *(.*)\n\\1", + "error_handler": 0, + "type": 5 + } + ], + "value_type": 0, + "value": "data: 4$" + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "result": "data: 4" + }, + { + "error": "/.*" + } + ], + "error": "/.*" + } + } +--- +test case: 'Apply trim($) + regsub("data: *(.*)", \1) + mult(2) to data: x$' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "$", + "error_handler": 0, + "type": 4 + }, + { + "error_handler_params": "", + "params": "data: *(.*)\n\\1", + "error_handler": 0, + "type": 5 + }, + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + } + ], + "value_type": 0, + "value": "data: x$" + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "result": "data: x" + }, + { + "result": "x" + }, + { + "error": "/.*" + } + ], + "error": "/.*" + } + } +--- +test case: 'Apply trim($) + regsub("data: *(.*)", \1) + mult(2) to 4$ with discard on fail' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "$", + "error_handler": 0, + "type": 4 + }, + { + "error_handler_params": "", + "params": "data: *(.*)\n\\1", + "error_handler": 1, + "type": 5 + }, + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + } + ], + "value_type": 0, + "value": "4$" + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "result": "4" + }, + { + "result": null, + "action": 1, + "error": "/.*" + } + ], + "result": null + } + } +--- +test case: 'Apply trim($) + regsub("data: *(.*)", \1) + mult(2) to 4$ with set value to 5 on fail' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "$", + "error_handler": 0, + "type": 4 + }, + { + "error_handler_params": "5", + "params": "data: *(.*)\n\\1", + "error_handler": 2, + "type": 5 + }, + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + } + ], + "value_type": 0, + "value": "4$" + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "result": "4" + }, + { + "result": "5", + "action": 2, + "error": "/.*" + }, + { + "result": "10" + } + ], + "result": "10" + } + } +--- +test case: 'Apply trim($) + regsub("data: *(.*)", \1) + mult(2) to 4$ with set error to Validation error on fail' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "$", + "error_handler": 0, + "type": 4 + }, + { + "error_handler_params": "Validation error", + "params": "data: *(.*)\n\\1", + "error_handler": 3, + "type": 5 + }, + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + } + ], + "value_type": 0, + "value": "4$" + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "result": "4" + }, + { + "failed": "Validation error", + "action": 3, + "error": "/[^V][^a][^l][^i][^d].*" + } + ], + "error": "Validation error" + } + } +--- +test case: 'Apply trim($) + regsub("data: *(.*)", \1) to data: x$ for uint value' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "$", + "error_handler": 0, + "type": 4 + }, + { + "error_handler_params": "", + "params": "data: *(.*)\n\\1", + "error_handler": 0, + "type": 5 + } + ], + "value_type": 3, + "value": "data: x$" + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "result": "data: x" + }, + { + "result": "x" + } + ], + "error": "/.*" + } + } +--- +test case: 'Apply regsub("data: *(.*)", \1) to data: x for uint value testing single step' +in: + request: | + { + "data": { + "steps":[ + { + "error_handler_params": "", + "params": "data: *(.*)\n\\1", + "error_handler": 0, + "type": 5 + } + ], + "value_type": 3, + "value": "data: x", + "single": true + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "result": "x" + } + ], + "result": "x" + } + } +--- +test case: 'Apply delta() to 100, 700' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "", + "error_handler": 0, + "type": 9 + } + ], + "value_type": 0, + "value": "700", + "history": { + "timestamp": "now-1m", + "value": "100" + } + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "result": "600" + } + ], + "result": "600" + } + } +--- +test case: 'Apply delta_per_second() to 100, 700' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "", + "error_handler": 0, + "type": 10 + } + ], + "value_type": 0, + "value": "700", + "history": { + "timestamp": "now-1m", + "value": "100" + } + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "result": "10" + } + ], + "result": "10" + } + } +--- +test case: 'Apply mult(2) + trim($) + regsub("data: *(.*)", \1) to history data: 4$' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + }, + { + "error_handler_params": "", + "params": "$", + "error_handler": 0, + "type": 4 + }, + { + "error_handler_params": "", + "params": "data: *(.*)\n\\1", + "error_handler": 0, + "type": 5 + } + ], + "value_type": 0, + "value": "data: 4", + "history": { + "timestamp": "now-1m", + "value": "data: 4$" + } + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "previous": true, + "steps": [ + { + "error": "/.*" + } + ], + "error": "/.*" + } + } +--- +test case: 'Apply trim($) + mult(2) + regsub("data: *(.*)", \1) to history data: 4$' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "$", + "error_handler": 0, + "type": 4 + }, + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + }, + { + "error_handler_params": "", + "params": "data: *(.*)\n\\1", + "error_handler": 0, + "type": 5 + } + ], + "value_type": 0, + "value": "data: 4$", + "history": { + "timestamp": "now-1m", + "value": "data: 4$" + } + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "previous": true, + "steps": [ + { + "result": "data: 4" + }, + { + "error": "/.*" + } + ], + "error": "/.*" + } + } +--- +test case: 'Apply trim($) + regsub("data: *(.*)", \1) + mult(2) to history data: x$' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "$", + "error_handler": 0, + "type": 4 + }, + { + "error_handler_params": "", + "params": "data: *(.*)\n\\1", + "error_handler": 0, + "type": 5 + }, + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + } + ], + "value_type": 0, + "value": "data: 4$", + "history": { + "timestamp": "now-1m", + "value": "data: x$" + } + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "previous": true, + "steps": [ + { + "result": "data: x" + }, + { + "result": "x" + }, + { + "error": "/.*" + } + ], + "error": "/.*" + } + } +--- +test case: 'Apply trim($) + regsub("data: *(.*)", \1) + mult(2) to history 4$ with discard on fail' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "$", + "error_handler": 0, + "type": 4 + }, + { + "error_handler_params": "", + "params": "data: *(.*)\n\\1", + "error_handler": 1, + "type": 5 + }, + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + } + ], + "value_type": 0, + "value": "data: 3", + "history": { + "timestamp": "now-1m", + "value": "4$" + } + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "result": "data: 3" + }, + { + "result": "3" + }, + { + "result": "6" + } + ], + "result": "6" + } + } +--- +test case: 'Apply trim($) + regsub("data: *(.*)", \1) + mult(2) to history 4$ with set value to 5 on fail' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "$", + "error_handler": 0, + "type": 4 + }, + { + "error_handler_params": "5", + "params": "data: *(.*)\n\\1", + "error_handler": 2, + "type": 5 + }, + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + } + ], + "value_type": 0, + "value": "data: 3", + "history": { + "timestamp": "now-1m", + "value": "4$" + } + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "result": "data: 3" + }, + { + "result": "3" + }, + { + "result": "6" + } + ], + "result": "6" + } + } +--- +test case: 'Apply trim($) + regsub("data: *(.*)", \1) + mult(2) to history 4$ with set error to Validation error on fail' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "$", + "error_handler": 0, + "type": 4 + }, + { + "error_handler_params": "Validation error", + "params": "data: *(.*)\n\\1", + "error_handler": 3, + "type": 5 + }, + { + "error_handler_params": "", + "params": "2", + "error_handler": 0, + "type": 1 + } + ], + "value_type": 0, + "value": "4$", + "history": { + "timestamp": "now-1m", + "value": "4$" + } + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "previous": true, + "steps": [ + { + "result": "4" + }, + { + "failed": "Validation error", + "action": 3, + "error": "/[^V][^a][^l][^i][^d].*" + } + ], + "error": "Validation error" + } + } +--- +test case: 'Apply trim($) + regsub("data: *(.*)", \1) to history data: x$ for uint value' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "", + "params": "$", + "error_handler": 0, + "type": 4 + }, + { + "error_handler_params": "", + "params": "data: *(.*)\n\\1", + "error_handler": 0, + "type": 5 + } + ], + "value_type": 3, + "value": "data: 1$", + "history": { + "timestamp": "now-1m", + "value": "data: x$" + } + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "previous": true, + "steps": [ + { + "result": "data: x" + }, + { + "result": "x" + } + ], + "error": "/.*" + } + } +--- +test case: 'Apply regsub("data: *(.*)", \1) to history data: x for uint value testing single step' +in: + request: | + { + "data": { + "steps":[ + { + "error_handler_params": "", + "params": "data: *(.*)\n\\1", + "error_handler": 0, + "type": 5 + } + ], + "value_type": 3, + "value": "data: x", + "single": true, + "history": { + "timestamp": "now-1m", + "value": "data: x$" + } + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "result": "x" + } + ], + "result": "x" + } + } +--- +test case: 'Apply regsub("data: *(.*)", \1) + delta() to data: 9, data:10' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "0", + "params": "data: *(.*)\n\\1", + "error_handler": 2, + "type": 5 + }, + { + "error_handler_params": "", + "params": "", + "error_handler": 0, + "type": 9 + } + ], + "value_type": 0, + "value": "data: 10", + "history": { + "timestamp": "now-1m", + "value": "data: 9" + } + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "result": "10" + }, + { + "result": "1" + } + ], + "result": "1" + } + } +--- +test case: 'Apply regsub("data: *(.*)", \1) + delta() to data: 9, data:10' +in: + request: | + { + "data": { + "steps": [ + { + "error_handler_params": "0", + "params": "data: *(.*)\n\\1", + "error_handler": 2, + "type": 5 + }, + { + "error_handler_params": "", + "params": "", + "error_handler": 0, + "type": 9 + } + ], + "value_type": 0, + "value": "data: 10", + "history": { + "timestamp": "now-1m", + "value": "x" + } + }, + "request": "preprocessing.test", + "sid": "6ed71f17963a881bd010e63b01c39484" + } +out: + return: SUCCEED + response: | + { + "response": "success", + "data": { + "steps": [ + { + "result": "10" + }, + { + "result": "10" + } + ], + "result": "10" + } + } +... diff --git a/tests/zbxmockjson.c b/tests/zbxmockjson.c new file mode 100644 index 00000000000..fda1e0f0ccf --- /dev/null +++ b/tests/zbxmockjson.c @@ -0,0 +1,259 @@ +/* +** Zabbix +** Copyright (C) 2001-2019 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. +**/ + +#include "zbxalgo.h" +#include "zbxjson.h" +#include "zbxregexp.h" + +#include "zbxmocktest.h" +#include "zbxmockdata.h" +#include "zbxmockassert.h" + +#define _FAIL(file, line, prefix, message, ...) \ + \ +do \ +{ \ + cm_print_error("%s%s" message "\n", (NULL != prefix_msg ? prefix_msg : ""), \ + (NULL != prefix_msg && '\0' != *prefix_msg ? ": " : ""), \ + __VA_ARGS__); \ + _fail(file, line); \ +} \ +while(0) + +void cm_print_error(const char * const format, ...); + +static void json_flatten_contents(struct zbx_json_parse *jp, const char *prefix, zbx_vector_ptr_pair_t *props); + +static void json_append_prop(zbx_vector_ptr_pair_t *props, const char *key, const char *value) +{ + zbx_ptr_pair_t pair; + + pair.first = zbx_strdup(NULL, key); + pair.second = zbx_strdup(NULL, value); + zbx_vector_ptr_pair_append_ptr(props, &pair); +} + +static int json_compare_props(const void *d1, const void *d2) +{ + const zbx_ptr_pair_t *p1 = (const zbx_ptr_pair_t *)d1; + const zbx_ptr_pair_t *p2 = (const zbx_ptr_pair_t *)d2; + + return strcmp((const char *)p1->first, (const char *)p2->first); +} + +static void json_flatten_value(const char *ptr, const char *path, zbx_vector_ptr_pair_t *props) +{ + struct zbx_json_parse jp_value; + char *value = NULL; + size_t value_alloc = 0; + zbx_json_type_t type; + + if (FAIL == zbx_json_brackets_open(ptr, &jp_value)) + { + zbx_json_decodevalue_dyn(ptr, &value, &value_alloc, &type); + json_append_prop(props, path, (ZBX_JSON_TYPE_NULL != type ? value : "null")); + } + else + json_flatten_contents(&jp_value, path, props); + + zbx_free(value); +} + +static int json_quote_key(char *key, size_t size) +{ + char *ptr, *out; + int quotes = 0, dot = 1; + + for (ptr = key; '\0' != *ptr; ptr++) + { + if ('\'' == *ptr || '\\' == *ptr) + quotes++; + + if (0 == isalnum((unsigned char)*ptr) && '_' != *ptr && '-' != *ptr) + dot = 0; + } + + if (1 == dot) + return FAIL; + + if (0 == quotes) + return SUCCEED; + + if (ptr - key + quotes + 1 > (int)size) + fail_msg("The hardcoded 2k limit exceeded by JSON key: %s", key); + + for (out = ptr + quotes; ptr != key; ptr--) + { + *out-- = *ptr; + if ('\'' == *ptr || '\\' == *ptr) + *out-- = '\\'; + } + + return SUCCEED; +} + +static void json_flatten_object(struct zbx_json_parse *jp, const char *prefix, zbx_vector_ptr_pair_t *props) +{ + const char *pnext = NULL; + char *path = NULL, key[MAX_STRING_LEN]; + size_t path_alloc = 0, path_offset = 0; + + while (NULL != (pnext = zbx_json_pair_next(jp, pnext, key, sizeof(key)))) + { + path_offset = 0; + if (SUCCEED == json_quote_key(key, sizeof(key))) + zbx_snprintf_alloc(&path, &path_alloc, &path_offset, "%s['%s']", prefix, key); + else + zbx_snprintf_alloc(&path, &path_alloc, &path_offset, "%s.%s", prefix, key); + + json_flatten_value(pnext, path, props); + } + zbx_free(path); +} + +static void json_flatten_array(struct zbx_json_parse *jp, const char *parent, zbx_vector_ptr_pair_t *props) +{ + const char *pnext; + char *path, *value = NULL; + int index = 0; + + + for (pnext = NULL; NULL != (pnext = zbx_json_next(jp, pnext));) + { + path = zbx_dsprintf(NULL, "%s[%d]", parent, index++); + json_flatten_value(pnext, path, props); + zbx_free(path); + } + + zbx_free(value); +} + +static void json_flatten_contents(struct zbx_json_parse *jp, const char *prefix, zbx_vector_ptr_pair_t *props) +{ + if ('{' == *jp->start) + json_flatten_object(jp, prefix, props); + else if ('[' == *jp->start) + json_flatten_array(jp, prefix, props); +} + +/****************************************************************************** + * * + * Function: json_flatten * + * * + * Purpose: flattens json into vector of key (json path), value pairs, sorted * + * by keys * + * * + ******************************************************************************/ +static void json_flatten(struct zbx_json_parse *jp, zbx_vector_ptr_pair_t *props) +{ + json_flatten_contents(jp, "$", props); + zbx_vector_ptr_pair_sort(props, json_compare_props); +} + +/****************************************************************************** + * * + * Function: __zbx_mock_assert_json_eq * + * * + * Purpose: compares returned json with expected * + * * + * Comments: The comparison is done by first flattening both jsons into * + * key(jsonpath)-value pairs, sorting by keys and then comparing * + * the resulting lists. * + * If expected value starts with / then regular expression match is * + * performed. * + * * + ******************************************************************************/ +void __zbx_mock_assert_json_eq(const char *file, int line, const char *prefix_msg, const char *expected_value, + const char *returned_value) +{ + struct zbx_json_parse jp_expected, jp_returned; + zbx_vector_ptr_pair_t props_expected, props_returned; + int i, props_num; + + if (FAIL == zbx_json_open(expected_value, &jp_expected)) + _FAIL(file, line, prefix_msg, "Expected value is not a valid JSON object: %s", zbx_json_strerror()); + + if (FAIL == zbx_json_open(returned_value, &jp_returned)) + _FAIL(file, line, prefix_msg, "Returned value is not a valid JSON object: %s", zbx_json_strerror()); + + zbx_vector_ptr_pair_create(&props_expected); + zbx_vector_ptr_pair_create(&props_returned); + + json_flatten(&jp_expected, &props_expected); + json_flatten(&jp_returned, &props_returned); + + props_num = MIN(props_expected.values_num, props_returned.values_num); + + for (i = 0; i < props_num; i++) + { + zbx_ptr_pair_t *pair_expected = &props_expected.values[i]; + zbx_ptr_pair_t *pair_returned = &props_returned.values[i]; + + if (0 != strcmp(pair_expected->first, pair_returned->first)) + { + _FAIL(file, line, prefix_msg, "Expected key \"%s\" while got \"%s\"", pair_expected->first, + pair_returned->first); + } + + if ('/' == *(char *)pair_expected->second) + { + char *pattern = (char *)pair_expected->second + 1; + if (NULL == zbx_regexp_match(pair_returned->second, pattern, NULL)) + { + _FAIL(file, line, prefix_msg, "Key \"%s\" value \"%s\" does not match pattern \"%s\"", + pair_returned->first, pair_returned->second, pattern); + } + } + else + { + if (0 != strcmp(pair_expected->second, pair_returned->second)) + { + _FAIL(file, line, prefix_msg, "Expected key \"%s\" value \"%s\" while got \"%s\"", + pair_expected->first, pair_expected->second, pair_returned->second); + + } + } + } + + if (i < props_expected.values_num) + { + zbx_ptr_pair_t *pair_expected = &props_expected.values[i]; + _FAIL(file, line, prefix_msg, "Expected key \"%s\" while got nothing", pair_expected->first); + } + + if (i < props_returned.values_num) + { + zbx_ptr_pair_t *pair_returned = &props_returned.values[i]; + _FAIL(file, line, prefix_msg, "Did not expect key \"%s\"", pair_returned->first); + } + + for (i = 0; i < props_expected.values_num; i++) + { + zbx_free(props_expected.values[i].first); + zbx_free(props_expected.values[i].second); + } + zbx_vector_ptr_pair_destroy(&props_expected); + + for (i = 0; i < props_returned.values_num; i++) + { + zbx_free(props_returned.values[i].first); + zbx_free(props_returned.values[i].second); + } + zbx_vector_ptr_pair_destroy(&props_returned); +} diff --git a/tests/zbxmockjson.h b/tests/zbxmockjson.h new file mode 100644 index 00000000000..9e4d4045fb8 --- /dev/null +++ b/tests/zbxmockjson.h @@ -0,0 +1,29 @@ +/* +** Zabbix +** Copyright (C) 2001-2019 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. +**/ + +#ifndef ZABBIX_MOCK_JSON_H +#define ZABBIX_MOCK_JSON_H + +void __zbx_mock_assert_json_eq(const char *file, int line, const char *prefix_msg, const char *expected_value, + const char *returned_value); + +#define zbx_mock_assert_json_eq(prefix_msg, expected_value, returned_value) \ + __zbx_mock_assert_json_eq(__FILE__, __LINE__, prefix_msg, expected_value, returned_value) + +#endif /* BUILD_TESTS_ZBXMOCKDB_H_ */ |