Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/zabbix/zabbix.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorAlexander Shubin <aleksandrs.subins@zabbix.com>2021-05-10 17:49:31 +0300
committerAlexander Shubin <aleksandrs.subins@zabbix.com>2021-05-10 17:49:31 +0300
commit6884c1c7a2e9debd41f149f168b87250a249c0c6 (patch)
tree4dafc24a5ee5c70d3dc808eede06d8bf09768c43 /ui
parent1ca32e7b027871a26432c1b9d2a5883740bd6231 (diff)
parentafed3e3c3669bec757c1b5eb92968f87ea1c9278 (diff)
A...I..... [ZBXNEXT-6411] updated to latest from master; resolved conflicts in:
# create/src/schema.tmpl # src/libs/zbxdbupgrade/dbupgrade_5030.c # templates/app/activemq_jmx/template_app_activemq_jmx.yaml # templates/app/apache_agent/template_app_apache_agent.yaml # templates/app/apache_http/template_app_apache_http.yaml # templates/app/aranet/aranet_cloud.yaml # templates/app/ceph_agent2/template_app_ceph_agent2.yaml # templates/app/docker/template_app_docker.yaml # templates/app/elasticsearch_http/template_app_elasticsearch_http.yaml # templates/app/etcd_http/template_app_etcd_http.yaml # templates/app/exchange/template_app_exchange.yaml # templates/app/exchange_active/template_app_exchange_active.yaml # templates/app/generic_java_jmx/template_app_generic_java_jmx.yaml # templates/app/gitlab_http/template_app_gitlab_http.yaml # templates/app/hadoop_http/template_app_hadoop_http.yaml # templates/app/haproxy_agent/template_app_haproxy_agent.yaml # templates/app/haproxy_http/template_app_haproxy_http.yaml # templates/app/iis_agent/template_app_iis_agent.yaml # templates/app/iis_agent_active/template_app_iis_agent_active.yaml # templates/app/jenkins/template_app_jenkins.yaml # templates/app/kafka_jmx/template_app_kafka_jmx.yaml # templates/app/memcached/template_app_memcached.yaml # templates/app/nginx_agent/template_app_nginx_agent.yaml # templates/app/nginx_http/template_app_nginx_http.yaml # templates/app/php-fpm_agent/template_app_php-fpm_agent.yaml # templates/app/php-fpm_http/template_app_php-fpm_http.yaml # templates/app/rabbitmq_agent/template_app_rabbitmq_agent.yaml # templates/app/rabbitmq_http/template_app_rabbitmq_http.yaml # templates/app/sharepoint_http/template_app_sharepoint_http.yaml # templates/app/squid_snmp/template_app_squid_snmp.yaml # templates/app/tomcat_jmx/template_app_tomcat_jmx.yaml # templates/app/vault_http/template_app_vault.yaml # templates/app/vmware/template_app_vmware.yaml # templates/app/vmware_fqdn/template_app_vmware_fqdn.yaml # templates/app/wildfly_domain_jmx/template_app_wildfly_domain_jmx.yaml # templates/app/wildfly_server_jmx/template_app_wildfly_server_jmx.yaml # templates/app/zookeeper_http/template_app_zookeeper_http.yaml # templates/cctv/hikvision/template_cctv_hikvision_camera.yaml # templates/classic/template_app_ftp_service.yaml # templates/classic/template_app_http_service.yaml # templates/classic/template_app_https_service.yaml # templates/classic/template_app_imap_service.yaml # templates/classic/template_app_ldap_service.yaml # templates/classic/template_app_nntp_service.yaml # templates/classic/template_app_ntp_service.yaml # templates/classic/template_app_pop_service.yaml # templates/classic/template_app_remote_zabbix_proxy.yaml # templates/classic/template_app_remote_zabbix_server.yaml # templates/classic/template_app_smtp_service.yaml # templates/classic/template_app_ssh_service.yaml # templates/classic/template_app_telnet_service.yaml # templates/classic/template_app_zabbix_proxy.yaml # templates/classic/template_app_zabbix_server.yaml # templates/classic/template_os_aix.yaml # templates/classic/template_os_freebsd.yaml # templates/classic/template_os_hp-ux.yaml # templates/classic/template_os_mac_os_x.yaml # templates/classic/template_os_openbsd.yaml # templates/classic/template_os_solaris.yaml # templates/classic/template_server_intel_sr1530_ipmi.yaml # templates/classic/template_server_intel_sr1630_ipmi.yaml # templates/db/cassandra_jmx/template_db_cassandra_jmx.yaml # templates/db/clickhouse_http/template_db_clickhouse_http.yaml # templates/db/ignite_jmx/template_db_ignite_jmx.yaml # templates/db/mongodb/template_db_mongodb.yaml # templates/db/mongodb_cluster/template_db_mongodb_cluster.yaml # templates/db/mssql_odbc/template_db_mssql_odbc.yaml # templates/db/mysql_agent/template_db_mysql_agent.yaml # templates/db/mysql_agent2/template_db_mysql_agent2.yaml # templates/db/mysql_odbc/template_db_mysql_odbc.yaml # templates/db/oracle_agent2/template_db_oracle_agent2.yaml # templates/db/oracle_odbc/template_db_oracle_odbc.yaml # templates/db/postgresql/template_db_postgresql.yaml # templates/db/postgresql_agent2/template_db_postgresql_agent2.yaml # templates/db/redis/template_db_redis.yaml # templates/db/tidb_http/tidb_pd_http/template_db_tidb_pd_http.yaml # templates/db/tidb_http/tidb_tidb_http/template_db_tidb_tidb_http.yaml # templates/db/tidb_http/tidb_tikv_http/template_db_tidb_tikv_http.yaml # templates/module/00icmp_ping/00template_module_icmp_ping.yaml # templates/module/ether_like_snmp/template_module_ether_like_snmp.yaml # templates/module/generic_snmp_snmp/template_module_generic_snmp_snmp.yaml # templates/module/host_resources_snmp/template_module_host_resources_snmp.yaml # templates/module/interfaces_simple_snmp/template_module_interfaces_simple_snmp.yaml # templates/module/interfaces_snmp/template_module_interfaces_snmp.yaml # templates/module/interfaces_win_snmp/template_module_interfaces_win_snmp.yaml # templates/module/smart_agent2/template_module_smart_agent2.yaml # templates/module/smart_agent2_active/template_module_smart_agent2_active.yaml # templates/module/zabbix_agent/template_module_zabbix_agent.yaml # templates/net/alcatel_timetra_snmp/template_net_alcatel_timetra_snmp.yaml # templates/net/arista_snmp/template_net_arista_snmp.yaml # templates/net/brocade_fc_sw_snmp/template_net_brocade_fc_sw_snmp.yaml # templates/net/brocade_foundry_sw_snmp/template_net_brocade_foundry_sw_snmp.yaml # templates/net/cisco_catalyst_3750/cisco_catalyst_3750_24fs_snmp/template_net_cisco_catalyst_3750_24fs_snmp.yaml # templates/net/cisco_catalyst_3750/cisco_catalyst_3750_24ps_snmp/template_net_cisco_catalyst_3750_24ps_snmp.yaml # templates/net/cisco_catalyst_3750/cisco_catalyst_3750_24ts_snmp/template_net_cisco_catalyst_3750_24ts_snmp.yaml # templates/net/cisco_catalyst_3750/cisco_catalyst_3750_48ps_snmp/template_net_cisco_catalyst_3750_48ps_snmp.yaml # templates/net/cisco_catalyst_3750/cisco_catalyst_3750_48ts_snmp/template_net_cisco_catalyst_3750_48ts_snmp.yaml # templates/net/cisco_snmp/template_net_cisco_snmp.yaml # templates/net/dell_force_s_series_snmp/template_net_dell_force_s_series_snmp.yaml # templates/net/dlink_des7200_snmp/template_net_dlink_des7200_snmp.yaml # templates/net/dlink_des_snmp/template_net_dlink_des_snmp.yaml # templates/net/extreme_snmp/template_net_extreme_snmp.yaml # templates/net/hp_hh3c_snmp/template_net_hp_hh3c_snmp.yaml # templates/net/hp_hpn_snmp/template_net_hp_hpn_snmp.yaml # templates/net/huawei_snmp/template_net_huawei_snmp.yaml # templates/net/intel_qlogic_infiniband_snmp/template_net_intel_qlogic_infiniband_snmp.yaml # templates/net/juniper_snmp/template_net_juniper_snmp.yaml # templates/net/mellanox_snmp/template_net_mellanox_snmp.yaml # templates/net/mikrotik_snmp/template_net_mikrotik_snmp.yaml # templates/net/morningstar_snmp/prostar_mppt_snmp/prostar_mppt_snmp.yaml # templates/net/morningstar_snmp/prostar_pwm_snmp/prostar_pwm_snmp.yaml # templates/net/morningstar_snmp/sunsaver_mppt_snmp/sunsaver_mppt_snmp.yaml # templates/net/morningstar_snmp/suresine_snmp/suresine_snmp.yaml # templates/net/morningstar_snmp/tristar_mppt_600V_snmp/tristar_mppt_600V_snmp.yaml # templates/net/morningstar_snmp/tristar_mppt_snmp/tristar_mppt_snmp.yaml # templates/net/morningstar_snmp/tristar_pwm_snmp/tristar_pwm_snmp.yaml # templates/net/netgear_snmp/template_net_netgear_snmp.yaml # templates/net/qtech_snmp/template_net_qtech_snmp.yaml # templates/net/tplink_snmp/template_net_tplink_snmp.yaml # templates/net/ubiquiti_airos_snmp/template_net_ubiquiti_airos_snmp.yaml # templates/os/linux/template_os_linux.yaml # templates/os/linux_active/template_os_linux_active.yaml # templates/os/linux_prom/template_os_linux_prom.yaml # templates/os/linux_snmp_snmp/template_os_linux_snmp_snmp.yaml # templates/os/windows_agent/template_os_windows_agent.yaml # templates/os/windows_agent_active/template_os_windows_agent_active.yaml # templates/power/apc/apc_ups_galaxy_3500_snmp/template_power_apc_ups_galaxy_3500_snmp.yaml # templates/power/apc/apc_ups_smart_2200_rm_snmp/template_power_apc_ups_smart_2200_rm_snmp.yaml # templates/power/apc/apc_ups_smart_3000_xlm_snmp/template_power_apc_ups_smart_3000_xlm_snmp.yaml # templates/power/apc/apc_ups_smart_rt_1000_rm_xl_snmp/template_power_apc_ups_smart_rt_1000_rm_xl_snmp.yaml # templates/power/apc/apc_ups_smart_rt_1000_xl_snmp/template_power_apc_ups_smart_rt_1000_xl_snmp.yaml # templates/power/apc/apc_ups_smart_srt_5000_snmp/template_power_apc_ups_smart_srt_5000_snmp.yaml # templates/power/apc/apc_ups_smart_srt_8000_snmp/template_power_apc_ups_smart_srt_8000_snmp.yaml # templates/power/apc/apc_ups_snmp/template_power_apc_ups_snmp.yaml # templates/power/apc/apc_ups_symmetra_lx_snmp/template_power_apc_ups_symmetra_lx_snmp.yaml # templates/power/apc/apc_ups_symmetra_rm_snmp/template_power_apc_ups_symmetra_rm_snmp.yaml # templates/power/apc/apc_ups_symmetra_rx_snmp/template_power_apc_ups_symmetra_rx_snmp.yaml # templates/san/huawei_5300v5_snmp/template_san_huawei_5300v5_snmp.yaml # templates/san/netapp_aff_a700_http/template_san_netapp_aff_a700_http.yaml # templates/san/netapp_fas3220_snmp/template_san_netapp_fas3220_snmp.yaml # templates/server/chassis_ipmi/template_server_chassis_ipmi.yaml # templates/server/cisco_ucs_snmp/template_server_cisco_ucs_snmp.yaml # templates/server/dell_idrac_snmp/template_server_dell_idrac_snmp.yaml # templates/server/hp_ilo_snmp/template_server_hp_ilo_snmp.yaml # templates/server/ibm_imm_snmp/template_server_ibm_imm_snmp.yaml # templates/server/supermicro_aten_snmp/template_server_supermicro_aten_snmp.yaml # templates/tel/asterisk_http/template_tel_asterisk_http.yaml # ui/include/classes/api/services/CValueMap.php # ui/include/classes/validators/CApiInputValidator.php # ui/include/defines.inc.php # ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php
Diffstat (limited to 'ui')
-rw-r--r--ui/app/controllers/CControllerPopupGeneric.php3
-rw-r--r--ui/app/controllers/CControllerPopupItemTestGetValue.php2
-rw-r--r--ui/app/controllers/CControllerPopupItemTestSend.php8
-rw-r--r--ui/app/controllers/CControllerPopupTriggerExpr.php821
-rw-r--r--ui/app/controllers/CControllerPopupValueMapEdit.php105
-rw-r--r--ui/app/controllers/CControllerPopupValueMapUpdate.php175
-rw-r--r--ui/app/partials/js/configuration.valuemap.js.php65
-rw-r--r--ui/app/views/js/popup.triggerexpr.js.php9
-rw-r--r--ui/app/views/js/popup.valuemap.edit.js.php49
-rw-r--r--ui/app/views/popup.generic.php35
-rw-r--r--ui/app/views/popup.triggerexpr.php72
-rw-r--r--ui/app/views/popup.valuemap.edit.php79
-rw-r--r--ui/assets/styles/blue-theme.css28
-rw-r--r--ui/assets/styles/dark-theme.css28
-rw-r--r--ui/assets/styles/hc-dark.css28
-rw-r--r--ui/assets/styles/hc-light.css28
-rw-r--r--ui/hosts.php7
-rw-r--r--ui/include/classes/api/services/CHostGeneral.php6
-rw-r--r--ui/include/classes/api/services/CItemGeneral.php6
-rw-r--r--ui/include/classes/api/services/CValueMap.php206
-rw-r--r--ui/include/classes/data/CHistFunctionData.php80
-rw-r--r--ui/include/classes/data/CMathFunctionData.php63
-rw-r--r--ui/include/classes/export/CConfigurationExport.php16
-rw-r--r--ui/include/classes/export/CConfigurationExportBuilder.php18
-rw-r--r--ui/include/classes/helpers/CValueMapHelper.php96
-rw-r--r--ui/include/classes/import/validators/C54XmlValidator.php15
-rw-r--r--ui/include/classes/macros/CMacrosResolverGeneral.php12
-rw-r--r--ui/include/classes/mvc/CRouter.php1
-rw-r--r--ui/include/classes/parsers/CExpressionParser.php2
-rw-r--r--ui/include/classes/parsers/CNumberParser.php12
-rw-r--r--ui/include/classes/parsers/CRangeParser.php49
-rw-r--r--ui/include/classes/parsers/CRangesParser.php26
-rw-r--r--ui/include/classes/screens/CScreenHistory.php4
-rw-r--r--ui/include/classes/validators/CApiInputValidator.php46
-rw-r--r--ui/include/classes/validators/CExpressionValidator.php10
-rw-r--r--ui/include/classes/validators/CHistFunctionValidator.php5
-rw-r--r--ui/include/classes/validators/CMathFunctionValidator.php15
-rw-r--r--ui/include/classes/xml/CXmlConstantName.php8
-rw-r--r--ui/include/classes/xml/CXmlConstantValue.php8
-rw-r--r--ui/include/defines.inc.php23
-rw-r--r--ui/include/items.inc.php4
-rw-r--r--ui/include/schema.inc.php19
-rw-r--r--ui/include/triggers.inc.php71
-rw-r--r--ui/templates.php7
-rw-r--r--ui/tests/api_json/testValuemap.php327
-rw-r--r--ui/tests/integration/IntegrationTests.php2
-rw-r--r--ui/tests/integration/testValuemaps.php304
-rw-r--r--ui/tests/selenium/common/testFormValueMappings.php17
-rw-r--r--ui/tests/selenium/testFormItem.php16
-rw-r--r--ui/tests/selenium/testFormItemPrototype.php16
-rw-r--r--ui/tests/unit/include/classes/helpers/CValueMapHelperTest.php261
-rw-r--r--ui/tests/unit/include/classes/parsers/CRangeParserTest.php164
-rw-r--r--ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php72
-rw-r--r--ui/tests/unit/include/classes/validators/CHistFunctionValidatorTest.php177
54 files changed, 3244 insertions, 482 deletions
diff --git a/ui/app/controllers/CControllerPopupGeneric.php b/ui/app/controllers/CControllerPopupGeneric.php
index e4d66845f14..619223b5025 100644
--- a/ui/app/controllers/CControllerPopupGeneric.php
+++ b/ui/app/controllers/CControllerPopupGeneric.php
@@ -1348,7 +1348,7 @@ class CControllerPopupGeneric extends CController {
$db_valuemaps = API::ValueMap()->get([
'output' => ['valuemapid', 'name', 'hostid'],
- 'selectMappings' => ['value', 'newvalue'],
+ 'selectMappings' => ['type', 'value', 'newvalue'],
'hostids' => $hostids,
'limit' => $limit
]);
@@ -1356,7 +1356,6 @@ class CControllerPopupGeneric extends CController {
$disable_names = $this->getInput('disable_names', []);
foreach ($db_valuemaps as $db_valuemap) {
- order_result($db_valuemap['mappings'], 'value');
$valuemap = [
'id' => $db_valuemap['valuemapid'],
'hostname' => $hosts[$db_valuemap['hostid']]['name'],
diff --git a/ui/app/controllers/CControllerPopupItemTestGetValue.php b/ui/app/controllers/CControllerPopupItemTestGetValue.php
index 1536ff035d2..3d697333407 100644
--- a/ui/app/controllers/CControllerPopupItemTestGetValue.php
+++ b/ui/app/controllers/CControllerPopupItemTestGetValue.php
@@ -147,7 +147,7 @@ class CControllerPopupItemTestGetValue extends CControllerPopupItemTest {
unset($data['value_type']);
}
else {
- $data['hostid'] = $this->getInput('hostid');
+ $data['host']['hostid'] = $this->getInput('hostid');
}
// Rename fields according protocol.
diff --git a/ui/app/controllers/CControllerPopupItemTestSend.php b/ui/app/controllers/CControllerPopupItemTestSend.php
index 074762e4db5..655cf7470a8 100644
--- a/ui/app/controllers/CControllerPopupItemTestSend.php
+++ b/ui/app/controllers/CControllerPopupItemTestSend.php
@@ -256,7 +256,7 @@ class CControllerPopupItemTestSend extends CControllerPopupItemTest {
$valuemap = ($this->getInput('valuemapid', 0) != 0)
? API::ValueMap()->get([
'output' => [],
- 'selectMappings' => ['newvalue', 'value'],
+ 'selectMappings' => ['type', 'newvalue', 'value'],
'valuemapids' => $this->getInput('valuemapid')
])[0]
: [];
@@ -294,7 +294,7 @@ class CControllerPopupItemTestSend extends CControllerPopupItemTest {
}
if ($item_test_data['type'] == ITEM_TYPE_CALCULATED) {
- $item_test_data['hostid'] = $this->getInput('hostid');
+ $item_test_data['host']['hostid'] = $this->getInput('hostid');
}
// Only non-empty fields need to be sent to server.
@@ -422,7 +422,9 @@ class CControllerPopupItemTestSend extends CControllerPopupItemTest {
];
if ($valuemap) {
- $output['mapped_value'] = CValueMapHelper::applyValueMap($result['result'], $valuemap);
+ $output['mapped_value'] = CValueMapHelper::applyValueMap($preproc_test_data['value_type'],
+ $result['result'], $valuemap
+ );
}
}
elseif (array_key_exists('error', $result)) {
diff --git a/ui/app/controllers/CControllerPopupTriggerExpr.php b/ui/app/controllers/CControllerPopupTriggerExpr.php
index aaaee743836..bf1cccd6d45 100644
--- a/ui/app/controllers/CControllerPopupTriggerExpr.php
+++ b/ui/app/controllers/CControllerPopupTriggerExpr.php
@@ -20,6 +20,7 @@
class CControllerPopupTriggerExpr extends CController {
+
private $metrics = [];
private $param1SecCount = [];
private $param1Period = [];
@@ -30,7 +31,6 @@ class CControllerPopupTriggerExpr extends CController {
private $param3SecVal = [];
private $param_find = [];
private $param3SecPercent = [];
- private $paramSecIntCount = [];
private $paramForecast = [];
private $paramTimeleft = [];
private $allowedTypesAny = [];
@@ -39,7 +39,7 @@ class CControllerPopupTriggerExpr extends CController {
private $allowedTypesLog = [];
private $allowedTypesInt = [];
private $functions = [];
- private $operators = [];
+ private $operators = ['=', '<>', '>', '<', '>=', '<='];
private $period_optional = [];
protected function init() {
@@ -196,25 +196,6 @@ class CControllerPopupTriggerExpr extends CController {
]
];
- $this->paramSecIntCount = [
- 'last' => [
- 'C' => _('Last of').' (T)',
- 'T' => T_ZBX_INT,
- 'M' => $this->metrics,
- 'A' => true
- ],
- 'shift' => [
- 'C' => _('Time shift'),
- 'T' => T_ZBX_INT,
- 'A' => false
- ],
- 'mask' => [
- 'C' => _('Mask'),
- 'T' => T_ZBX_STR,
- 'A' => true
- ]
- ];
-
$this->paramForecast = [
'last' => [
'C' => _('Last of').' (T)',
@@ -297,180 +278,767 @@ class CControllerPopupTriggerExpr extends CController {
$this->functions = [
'abs' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
'description' => _('abs() - Absolute value'),
'allowed_types' => $this->allowedTypesAny,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
+ ],
+ 'acos' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('acos() - The arccosine of a value as an angle, expressed in radians'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'ascii' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('ascii() - Returns the ASCII code of the leftmost character of the value'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesStr,
+ 'operators' => $this->operators
+ ],
+ 'asin' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('asin() - The arcsine of a value as an angle, expressed in radians'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'atan' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('atan() - The arctangent of a value as an angle, expressed in radians'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'atan2' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('atan2() - The arctangent of the ordinate (exprue) and abscissa coordinates specified as an angle, expressed in radians'),
+ 'params' => $this->param1SecCount + [
+ 'abscissa' => [
+ 'C' => _('Abscissa'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
],
'avg' => [
+ 'types' => [ZBX_FUNCTION_TYPE_AGGREGATE, ZBX_FUNCTION_TYPE_MATH],
'description' => _('avg() - Average value of a period T'),
'params' => $this->param1SecCount,
'allowed_types' => $this->allowedTypesNumeric,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
],
- 'change' => [
- 'description' => _('change() - Difference between last and previous value'),
- 'allowed_types' => $this->allowedTypesAny,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'between' => [
+ 'types' => [ZBX_FUNCTION_TYPE_OPERATOR],
+ 'description' => _('between() - Checks if a value belongs to the given range (1 - in range, 0 - otherwise)'),
+ 'params' => $this->param1SecCount + [
+ 'min' => [
+ 'C' => _('Min'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ],
+ 'max' => [
+ 'C' => _('Max'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => ['=', '<>']
],
- 'count' => [
- 'description' => _('count() - Number of successfully retrieved values V (which fulfill operator O) for period T'),
- 'params' => $this->param3SecVal,
- 'allowed_types' => $this->allowedTypesAny,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'bitand' => [
+ 'types' => [ZBX_FUNCTION_TYPE_BITWISE],
+ 'description' => _('bitand() - Bitwise AND'),
+ 'params' => $this->param1SecCount + [
+ 'mask' => [
+ 'C' => _('Mask'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesInt,
+ 'operators' => $this->operators
],
- 'find' => [
- 'description' => _('find() - Check occurrence of pattern V (which fulfill operator O) for period T (1 - match, 0 - no match)'),
- 'params' => $this->period_optional + $this->param_find,
+ 'bitlength' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('bitlength() - Returns the length in bits'),
+ 'params' => $this->param1SecCount,
'allowed_types' => $this->allowedTypesAny,
- 'operators' => ['=', '<>']
+ 'operators' => $this->operators
],
- 'last' => [
- 'description' => _('last() - Last (most recent) T value'),
+ 'bitlshift' => [
+ 'types' => [ZBX_FUNCTION_TYPE_BITWISE],
+ 'description' => _('bitlshift() - Bitwise shift left'),
+ 'params' => $this->param1SecCount + [
+ 'bits' => [
+ 'C' => _('Bits to shift'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesInt,
+ 'operators' => $this->operators
+ ],
+ 'bitnot' => [
+ 'types' => [ZBX_FUNCTION_TYPE_BITWISE],
+ 'description' => _('bitnot() - Bitwise NOT'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesInt,
+ 'operators' => $this->operators
+ ],
+ 'bitor' => [
+ 'types' => [ZBX_FUNCTION_TYPE_BITWISE],
+ 'description' => _('bitor() - Bitwise OR'),
+ 'params' => $this->param1SecCount + [
+ 'mask' => [
+ 'C' => _('Mask'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesInt,
+ 'operators' => $this->operators
+ ],
+ 'bitrshift' => [
+ 'types' => [ZBX_FUNCTION_TYPE_BITWISE],
+ 'description' => _('bitrshift() - Bitwise shift right'),
+ 'params' => $this->param1SecCount + [
+ 'bits' => [
+ 'C' => _('Bits to shift'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesInt,
+ 'operators' => $this->operators
+ ],
+ 'bitxor' => [
+ 'types' => [ZBX_FUNCTION_TYPE_BITWISE],
+ 'description' => _('bitxor() - Bitwise exclusive OR'),
+ 'params' => $this->param1SecCount + [
+ 'mask' => [
+ 'C' => _('Mask'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesInt,
+ 'operators' => $this->operators
+ ],
+ 'bytelength' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('bytelength() - Returns the length in bytes'),
'params' => $this->param1SecCount,
'allowed_types' => $this->allowedTypesAny,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
],
- 'length' => [
- 'description' => _('length() - Length of last (most recent) T value in characters'),
- 'allowed_types' => $this->allowedTypesStr,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'cbrt' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('cbrt() - Cube root'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
],
- 'max' => [
- 'description' => _('max() - Maximum value for period T'),
+ 'ceil' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('ceil() - Rounds up to the nearest greater integer'),
'params' => $this->param1SecCount,
'allowed_types' => $this->allowedTypesNumeric,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
],
- 'min' => [
- 'description' => _('min() - Minimum value for period T'),
+ 'change' => [
+ 'types' => [ZBX_FUNCTION_TYPE_HISTORY],
+ 'description' => _('change() - Difference between last and previous value'),
+ 'allowed_types' => $this->allowedTypesAny,
+ 'operators' => $this->operators
+ ],
+ 'char' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('char() - Returns the character which represents the given ASCII code'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesInt,
+ 'operators' => $this->operators
+ ],
+ 'concat' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('concat() - Returns a string that is the result of concatenating value to string'),
+ 'params' => $this->param1SecCount + [
+ 'string' => [
+ 'C' => _('String'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesAny,
+ 'operators' => $this->operators
+ ],
+ 'cos' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('cos() - The cosine of a value, where the value is an angle expressed in radians'),
'params' => $this->param1SecCount,
'allowed_types' => $this->allowedTypesNumeric,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
],
- 'percentile' => [
- 'description' => _('percentile() - Percentile P of a period T'),
- 'params' => $this->param3SecPercent,
+ 'cosh' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('cosh() - The hyperbolic cosine of a value'),
+ 'params' => $this->param1SecCount,
'allowed_types' => $this->allowedTypesNumeric,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
],
- 'sum' => [
- 'description' => _('sum() - Sum of values of a period T'),
+ 'cot' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('cot() - The cotangent of a value, where the value is an angle expressed in radians'),
'params' => $this->param1SecCount,
'allowed_types' => $this->allowedTypesNumeric,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
+ ],
+ 'count' => [
+ 'types' => [ZBX_FUNCTION_TYPE_AGGREGATE],
+ 'description' => _('count() - Number of successfully retrieved values V (which fulfill operator O) for period T'),
+ 'params' => $this->param3SecVal,
+ 'allowed_types' => $this->allowedTypesAny,
+ 'operators' => $this->operators
+ ],
+ 'countunique' => [
+ 'types' => [ZBX_FUNCTION_TYPE_AGGREGATE],
+ 'description' => _('countunique() - The number of unique values'),
+ 'params' => $this->param3SecVal,
+ 'allowed_types' => $this->allowedTypesAny,
+ 'operators' => $this->operators
],
'date' => [
+ 'types' => [ZBX_FUNCTION_TYPE_DATE_TIME],
'description' => _('date() - Current date'),
'allowed_types' => $this->allowedTypesAny,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
+ ],
+ 'dayofmonth' => [
+ 'types' => [ZBX_FUNCTION_TYPE_DATE_TIME],
+ 'description' => _('dayofmonth() - Day of month'),
+ 'allowed_types' => $this->allowedTypesAny,
+ 'operators' => $this->operators
],
'dayofweek' => [
+ 'types' => [ZBX_FUNCTION_TYPE_DATE_TIME],
'description' => _('dayofweek() - Day of week'),
'allowed_types' => $this->allowedTypesAny,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
],
- 'dayofmonth' => [
- 'description' => _('dayofmonth() - Day of month'),
+ 'degrees' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('degrees() - Converts a value from radians to degrees'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'e' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _("e() - Returns Euler's number"),
+ 'allowed_types' => $this->allowedTypesAny
+ ],
+ 'exp' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _("exp() - Euler's number at a power of a value"),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'expm1' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _("expm1() - Euler's number at a power of a value minus 1"),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'find' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('find() - Check occurrence of pattern V (which fulfill operator O) for period T (1 - match, 0 - no match)'),
+ 'params' => $this->period_optional + $this->param_find,
'allowed_types' => $this->allowedTypesAny,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => ['=', '<>']
+ ],
+ 'first' => [
+ 'types' => [ZBX_FUNCTION_TYPE_HISTORY],
+ 'description' => _('first() - The oldest value in the specified time interval'),
+ 'params' => $this->param1Sec + $this->period_optional,
+ 'allowed_types' => $this->allowedTypesAny,
+ 'operators' => $this->operators
+ ],
+ 'floor' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('floor() - Rounds down to the nearest smaller integer'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'forecast' => [
+ 'types' => [ZBX_FUNCTION_TYPE_PREDICTION],
+ 'description' => _('forecast() - Forecast for next t seconds based on period T'),
+ 'params' => $this->paramForecast,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
],
'fuzzytime' => [
+ 'types' => [ZBX_FUNCTION_TYPE_HISTORY],
'description' => _('fuzzytime() - Difference between item value (as timestamp) and Zabbix server timestamp is less than or equal to T seconds (1 - true, 0 - false)'),
'params' => $this->param1Sec,
'allowed_types' => $this->allowedTypesNumeric,
'operators' => ['=', '<>']
],
+ 'in' => [
+ 'types' => [ZBX_FUNCTION_TYPE_OPERATOR],
+ 'description' => _('in() - Checks if a value equals to one of the listed values (1 - equals, 0 - otherwise)'),
+ 'params' => $this->param1SecCount + [
+ 'values' => [
+ 'C' => _('Values'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesAny,
+ 'operators' => ['=', '<>']
+ ],
+ 'insert' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('insert() - Inserts specified characters or spaces into a character string, beginning at a specified position in the string'),
+ 'params' => $this->param1SecCount + [
+ 'start' => [
+ 'C' => _('Start'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ],
+ 'length' => [
+ 'C' => _('Length'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ],
+ 'replace' => [
+ 'C' => _('Replacement'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesStr,
+ 'operators' => $this->operators
+ ],
+ 'kurtosis' => [
+ 'types' => [ZBX_FUNCTION_TYPE_AGGREGATE],
+ 'description' => _('kurtosis() - Measures the "tailedness" of the probability distribution'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'last' => [
+ 'types' => [ZBX_FUNCTION_TYPE_HISTORY],
+ 'description' => _('last() - Last (most recent) T value'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesAny,
+ 'operators' => $this->operators
+ ],
+ 'left' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('left() - Returns the leftmost count characters'),
+ 'params' => $this->param1SecCount + [
+ 'count' => [
+ 'C' => _('Count'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesStr,
+ 'operators' => $this->operators
+ ],
+ 'length' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('length() - Length of last (most recent) T value in characters'),
+ 'allowed_types' => $this->allowedTypesStr,
+ 'operators' => $this->operators
+ ],
+ 'log' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('log() - Natural logarithm'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'log10' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('log10() - Decimal logarithm'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
'logeventid' => [
+ 'types' => [ZBX_FUNCTION_TYPE_HISTORY],
'description' => _('logeventid() - Event ID of last log entry matching regular expression V for period T (1 - match, 0 - no match)'),
'params' => $this->period_optional + $this->param1Str,
'allowed_types' => $this->allowedTypesLog,
'operators' => ['=', '<>']
],
'logseverity' => [
+ 'types' => [ZBX_FUNCTION_TYPE_HISTORY],
'description' => _('logseverity() - Log severity of the last log entry for period T'),
'params' => $this->period_optional,
'allowed_types' => $this->allowedTypesLog,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
],
'logsource' => [
+ 'types' => [ZBX_FUNCTION_TYPE_HISTORY],
'description' => _('logsource() - Log source of the last log entry matching parameter V for period T (1 - match, 0 - no match)'),
'params' => $this->period_optional + $this->param1Str,
'allowed_types' => $this->allowedTypesLog,
'operators' => ['=', '<>']
],
- 'now' => [
- 'description' => _('now() - Number of seconds since the Epoch'),
- 'allowed_types' => $this->allowedTypesAny,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'ltrim' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('ltrim() - Remove specified characters from the beginning of a string'),
+ 'params' => $this->param1SecCount + [
+ 'chars' => [
+ 'C' => _('Chars'),
+ 'T' => T_ZBX_STR,
+ 'A' => false
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesStr,
+ 'operators' => $this->operators
],
- 'time' => [
- 'description' => _('time() - Current time'),
- 'allowed_types' => $this->allowedTypesAny,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'mad' => [
+ 'types' => [ZBX_FUNCTION_TYPE_AGGREGATE],
+ 'description' => _('mad() - Median absolute deviation'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'max' => [
+ 'types' => [ZBX_FUNCTION_TYPE_AGGREGATE, ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('max() - Maximum value for period T'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'mid' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('mid() - Returns a substring beginning at the character position specified by start for N characters'),
+ 'params' => $this->param1SecCount + [
+ 'start' => [
+ 'C' => _('Start'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ],
+ 'length' => [
+ 'C' => _('Length'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesStr,
+ 'operators' => $this->operators
+ ],
+ 'min' => [
+ 'types' => [ZBX_FUNCTION_TYPE_AGGREGATE, ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('min() - Minimum value for period T'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'mod' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('mod() - Division remainder'),
+ 'params' => $this->param1SecCount + [
+ 'denominator' => [
+ 'C' => _('Division denominator'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
],
'nodata' => [
+ 'types' => [ZBX_FUNCTION_TYPE_HISTORY],
'description' => _('nodata() - No data received during period of time T (1 - true, 0 - false), Mode (strict - ignore proxy time delay in sending data)'),
'params' => $this->param2SecMode,
'allowed_types' => $this->allowedTypesAny,
'operators' => ['=', '<>']
],
- 'bitand' => [
- 'description' => _('bitand() - Bitwise AND of last (most recent) T value and mask'),
- 'params' => $this->paramSecIntCount,
- 'allowed_types' => $this->allowedTypesInt,
- 'operators' => ['=', '<>']
+ 'now' => [
+ 'types' => [ZBX_FUNCTION_TYPE_DATE_TIME],
+ 'description' => _('now() - Number of seconds since the Epoch'),
+ 'allowed_types' => $this->allowedTypesAny,
+ 'operators' => $this->operators
],
- 'forecast' => [
- 'description' => _('forecast() - Forecast for next t seconds based on period T'),
- 'params' => $this->paramForecast,
+ 'percentile' => [
+ 'types' => [ZBX_FUNCTION_TYPE_HISTORY],
+ 'description' => _('percentile() - Percentile P of a period T'),
+ 'params' => $this->param3SecPercent,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'pi' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('pi() - Returns the Pi constant'),
+ 'allowed_types' => $this->allowedTypesAny
+ ],
+ 'power' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('power() - The power of a base value to a power value'),
+ 'params' => $this->param1SecCount + [
+ 'power' => [
+ 'C' => _('Power value'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'radians' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('radians() - Converts a value from degrees to radians'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'rand' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('rand() - A random integer value'),
+ 'allowed_types' => $this->allowedTypesAny
+ ],
+ 'repeat' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('repeat() - Returns a string composed of value repeated count times'),
+ 'params' => $this->param1SecCount + [
+ 'count' => [
+ 'C' => _('Count'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesStr,
+ 'operators' => $this->operators
+ ],
+ 'replace' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('replace() - Search value for occurrences of pattern, and replace with replacement'),
+ 'params' => $this->param1SecCount + [
+ 'pattern' => [
+ 'C' => _('Pattern'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ],
+ 'replace' => [
+ 'C' => _('Replacement'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesStr,
+ 'operators' => $this->operators
+ ],
+ 'right' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('right() - Returns the rightmost count characters'),
+ 'params' => $this->param1SecCount + [
+ 'count' => [
+ 'C' => _('Count'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesStr,
+ 'operators' => $this->operators
+ ],
+ 'round' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('round() - Rounds a value to decimal places'),
+ 'params' => $this->param1SecCount + [
+ 'decimals' => [
+ 'C' => _('Decimal places'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'rtrim' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('rtrim() - Removes specified characters from the end of a string'),
+ 'params' => $this->param1SecCount + [
+ 'chars' => [
+ 'C' => _('Chars'),
+ 'T' => T_ZBX_STR,
+ 'A' => false
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesStr,
+ 'operators' => $this->operators
+ ],
+ 'signum' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('signum() - Returns -1 if a value is negative, 0 if a value is zero, 1 if a value is positive'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'sin' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('sin() - The sine of a value, where the value is an angle expressed in radians'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'sinh' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('sinh() - The hyperbolic sine of a value'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'skewness' => [
+ 'types' => [ZBX_FUNCTION_TYPE_AGGREGATE],
+ 'description' => _('skewness() - Measures the asymmetry of the probability distribution'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'sqrt' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('sqrt() - Square root of a value'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'stddevpop' => [
+ 'types' => [ZBX_FUNCTION_TYPE_AGGREGATE],
+ 'description' => _('stddevpop() - Population standard deviation'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'stddevsamp' => [
+ 'types' => [ZBX_FUNCTION_TYPE_AGGREGATE],
+ 'description' => _('stddevsamp() - Sample standard deviation'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'sum' => [
+ 'types' => [ZBX_FUNCTION_TYPE_AGGREGATE, ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('sum() - Sum of values of a period T'),
+ 'params' => $this->param1SecCount,
'allowed_types' => $this->allowedTypesNumeric,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
+ ],
+ 'sumofsquares' => [
+ 'types' => [ZBX_FUNCTION_TYPE_AGGREGATE],
+ 'description' => _('sumofsquares() - The sum of squares'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'tan' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('tan() - The tangent of a value'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'time' => [
+ 'types' => [ZBX_FUNCTION_TYPE_DATE_TIME],
+ 'description' => _('time() - Current time'),
+ 'allowed_types' => $this->allowedTypesAny,
+ 'operators' => $this->operators
],
'timeleft' => [
+ 'types' => [ZBX_FUNCTION_TYPE_PREDICTION],
'description' => _('timeleft() - Time to reach threshold estimated based on period T'),
'params' => $this->paramTimeleft,
'allowed_types' => $this->allowedTypesNumeric,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
],
'trendavg' => [
+ 'types' => [ZBX_FUNCTION_TYPE_HISTORY],
'description' => _('trendavg() - Average value of a period T with exact period shift'),
'params' => $this->param1Period,
'allowed_types' => $this->allowedTypesNumeric,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
],
'trendcount' => [
- 'description' => _('trendcount() - Number of successfully retrieved values V (which fulfill operator O) for period T with exact period shift'),
+ 'types' => [ZBX_FUNCTION_TYPE_HISTORY],
+ 'description' => _('trendcount() - Number of successfully retrieved values for period T'),
'params' => $this->param1Period,
'allowed_types' => $this->allowedTypesAny,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
],
'trendmax' => [
+ 'types' => [ZBX_FUNCTION_TYPE_HISTORY],
'description' => _('trendmax() - Maximum value for period T with exact period shift'),
'params' => $this->param1Period,
'allowed_types' => $this->allowedTypesNumeric,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
],
'trendmin' => [
+ 'types' => [ZBX_FUNCTION_TYPE_HISTORY],
'description' => _('trendmin() - Minimum value for period T with exact period shift'),
'params' => $this->param1Period,
'allowed_types' => $this->allowedTypesNumeric,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
],
'trendsum' => [
+ 'types' => [ZBX_FUNCTION_TYPE_HISTORY],
'description' => _('trendsum() - Sum of values of a period T with exact period shift'),
'params' => $this->param1Period,
'allowed_types' => $this->allowedTypesNumeric,
- 'operators' => ['=', '<>', '>', '<', '>=', '<=']
+ 'operators' => $this->operators
+ ],
+ 'trim' => [
+ 'types' => [ZBX_FUNCTION_TYPE_STRING],
+ 'description' => _('trim() - Remove specified characters from the beginning and the end of a string'),
+ 'params' => $this->param1SecCount + [
+ 'chars' => [
+ 'C' => _('Chars'),
+ 'T' => T_ZBX_STR,
+ 'A' => false
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesStr,
+ 'operators' => $this->operators
+ ],
+ 'truncate' => [
+ 'types' => [ZBX_FUNCTION_TYPE_MATH],
+ 'description' => _('truncate() - Truncates a value to decimal places'),
+ 'params' => $this->param1SecCount + [
+ 'decimals' => [
+ 'C' => _('Decimal places'),
+ 'T' => T_ZBX_STR,
+ 'A' => true
+ ]
+ ],
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'varpop' => [
+ 'types' => [ZBX_FUNCTION_TYPE_AGGREGATE],
+ 'description' => _('varpop() - Population variance'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
+ ],
+ 'varsamp' => [
+ 'types' => [ZBX_FUNCTION_TYPE_AGGREGATE],
+ 'description' => _('varsamp() - Sample variance'),
+ 'params' => $this->param1SecCount,
+ 'allowed_types' => $this->allowedTypesNumeric,
+ 'operators' => $this->operators
]
];
CArrayHelper::sort($this->functions, ['description']);
-
- foreach ($this->functions as $function) {
- foreach ($function['operators'] as $operator) {
- $this->operators[$operator] = true;
- }
- }
}
protected function checkInput() {
@@ -481,7 +1049,7 @@ class CControllerPopupTriggerExpr extends CController {
'itemid' => 'db items.itemid',
'parent_discoveryid' => 'db items.itemid',
'function' => 'in '.implode(',', array_keys($this->functions)),
- 'operator' => 'in '.implode(',', array_keys($this->operators)),
+ 'operator' => 'in '.implode(',', $this->operators),
'params' => '',
'paramtype' => 'in '.implode(',', [PARAM_TYPE_TIME, PARAM_TYPE_COUNTS]),
'value' => 'string|not_empty',
@@ -517,7 +1085,7 @@ class CControllerPopupTriggerExpr extends CController {
protected function doAction() {
$expression_parser = new CExpressionParser(['lldmacros' => true]);
- $expression_validator = new CExpressionValidator();
+ $expression_validator = new CExpressionValidator(['partial' => true]);
$itemid = $this->getInput('itemid', 0);
$function = $this->getInput('function', 'last');
@@ -572,7 +1140,7 @@ class CControllerPopupTriggerExpr extends CController {
if (array_key_exists($index, $tokens)
&& $tokens[$index]['type'] == CExpressionParserResult::TOKEN_TYPE_OPERATOR
- && in_array($tokens[$index]['match'], ['=', '<>', '>', '<', '>=', '<='])) {
+ && in_array($tokens[$index]['match'], $this->operators)) {
$operator = $tokens[$index]['match'];
$index++;
@@ -725,9 +1293,10 @@ class CControllerPopupTriggerExpr extends CController {
'params' => $params,
'paramtype' => $param_type,
'item_description' => $description,
- 'item_required' => !in_array($function, getStandaloneFunctions()),
+ 'item_required' => !in_array($function, array_merge(getStandaloneFunctions(), getFunctionsConstants())),
'functions' => $this->functions,
'function' => $function,
+ 'function_type' => reset($this->functions[$function]['types']),
'operator' => $operator,
'item_key' => $item_key,
'itemValueType' => $item_value_type,
@@ -748,6 +1317,7 @@ class CControllerPopupTriggerExpr extends CController {
if ($data['selectedFunction'] === null) {
$data['selectedFunction'] = 'last';
$data['function'] = 'last';
+ $data['function_type'] = ZBX_FUNCTION_TYPE_HISTORY;
}
// Remove functions that not correspond to chosen item.
@@ -755,9 +1325,10 @@ class CControllerPopupTriggerExpr extends CController {
if ($data['itemValueType'] !== null && !array_key_exists($data['itemValueType'], $f['allowed_types'])) {
unset($data['functions'][$id]);
- // Take first available function from list and change to first available operator for that function.
+ // Take first available function from list.
if ($id === $data['function']) {
$data['function'] = key($data['functions']);
+ $data['function_type'] = reset($data['functions'][$data['function']]['types']);
$data['operator'] = reset($data['functions'][$data['function']]['operators']);
}
}
@@ -766,7 +1337,10 @@ class CControllerPopupTriggerExpr extends CController {
// Create and validate trigger expression before inserting it into textarea field.
if ($this->getInput('add', false)) {
try {
- if (in_array($function, getStandaloneFunctions())) {
+ if (in_array($function, getFunctionsConstants())) {
+ $data['expression'] = sprintf('%s()', $function);
+ }
+ elseif (in_array($function, getStandaloneFunctions())) {
$data['expression'] = sprintf('%s()%s%s', $function, $operator,
CExpressionParser::quoteString($data['value'])
);
@@ -774,7 +1348,7 @@ class CControllerPopupTriggerExpr extends CController {
elseif ($data['item_description']) {
// Quote function string parameters.
foreach ($data['params'] as $param_key => $param) {
- if (!in_array($param_key, ['v', 'o', 'fit', 'mode', 'pattern'])
+ if (!in_array($param_key, ['v', 'o', 'chars', 'fit', 'mode', 'pattern', 'replace', 'string'])
|| !array_key_exists($param_key, $data['params'])
|| $data['params'][$param_key] === '') {
continue;
@@ -802,42 +1376,33 @@ class CControllerPopupTriggerExpr extends CController {
}
unset($data['params']['shift'], $data['params']['period_shift']);
- $mask = '';
- if ($function === 'bitand' && array_key_exists('mask', $data['params'])) {
- $mask = $data['params']['mask'];
- unset($data['params']['mask']);
- }
-
- $fn_params = rtrim(implode(',', $data['params']), ',');
-
- if ($function === 'abs') {
- $data['expression'] = sprintf('abs(last(/%s/%s)%s)%s%s',
- $item_host_data['host'],
- $data['item_key'],
- ($fn_params === '') ? '' : ','.$fn_params,
- $operator,
- CExpressionParser::quoteString($data['value'])
- );
- }
- elseif ($function === 'bitand') {
- $data['expression'] = sprintf('bitand(last(/%s/%s%s)%s)%s%s',
+ // Functions where item is wrapped in last() like func(last(/host/item)).
+ $last_functions = [
+ 'abs', 'acos', 'ascii', 'asin', 'atan', 'atan2', 'between', 'bitand', 'bitlength', 'bitlshift',
+ 'bitnot', 'bitor', 'bitrshift', 'bitxor', 'bytelength', 'cbrt', 'ceil', 'char', 'concat', 'cos',
+ 'cosh', 'cot', 'degrees', 'exp', 'expm1', 'floor', 'in', 'insert', 'left', 'log', 'log10',
+ 'ltrim', 'mid', 'mod', 'power', 'radians', 'repeat', 'replace', 'right', 'round', 'signum',
+ 'sin', 'sinh', 'sqrt', 'tan', 'trim', 'truncate'
+ ];
+
+ if (in_array($function, $last_functions)) {
+ $last_params = $data['params']['last'];
+ unset($data['params']['last']);
+ $fn_params = rtrim(implode(',', $data['params']), ',');
+
+ $data['expression'] = sprintf('%s(last(/%s/%s%s)%s)%s%s',
+ $function,
$item_host_data['host'],
$data['item_key'],
+ ($last_params === '') ? '' : ','.$last_params,
($fn_params === '') ? '' : ','.$fn_params,
- ($mask === '') ? '' : ','.$mask,
- $operator,
- CExpressionParser::quoteString($data['value'])
- );
- }
- elseif ($function === 'length') {
- $data['expression'] = sprintf('length(last(/%s/%s))%s%s',
- $item_host_data['host'],
- $data['item_key'],
$operator,
CExpressionParser::quoteString($data['value'])
);
}
else {
+ $fn_params = rtrim(implode(',', $data['params']), ',');
+
$data['expression'] = sprintf('%s(/%s/%s%s)%s%s',
$function,
$item_host_data['host'],
@@ -856,7 +1421,7 @@ class CControllerPopupTriggerExpr extends CController {
// Parse and validate trigger expression.
if ($expression_parser->parse($data['expression']) == CParser::PARSE_SUCCESS) {
if (!$expression_validator->validate($expression_parser->getResult()->getTokens())) {
- error($expression_validator->getError());
+ error(_s('Invalid condition: %1$s.', $expression_validator->getError()));
}
}
else {
diff --git a/ui/app/controllers/CControllerPopupValueMapEdit.php b/ui/app/controllers/CControllerPopupValueMapEdit.php
index 401b8b5fb93..9a2c89d2491 100644
--- a/ui/app/controllers/CControllerPopupValueMapEdit.php
+++ b/ui/app/controllers/CControllerPopupValueMapEdit.php
@@ -35,7 +35,7 @@ class CControllerPopupValueMapEdit extends CController {
'valuemap_names' => 'array'
];
- $ret = $this->validateInput($fields) && $this->validateValueMap();
+ $ret = $this->validateInput($fields);
if (!$ret) {
$output = [];
@@ -51,92 +51,13 @@ class CControllerPopupValueMapEdit extends CController {
return $ret;
}
- /**
- * Validate vlue map to be added.
- *
- * @return bool
- */
- protected function validateValueMap(): bool {
- if (!$this->hasInput('update')) {
- return true;
- }
-
- $name = $this->getInput('name', '');
-
- if ($name === '') {
- error(_s('Incorrect value for field "%1$s": %2$s.', _('Name'), _('cannot be empty')));
- return false;
- }
-
- $valuemap_names = $this->getInput('valuemap_names', []);
-
- if (in_array($name, $valuemap_names)) {
- error(_s('Incorrect value for field "%1$s": %2$s.', _('Name'),
- _s('value %1$s already exists', '('.$name.')'))
- );
- return false;
- }
-
- $mappings = array_filter($this->getInput('mappings', []), function ($mapping) {
- return ($mapping['value'] !== '' || $mapping['newvalue'] !== '');
- });
-
- if (!$mappings) {
- error(_s('Incorrect value for field "%1$s": %2$s.', _('Mappings'), _('cannot be empty')));
- return false;
- }
-
- $values = [];
-
- foreach ($mappings as $mapping) {
- if ($mapping['newvalue'] === '') {
- error(_s('Incorrect value for field "%1$s": %2$s.', _('Mapped to'), _('cannot be empty')));
- return false;
- }
-
- if (array_key_exists($mapping['value'], $values)) {
- error(_s('Incorrect value for field "%1$s": %2$s.', _('Value'),
- _s('value %1$s already exists', '('.$mapping['value'].')'))
- );
- return false;
- }
-
- $values[$mapping['value']] = 1;
- }
-
- return true;
- }
-
protected function checkPermissions() {
return true;
}
protected function doAction() {
- $this->setResponse($this->hasInput('update') ? $this->update() : $this->form());
- }
-
- /**
- * Get response object with data to be returned as json data when added value map is valid.
- *
- * @return CControllerResponse
- */
- protected function update(): CControllerResponse {
- $data = [];
- $this->getInputs($data, ['valuemapid', 'name', 'mappings', 'edit']);
- order_result($data['mappings'], 'value');
- $data['mappings'] = array_values($data['mappings']);
-
- return (new CControllerResponseData(['main_block' => json_encode($data)]))->disableView();
- }
-
- /**
- * Get response object with data required to render value map edit form.
- *
- * @return CControllerResponse
- */
- protected function form(): CControllerResponse {
$data = [
- 'action' => $this->getAction(),
+ 'action' => 'popup.valuemap.update',
'edit' => 0,
'mappings' => [],
'name' => '',
@@ -146,9 +67,27 @@ class CControllerPopupValueMapEdit extends CController {
$this->getInputs($data, array_keys($data));
if (!$data['mappings']) {
- $data['mappings'][] = ['value' => '', 'newvalue' => ''];
+ $mappings = [['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => '', 'newvalue' => '']];
+ }
+ else {
+ $mappings = [];
+ $default = [];
+
+ foreach ($data['mappings'] as $mapping) {
+ if ($mapping['type'] == VALUEMAP_MAPPING_TYPE_DEFAULT) {
+ $default = $mapping;
+ }
+ else {
+ $mappings[] = $mapping;
+ }
+ }
+
+ if ($default) {
+ $mappings[] = $default;
+ }
}
+ $data['mappings'] = $mappings;
$data += [
'title' => _('Value mapping'),
'user' => [
@@ -156,6 +95,6 @@ class CControllerPopupValueMapEdit extends CController {
]
];
- return new CControllerResponseData($data);
+ $this->setResponse(new CControllerResponseData($data));
}
}
diff --git a/ui/app/controllers/CControllerPopupValueMapUpdate.php b/ui/app/controllers/CControllerPopupValueMapUpdate.php
new file mode 100644
index 00000000000..19631a8b8b9
--- /dev/null
+++ b/ui/app/controllers/CControllerPopupValueMapUpdate.php
@@ -0,0 +1,175 @@
+<?php
+/*
+** Zabbix
+** Copyright (C) 2001-2020 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.
+**/
+
+
+class CControllerPopupValueMapUpdate extends CController {
+
+ protected function checkInput() {
+ $fields = [
+ 'edit' => 'in 1,0',
+ 'mappings' => 'array',
+ 'name' => 'string',
+ 'update' => 'in 1',
+ 'valuemapid' => 'id',
+ 'valuemap_names' => 'array'
+ ];
+
+ $ret = $this->validateInput($fields) && $this->validateValueMap();
+
+ if (!$ret) {
+ $output = [];
+ if (($messages = getMessages()) !== null) {
+ $output['errors'] = $messages->toString();
+ }
+
+ $this->setResponse(
+ (new CControllerResponseData(['main_block' => json_encode($output)]))->disableView()
+ );
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Validate vlue map to be added.
+ *
+ * @return bool
+ */
+ protected function validateValueMap(): bool {
+ if (!$this->hasInput('update')) {
+ return true;
+ }
+
+ $name = $this->getInput('name', '');
+
+ if ($name === '') {
+ error(_s('Incorrect value for field "%1$s": %2$s.', _('Name'), _('cannot be empty')));
+ return false;
+ }
+
+ $valuemap_names = $this->getInput('valuemap_names', []);
+
+ if (in_array($name, $valuemap_names)) {
+ error(_s('Incorrect value for field "%1$s": %2$s.', _('Name'),
+ _s('value %1$s already exists', '('.$name.')'))
+ );
+ return false;
+ }
+
+ $type_uniq = array_fill_keys([VALUEMAP_MAPPING_TYPE_EQUAL, VALUEMAP_MAPPING_TYPE_GREATER_EQUAL,
+ VALUEMAP_MAPPING_TYPE_LESS_EQUAL, VALUEMAP_MAPPING_TYPE_IN_RANGE, VALUEMAP_MAPPING_TYPE_REGEXP
+ ], []
+ );
+ $number_parser = new CNumberParser();
+ $range_parser = new CRangesParser(['with_minus' => true, 'with_float' => true, 'with_suffix' => true]);
+ $mappings = [];
+
+ foreach ($this->getInput('mappings', []) as $mapping) {
+ $mapping += ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => '', 'newvalue' => ''];
+ $type = $mapping['type'];
+ $value = $mapping['value'];
+
+ if ($type != VALUEMAP_MAPPING_TYPE_DEFAULT && $value === '' && $mapping['newvalue'] === '') {
+ continue;
+ }
+
+ if ($mapping['newvalue'] === '') {
+ error(_s('Incorrect value for field "%1$s": %2$s.', _('Mapped to'), _('cannot be empty')));
+
+ return false;
+ }
+ elseif ($type == VALUEMAP_MAPPING_TYPE_REGEXP
+ && @preg_match('/'.str_replace('/', '\/', $value).'/', '') === false) {
+ error(_s('Incorrect value for field "%1$s": %2$s.', _('Value'), _('invalid regular expression')));
+
+ return false;
+ }
+ elseif ($type == VALUEMAP_MAPPING_TYPE_IN_RANGE && $range_parser->parse($value) != CParser::PARSE_SUCCESS) {
+ error(_s('Incorrect value for field "%1$s": %2$s.', _('Value'),
+ _('invalid range expression')
+ ));
+
+ return false;
+ }
+ elseif ($type == VALUEMAP_MAPPING_TYPE_LESS_EQUAL || $type == VALUEMAP_MAPPING_TYPE_GREATER_EQUAL) {
+ if ($number_parser->parse($value) != CParser::PARSE_SUCCESS) {
+ error(_s('Incorrect value for field "%1$s": %2$s.', _('Value'),
+ _('a floating point value is expected')
+ ));
+
+ return false;
+ }
+
+ $value = (float) $number_parser->getMatch();
+ $value = strval($value);
+ }
+
+ if ($type != VALUEMAP_MAPPING_TYPE_DEFAULT && array_key_exists($value, $type_uniq[$type])) {
+ error(_s('Incorrect value for field "%1$s": %2$s.', _('Value'),
+ _s('value %1$s already exists', '('.$value.')'))
+ );
+
+ return false;
+ }
+
+ $type_uniq[$type][$value] = true;
+ $mappings[] = $mapping;
+ }
+
+ if (!$mappings) {
+ error(_s('Incorrect value for field "%1$s": %2$s.', _('Mappings'), _('cannot be empty')));
+ return false;
+ }
+
+ return true;
+ }
+
+ protected function checkPermissions() {
+ return true;
+ }
+
+ protected function doAction() {
+ $data = [];
+ $mappings = [];
+ $default = [];
+ $this->getInputs($data, ['valuemapid', 'name', 'mappings', 'edit']);
+
+ foreach ($data['mappings'] as $mapping) {
+ if ($mapping['type'] != VALUEMAP_MAPPING_TYPE_DEFAULT &&
+ $mapping['value'] === '' && $mapping['newvalue'] === '') {
+ continue;
+ }
+ elseif ($mapping['type'] == VALUEMAP_MAPPING_TYPE_DEFAULT) {
+ $default = $mapping;
+
+ continue;
+ }
+
+ $mappings[] = $mapping;
+ }
+
+ if ($default) {
+ $mappings[] = $default;
+ }
+
+ $data['mappings'] = $mappings;
+ $this->setResponse((new CControllerResponseData(['main_block' => json_encode($data)])));
+ }
+}
diff --git a/ui/app/partials/js/configuration.valuemap.js.php b/ui/app/partials/js/configuration.valuemap.js.php
index 4f4a1062c22..b1719df377f 100644
--- a/ui/app/partials/js/configuration.valuemap.js.php
+++ b/ui/app/partials/js/configuration.valuemap.js.php
@@ -105,25 +105,64 @@ var AddValueMap = class {
createMappingCell() {
let i = 0;
- const cell = document.createElement('td');
- const hellip = document.createElement('span');
- hellip.innerHTML = '&hellip;';
+ let cell = document.createElement('td');
+ let hellip = document.createElement('span');
+ let arrow_cell = document.createElement('div');
+ let mappings_table = document.createElement('div');
+ let value_cell, newvalue_cell;
+ hellip.innerHTML = '&hellip;';
+ arrow_cell.textContent = '⇒';
+ mappings_table.classList.add('mappings-table');
cell.classList.add('wordwrap');
- for (let value of this.data.mappings) {
- if (i <= this.MAX_MAPPINGS) {
- cell.append((i < this.MAX_MAPPINGS)
- ? `${value.value} ⇒ ${value.newvalue}`
- : hellip,
- document.createElement('br')
- );
- }
- cell.appendChild(this.createHiddenInput(`[mappings][${i}][value]`, value.value));
- cell.appendChild(this.createHiddenInput(`[mappings][${i}][newvalue]`, value.newvalue));
+ for (let mapping of this.data.mappings) {
+ mapping = {value: '', ...mapping};
+
+ cell.appendChild(this.createHiddenInput(`[mappings][${i}][type]`, mapping.type));
+ cell.appendChild(this.createHiddenInput(`[mappings][${i}][value]`, mapping.value));
+ cell.appendChild(this.createHiddenInput(`[mappings][${i}][newvalue]`, mapping.newvalue));
i++;
}
+ for (let mapping of this.data.mappings.slice(0, this.MAX_MAPPINGS)) {
+ value_cell = document.createElement('div');
+ newvalue_cell = document.createElement('div');
+ newvalue_cell.textContent = mapping.newvalue;
+
+ switch (parseInt(mapping.type, 10)) {
+ case <?= VALUEMAP_MAPPING_TYPE_EQUAL ?>:
+ value_cell.textContent = `=${mapping.value}`;
+ break;
+
+ case <?= VALUEMAP_MAPPING_TYPE_GREATER_EQUAL ?>:
+ value_cell.textContent = `>=${mapping.value}`;
+ break;
+
+ case <?= VALUEMAP_MAPPING_TYPE_LESS_EQUAL ?>:
+ value_cell.textContent = `<=${mapping.value}`;
+ break;
+
+ case <?= VALUEMAP_MAPPING_TYPE_DEFAULT ?>:
+ value_cell = document.createElement('em');
+ value_cell.textContent = <?= json_encode(_('default')) ?>;
+ break;
+
+ default:
+ value_cell.textContent = mapping.value;
+ }
+
+ mappings_table.append(value_cell);
+ mappings_table.append(arrow_cell.cloneNode(true));
+ mappings_table.append(newvalue_cell);
+ }
+
+ cell.append(mappings_table);
+
+ if (this.data.mappings.length > this.MAX_MAPPINGS) {
+ cell.append(hellip);
+ }
+
return cell;
}
diff --git a/ui/app/views/js/popup.triggerexpr.js.php b/ui/app/views/js/popup.triggerexpr.js.php
index c01cab3d6af..ffb6410b8ad 100644
--- a/ui/app/views/js/popup.triggerexpr.js.php
+++ b/ui/app/views/js/popup.triggerexpr.js.php
@@ -40,7 +40,12 @@ $(() => {
}
};
- $('#function').on('change', (e) => {
- reloadPopup($(e.target).closest('form').get(0), 'popup.triggerexpr');
+ $('#function-select').on('change', (e) => {
+ var form = e.target.closest('form'),
+ function_name_parts = form.elements.function_select.value.split('_');
+
+ form.elements.function.value = function_name_parts[1];
+
+ reloadPopup(form, 'popup.triggerexpr');
});
});
diff --git a/ui/app/views/js/popup.valuemap.edit.js.php b/ui/app/views/js/popup.valuemap.edit.js.php
index 46047a57289..7fa3950cb14 100644
--- a/ui/app/views/js/popup.valuemap.edit.js.php
+++ b/ui/app/views/js/popup.valuemap.edit.js.php
@@ -24,7 +24,54 @@
*/
?>
$(() => {
- $('#mappings_table').dynamicRows({template: '#mapping-row-tmpl', rows: <?= json_encode($data['mappings']) ?>});
+ let VALUEMAP_MAPPING_TYPE_DEFAULT = <?= VALUEMAP_MAPPING_TYPE_DEFAULT ?>;
+ let type_placeholder = <?= json_encode([
+ VALUEMAP_MAPPING_TYPE_EQUAL => _('value'),
+ VALUEMAP_MAPPING_TYPE_GREATER_EQUAL => _('value'),
+ VALUEMAP_MAPPING_TYPE_LESS_EQUAL => _('value'),
+ VALUEMAP_MAPPING_TYPE_IN_RANGE => _('value'),
+ VALUEMAP_MAPPING_TYPE_REGEXP => _('regexp')
+ ]) ?>;
+ let table = document.querySelector('#mappings_table');
+ let observer = new MutationObserver(mutationHandler);
+
+ // Observe changes for form fields: type, value.
+ observer.observe(table, {
+ childList: true,
+ subtree: true,
+ attributes: true,
+ attributeFilter: ['value']
+ });
+ updateOnTypeChange();
+
+ function updateOnTypeChange() {
+ let default_select = table.querySelector(`z-select[value="${VALUEMAP_MAPPING_TYPE_DEFAULT}"]`);
+
+ table.querySelectorAll('tr').forEach((row) => {
+ let zselect = row.querySelector('z-select[name$="[type]"]');
+ let input = row.querySelector('input[name$="[value]"]');
+
+ if (zselect) {
+ zselect.getOptionByValue(VALUEMAP_MAPPING_TYPE_DEFAULT).disabled = (default_select
+ && zselect !== default_select
+ );
+ input.classList.toggle('visibility-hidden', (zselect === default_select));
+ input.disabled = (zselect === default_select);
+ input.setAttribute('placeholder', type_placeholder[zselect.value]||'');
+ }
+ });
+ }
+
+ function mutationHandler(mutation_records, observer) {
+ let update = mutation_records.filter((mutation) => {
+ return (mutation.target.tagName === 'INPUT' && mutation.target.getAttribute('name').substr(-6) === '[type]')
+ || (mutation.target.tagName === 'TBODY' && mutation.removedNodes.length > 0);
+ });
+
+ if (update.length) {
+ updateOnTypeChange();
+ }
+ }
});
function submitValueMap(overlay) {
diff --git a/ui/app/views/popup.generic.php b/ui/app/views/popup.generic.php
index 7d308691736..a0ef5b316ef 100644
--- a/ui/app/views/popup.generic.php
+++ b/ui/app/views/popup.generic.php
@@ -594,18 +594,39 @@ switch ($data['popup_type']) {
->onClick(sprintf($inline_js, $valuemap['id'], $js_action_onclick));
}
- $span = [];
+ $mappings_table = [];
foreach (array_slice($valuemap['mappings'], 0, 3) as $mapping) {
- $span[] = $mapping['value'].' ⇒ '.$mapping['newvalue'];
- $span[] = BR();
- }
+ switch ($mapping['type']) {
+ case VALUEMAP_MAPPING_TYPE_EQUAL:
+ $value = '='.$mapping['value'];
+ break;
+
+ case VALUEMAP_MAPPING_TYPE_GREATER_EQUAL:
+ $value = '>='.$mapping['value'];
+ break;
+
+ case VALUEMAP_MAPPING_TYPE_LESS_EQUAL:
+ $value = '<='.$mapping['value'];
+ break;
+
+ case VALUEMAP_MAPPING_TYPE_DEFAULT:
+ $value = new CTag('em', true, _('default'));
+ break;
+
+ default:
+ $value = $mapping['value'];
+ }
- if (count($valuemap['mappings']) > 3) {
- $span[] = '&hellip;';
+ $mappings_table[] = new CDiv($value);
+ $mappings_table[] = new CDiv('⇒');
+ $mappings_table[] = new CDiv($mapping['newvalue']);
}
- $table->addRow([$check_box, $name, $span]);
+ $hellip = (count($valuemap['mappings']) > 3) ? '&hellip;' : null;
+ $table->addRow([$check_box, $name, [
+ (new CDiv($mappings_table))->addClass(ZBX_STYLE_VALUEMAP_MAPPINGS_TABLE), $hellip
+ ]]);
}
break;
}
diff --git a/ui/app/views/popup.triggerexpr.php b/ui/app/views/popup.triggerexpr.php
index c64ab723e08..84ce023c69b 100644
--- a/ui/app/views/popup.triggerexpr.php
+++ b/ui/app/views/popup.triggerexpr.php
@@ -32,6 +32,7 @@ $expression_form = (new CForm())
->addVar('dstfld1', $data['dstfld1'])
->addItem((new CVar('hostid', $data['hostid']))->removeId())
->addVar('groupid', $data['groupid'])
+ ->addVar('function', $data['function'])
->addItem((new CInput('submit', 'submit'))
->addStyle('display: none;')
->removeId()
@@ -98,13 +99,34 @@ if ($data['item_required']) {
$expression_form_list->addRow((new CLabel(_('Item'), 'item_description'))->setAsteriskMark(), $item);
}
-$function_select = (new CSelect('function'))
+$function_select = (new CSelect('function_select'))
->setFocusableElementId('label-function')
- ->setId('function')
- ->setValue($data['function']);
+ ->setId('function-select')
+ ->setValue($data['function_type'].'_'.$data['function']);
+
+$function_types = [
+ ZBX_FUNCTION_TYPE_AGGREGATE => _('Aggregate functions'),
+ ZBX_FUNCTION_TYPE_BITWISE => _('Bitwise functions'),
+ ZBX_FUNCTION_TYPE_DATE_TIME => _('Date and time functions'),
+ ZBX_FUNCTION_TYPE_HISTORY => _('History functions'),
+ ZBX_FUNCTION_TYPE_MATH => _('Mathematical functions'),
+ ZBX_FUNCTION_TYPE_OPERATOR => _('Operator functions'),
+ ZBX_FUNCTION_TYPE_PREDICTION => _('Prediction functions'),
+ ZBX_FUNCTION_TYPE_STRING => _('String functions')
+];
+
+$functions_by_group = [];
+foreach ($data['functions'] as $id => $function) {
+ foreach ($function['types'] as $type) {
+ $functions_by_group[$function_types[$type]][$type.'_'.$id] = $function['description'];
+ }
+}
+ksort($functions_by_group);
-foreach ($data['functions'] as $id => $f) {
- $function_select->addOption(new CSelectOption($id, $f['description']));
+foreach ($functions_by_group as $group_name => $functions) {
+ $function_select->addOptionGroup(
+ (new CSelectOptionGroup($group_name))->addOptions(CSelect::createOptionsFromArray($functions))
+ );
}
$expression_form_list->addRow(new CLabel(_('Function'), $function_select->getFocusableElementId()), $function_select);
@@ -112,6 +134,14 @@ $expression_form_list->addRow(new CLabel(_('Function'), $function_select->getFoc
if (array_key_exists('params', $data['functions'][$data['selectedFunction']])) {
$paramid = 0;
+ // Functions with optional #num and time shift parameters.
+ $count_functions = [
+ 'acos', 'ascii', 'asin', 'atan', 'atan2', 'between', 'bitand', 'bitlength', 'bitlshift', 'bitnot', 'bitor',
+ 'bitrshift', 'bitxor', 'bytelength', 'cbrt', 'ceil', 'char', 'concat', 'cos', 'cosh', 'cot', 'degrees', 'exp',
+ 'expm1', 'floor', 'in', 'insert', 'last', 'left', 'log', 'log10', 'ltrim', 'mid', 'mod', 'power', 'radians',
+ 'repeat', 'replace', 'right', 'round', 'rtrim', 'signum', 'sin', 'sinh', 'sqrt', 'tan', 'trim', 'truncate'
+ ];
+
foreach ($data['functions'][$data['selectedFunction']]['params'] as $param_name => $param_function) {
if (array_key_exists($param_name, $data['params'])) {
$param_value = $data['params'][$param_name];
@@ -127,7 +157,7 @@ if (array_key_exists('params', $data['functions'][$data['selectedFunction']])) {
if (in_array($param_name, ['last'])) {
if (array_key_exists('M', $param_function)) {
- if (in_array($data['selectedFunction'], ['last', 'bitand', 'strlen'])) {
+ if (in_array($data['selectedFunction'], $count_functions)) {
$param_type_element = $param_function['M'][PARAM_TYPE_COUNTS];
$label = $param_function['C'];
$expression_form->addItem((new CVar('paramtype', PARAM_TYPE_COUNTS))->removeId());
@@ -181,20 +211,22 @@ else {
$expression_form->addVar('paramtype', PARAM_TYPE_TIME);
}
-$expression_form_list->addRow(
- (new CLabel(_('Result'), 'value'))->setAsteriskMark(), [
- (new CSelect('operator'))
- ->setValue($data['operator'])
- ->setFocusableElementId('value')
- ->addOptions(CSelect::createOptionsFromArray(array_combine($data['functions'][$data['function']]['operators'],
- $data['functions'][$data['function']]['operators']
- ))),
- ' ',
- (new CTextBox('value', $data['value']))
- ->setAriaRequired()
- ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH)
- ]
-);
+if (array_key_exists('operators', $data['functions'][$data['selectedFunction']])) {
+ $expression_form_list->addRow(
+ (new CLabel(_('Result'), 'value'))->setAsteriskMark(), [
+ (new CSelect('operator'))
+ ->setValue($data['operator'])
+ ->setFocusableElementId('value')
+ ->addOptions(CSelect::createOptionsFromArray(array_combine($data['functions'][$data['function']]['operators'],
+ $data['functions'][$data['function']]['operators']
+ ))),
+ ' ',
+ (new CTextBox('value', $data['value']))
+ ->setAriaRequired()
+ ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH)
+ ]
+ );
+}
$expression_form->addItem($expression_form_list);
diff --git a/ui/app/views/popup.valuemap.edit.php b/ui/app/views/popup.valuemap.edit.php
index cecf9375494..cf658b345b7 100644
--- a/ui/app/views/popup.valuemap.edit.php
+++ b/ui/app/views/popup.valuemap.edit.php
@@ -23,12 +23,12 @@
* @var CView $this
*/
$form = (new CForm())
- ->cleanItems()
->setId('valuemap-edit-form')
->setName('valuemap-edit-form')
->addVar('action', $data['action'])
->addVar('update', 1)
->addVar('source-name', $data['name'])
+ ->addItem(new CJsScript($this->readJsFile('../../../include/views/js/editabletable.js.php')))
->addItem((new CInput('submit', 'submit'))
->addStyle('display: none;')
->removeId()
@@ -48,19 +48,55 @@ foreach (array_values($data['valuemap_names']) as $index => $name) {
$form_grid = (new CFormGrid())->addClass(CFormGrid::ZBX_STYLE_FORM_GRID_1_1);
-$table = (new CTable())
- ->setId('mappings_table')
- ->addClass(ZBX_STYLE_TABLE_FORMS)
- ->setHeader([_('Value'), '', _('Mapped to'), _('Action')])
- ->addStyle('width: 100%;');
+$header_row = ['', _('Type'), _('Value'), '', _('Mapped to'), _('Action'), ''];
+$mappings = (new CDiv([
+ (new CTable())
+ ->setId('mappings_table')
+ ->addClass(ZBX_STYLE_TABLE_FORMS)
+ ->setHeader($header_row)
+ ->addRow((new CRow)->setAttribute('data-insert-point', 'append'))
+ ->setFooter(new CRow(
+ (new CCol(
+ (new CButton(null, _('Add')))
+ ->addClass(ZBX_STYLE_BTN_LINK)
+ ->setAttribute('data-row-action', 'add_row')
+ ))->setColSpan(count($header_row))
+ ))
+]))->setAttribute('data-sortable-pairs-table', '1');
-$table->addRow([
- (new CCol(
- (new CButton(null, _('Add')))
+// Value map mapping template.
+$mappings->addItem(
+ (new CTag('script', true))
+ ->setAttribute('type', 'text/x-jquery-tmpl')
+ ->addItem((new CRow([
+ (new CCol((new CDiv)
+ ->addClass(ZBX_STYLE_DRAG_ICON)))
+ ->addClass(ZBX_STYLE_TD_DRAG_ICON),
+ (new CSelect('mappings[#{index}][type]'))
+ ->setValue('#{type}')
+ ->addOptions(CSelect::createOptionsFromArray([
+ VALUEMAP_MAPPING_TYPE_EQUAL => _('equals'),
+ VALUEMAP_MAPPING_TYPE_GREATER_EQUAL => _('is greater than or equals'),
+ VALUEMAP_MAPPING_TYPE_LESS_EQUAL => _('is less than or equals'),
+ VALUEMAP_MAPPING_TYPE_IN_RANGE => _('in range'),
+ VALUEMAP_MAPPING_TYPE_REGEXP => _('regexp'),
+ VALUEMAP_MAPPING_TYPE_DEFAULT => _('default')
+ ])),
+ (new CTextBox('mappings[#{index}][value]', '#{value}', false, DB::getFieldLength('valuemap_mapping', 'value')))
+ ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH),
+ '&rArr;',
+ (new CTextBox('mappings[#{index}][newvalue]', '#{newvalue}', false, DB::getFieldLength('valuemap_mapping', 'newvalue')))
+ ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH)
+ ->setAriaRequired(),
+ (new CButton('mappings[#{index}][remove]', _('Remove')))
->addClass(ZBX_STYLE_BTN_LINK)
- ->addClass('element-table-add')
- ))->setColSpan(4)
- ]);
+ ->setAttribute('data-row-action', 'remove_row')
+ ])))
+);
+
+$mappings_data = (new CTag('script', true))->setAttribute('type', 'text/json');
+$mappings_data->items = [json_encode($data['mappings'])];
+$mappings->addItem($mappings_data);
$form_grid
->addItem([
@@ -75,7 +111,7 @@ $form_grid
->addItem([
(new CLabel(_('Mappings'), 'mappings'))->setAsteriskMark(),
(new CFormField(
- (new CDiv($table))
+ (new CDiv($mappings))
->addStyle('width: 100%;')
->addClass('table-forms-separator')
))->addClass(CFormField::ZBX_STYLE_FORM_FIELD_FLUID)
@@ -83,24 +119,9 @@ $form_grid
$form->addItem($form_grid);
-// Value map mapping template.
-$form->addItem((new CScriptTemplate('mapping-row-tmpl'))->addItem(
- (new CRow([
- (new CTextBox('mappings[#{rowNum}][value]', '#{value}', false, DB::getFieldLength('valuemap_mapping', 'value')))
- ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH),
- '&rArr;',
- (new CTextBox('mappings[#{rowNum}][newvalue]', '#{newvalue}', false, DB::getFieldLength('valuemap_mapping', 'newvalue')))
- ->setWidth(ZBX_TEXTAREA_SMALL_WIDTH)
- ->setAriaRequired(),
- (new CButton('mappings[#{rowNum}][remove]', _('Remove')))
- ->addClass(ZBX_STYLE_BTN_LINK)
- ->addClass('element-table-remove')
- ]))->addClass('form_row')
-));
-
$output = [
'header' => $data['title'],
- 'script_inline' => $this->readJsFile('popup.valuemap.edit.js.php', ['mappings' => $data['mappings']]),
+ 'script_inline' => $this->readJsFile('popup.valuemap.edit.js.php', []),
'body' => $form->toString(),
'buttons' => [
[
diff --git a/ui/assets/styles/blue-theme.css b/ui/assets/styles/blue-theme.css
index 70bdc73a7a3..454f246990e 100644
--- a/ui/assets/styles/blue-theme.css
+++ b/ui/assets/styles/blue-theme.css
@@ -2896,6 +2896,15 @@ form.is-loading {
padding-top: 5px; }
.table-forms tfoot button, .table-forms .tfoot-buttons button {
margin: 0 10px 0 0; }
+ .table-forms td {
+ position: relative; }
+ .table-forms td.td-drag-icon {
+ padding: 0 11px 0 0;
+ vertical-align: middle; }
+ .table-forms td .drag-icon {
+ position: absolute;
+ top: 5px;
+ margin-right: 5px; }
.table-forms .table-forms-td-left {
display: table-cell;
padding: 5px 0;
@@ -2919,12 +2928,7 @@ form.is-loading {
padding: 5px 5px 5px 0;
position: relative; }
.table-forms .table-forms-td-right td.td-drag-icon {
- padding: 0 11px 0 0;
- vertical-align: middle; }
- .table-forms .table-forms-td-right td .drag-icon {
- position: absolute;
- top: 5px;
- margin-right: 5px; }
+ padding-right: 11px; }
.table-forms .table-forms-td-right td.center {
text-align: center;
vertical-align: middle; }
@@ -5350,6 +5354,9 @@ svg {
.display-none, .table-forms li.display-none {
display: none; }
+.visibility-hidden {
+ visibility: hidden; }
+
.checkbox-list li {
overflow: hidden;
text-overflow: ellipsis;
@@ -6358,6 +6365,15 @@ z-select.z-select-host-interface li[disabled] .description:not(:empty),
.valuemap-list-table tbody tr:first-child td {
border-top: 1px solid #ebeef0; }
+.mappings-table {
+ display: grid;
+ grid-template-columns: auto auto minmax(auto, 100%); }
+ .mappings-table > div {
+ text-align: left; }
+ .mappings-table > div:nth-child(3n + 2) {
+ text-align: center;
+ padding: 0 10px; }
+
.valuemap-checkbox {
margin-top: 10px; }
diff --git a/ui/assets/styles/dark-theme.css b/ui/assets/styles/dark-theme.css
index 2ac7097fa9e..e8660aafd2d 100644
--- a/ui/assets/styles/dark-theme.css
+++ b/ui/assets/styles/dark-theme.css
@@ -2907,6 +2907,15 @@ form.is-loading {
padding-top: 5px; }
.table-forms tfoot button, .table-forms .tfoot-buttons button {
margin: 0 10px 0 0; }
+ .table-forms td {
+ position: relative; }
+ .table-forms td.td-drag-icon {
+ padding: 0 11px 0 0;
+ vertical-align: middle; }
+ .table-forms td .drag-icon {
+ position: absolute;
+ top: 5px;
+ margin-right: 5px; }
.table-forms .table-forms-td-left {
display: table-cell;
padding: 5px 0;
@@ -2930,12 +2939,7 @@ form.is-loading {
padding: 5px 5px 5px 0;
position: relative; }
.table-forms .table-forms-td-right td.td-drag-icon {
- padding: 0 11px 0 0;
- vertical-align: middle; }
- .table-forms .table-forms-td-right td .drag-icon {
- position: absolute;
- top: 5px;
- margin-right: 5px; }
+ padding-right: 11px; }
.table-forms .table-forms-td-right td.center {
text-align: center;
vertical-align: middle; }
@@ -5361,6 +5365,9 @@ svg {
.display-none, .table-forms li.display-none {
display: none; }
+.visibility-hidden {
+ visibility: hidden; }
+
.checkbox-list li {
overflow: hidden;
text-overflow: ellipsis;
@@ -6369,6 +6376,15 @@ z-select.z-select-host-interface li[disabled] .description:not(:empty),
.valuemap-list-table tbody tr:first-child td {
border-top: 1px solid #383838; }
+.mappings-table {
+ display: grid;
+ grid-template-columns: auto auto minmax(auto, 100%); }
+ .mappings-table > div {
+ text-align: left; }
+ .mappings-table > div:nth-child(3n + 2) {
+ text-align: center;
+ padding: 0 10px; }
+
.valuemap-checkbox {
margin-top: 10px; }
diff --git a/ui/assets/styles/hc-dark.css b/ui/assets/styles/hc-dark.css
index e60dacd8873..ddc496a50dd 100644
--- a/ui/assets/styles/hc-dark.css
+++ b/ui/assets/styles/hc-dark.css
@@ -2860,6 +2860,15 @@ form.is-loading {
padding-top: 5px; }
.table-forms tfoot button, .table-forms .tfoot-buttons button {
margin: 0 10px 0 0; }
+ .table-forms td {
+ position: relative; }
+ .table-forms td.td-drag-icon {
+ padding: 0 11px 0 0;
+ vertical-align: middle; }
+ .table-forms td .drag-icon {
+ position: absolute;
+ top: 5px;
+ margin-right: 5px; }
.table-forms .table-forms-td-left {
display: table-cell;
padding: 5px 0;
@@ -2883,12 +2892,7 @@ form.is-loading {
padding: 5px 5px 5px 0;
position: relative; }
.table-forms .table-forms-td-right td.td-drag-icon {
- padding: 0 11px 0 0;
- vertical-align: middle; }
- .table-forms .table-forms-td-right td .drag-icon {
- position: absolute;
- top: 5px;
- margin-right: 5px; }
+ padding-right: 11px; }
.table-forms .table-forms-td-right td.center {
text-align: center;
vertical-align: middle; }
@@ -5305,6 +5309,9 @@ svg {
.display-none, .table-forms li.display-none {
display: none; }
+.visibility-hidden {
+ visibility: hidden; }
+
.checkbox-list li {
overflow: hidden;
text-overflow: ellipsis;
@@ -6313,6 +6320,15 @@ z-select.z-select-host-interface li[disabled] .description:not(:empty),
.valuemap-list-table tbody tr:first-child td {
border-top: 1px solid #333333; }
+.mappings-table {
+ display: grid;
+ grid-template-columns: auto auto minmax(auto, 100%); }
+ .mappings-table > div {
+ text-align: left; }
+ .mappings-table > div:nth-child(3n + 2) {
+ text-align: center;
+ padding: 0 10px; }
+
.valuemap-checkbox {
margin-top: 10px; }
diff --git a/ui/assets/styles/hc-light.css b/ui/assets/styles/hc-light.css
index 9675b39eefd..d54eefc5b70 100644
--- a/ui/assets/styles/hc-light.css
+++ b/ui/assets/styles/hc-light.css
@@ -2860,6 +2860,15 @@ form.is-loading {
padding-top: 5px; }
.table-forms tfoot button, .table-forms .tfoot-buttons button {
margin: 0 10px 0 0; }
+ .table-forms td {
+ position: relative; }
+ .table-forms td.td-drag-icon {
+ padding: 0 11px 0 0;
+ vertical-align: middle; }
+ .table-forms td .drag-icon {
+ position: absolute;
+ top: 5px;
+ margin-right: 5px; }
.table-forms .table-forms-td-left {
display: table-cell;
padding: 5px 0;
@@ -2883,12 +2892,7 @@ form.is-loading {
padding: 5px 5px 5px 0;
position: relative; }
.table-forms .table-forms-td-right td.td-drag-icon {
- padding: 0 11px 0 0;
- vertical-align: middle; }
- .table-forms .table-forms-td-right td .drag-icon {
- position: absolute;
- top: 5px;
- margin-right: 5px; }
+ padding-right: 11px; }
.table-forms .table-forms-td-right td.center {
text-align: center;
vertical-align: middle; }
@@ -5305,6 +5309,9 @@ svg {
.display-none, .table-forms li.display-none {
display: none; }
+.visibility-hidden {
+ visibility: hidden; }
+
.checkbox-list li {
overflow: hidden;
text-overflow: ellipsis;
@@ -6313,6 +6320,15 @@ z-select.z-select-host-interface li[disabled] .description:not(:empty),
.valuemap-list-table tbody tr:first-child td {
border-top: 1px solid #888888; }
+.mappings-table {
+ display: grid;
+ grid-template-columns: auto auto minmax(auto, 100%); }
+ .mappings-table > div {
+ text-align: left; }
+ .mappings-table > div:nth-child(3n + 2) {
+ text-align: center;
+ padding: 0 10px; }
+
.valuemap-checkbox {
margin-top: 10px; }
diff --git a/ui/hosts.php b/ui/hosts.php
index 049f8a835a0..eef5649dd55 100644
--- a/ui/hosts.php
+++ b/ui/hosts.php
@@ -842,13 +842,6 @@ if (hasRequest('form')) {
// Valuemap
order_result($dbHost['valuemaps'], 'name');
-
- foreach ($dbHost['valuemaps'] as &$valuemap) {
- order_result($valuemap['mappings'], 'value');
- $valuemap['mappings'] = array_values($valuemap['mappings']);
- }
- unset($valuemap);
-
$data['valuemaps'] = array_values($dbHost['valuemaps']);
$groups = zbx_objectValues($dbHost['groups'], 'groupid');
diff --git a/ui/include/classes/api/services/CHostGeneral.php b/ui/include/classes/api/services/CHostGeneral.php
index c6d52aada11..3f78404ab10 100644
--- a/ui/include/classes/api/services/CHostGeneral.php
+++ b/ui/include/classes/api/services/CHostGeneral.php
@@ -831,13 +831,15 @@ abstract class CHostGeneral extends CHostBase {
if ($this->outputIsRequested('mappings', $options['selectValueMaps']) && $valuemaps) {
$params = [
- 'output' => ['valuemapid', 'value', 'newvalue'],
- 'filter' => ['valuemapid' => array_keys($valuemaps)]
+ 'output' => ['valuemapid', 'type', 'value', 'newvalue'],
+ 'filter' => ['valuemapid' => array_keys($valuemaps)],
+ 'sortfield' => ['sortorder']
];
$query = DBselect(DB::makeSql('valuemap_mapping', $params));
while ($mapping = DBfetch($query)) {
$valuemaps[$mapping['valuemapid']]['mappings'][] = [
+ 'type' => $mapping['type'],
'value' => $mapping['value'],
'newvalue' => $mapping['newvalue']
];
diff --git a/ui/include/classes/api/services/CItemGeneral.php b/ui/include/classes/api/services/CItemGeneral.php
index a6415fab9c1..f3966f23417 100644
--- a/ui/include/classes/api/services/CItemGeneral.php
+++ b/ui/include/classes/api/services/CItemGeneral.php
@@ -2073,13 +2073,15 @@ abstract class CItemGeneral extends CApiService {
if ($this->outputIsRequested('mappings', $options['selectValueMap']) && $valuemaps) {
$params = [
- 'output' => ['valuemapid', 'value', 'newvalue'],
- 'filter' => ['valuemapid' => array_keys($valuemaps)]
+ 'output' => ['valuemapid', 'type', 'value', 'newvalue'],
+ 'filter' => ['valuemapid' => array_keys($valuemaps)],
+ 'sortfield' => ['sortorder']
];
$query = DBselect(DB::makeSql('valuemap_mapping', $params));
while ($mapping = DBfetch($query)) {
$valuemaps[$mapping['valuemapid']]['mappings'][] = [
+ 'type' => $mapping['type'],
'value' => $mapping['value'],
'newvalue' => $mapping['newvalue']
];
diff --git a/ui/include/classes/api/services/CValueMap.php b/ui/include/classes/api/services/CValueMap.php
index 3666d5881aa..891d85a86d6 100644
--- a/ui/include/classes/api/services/CValueMap.php
+++ b/ui/include/classes/api/services/CValueMap.php
@@ -61,7 +61,7 @@ class CValueMap extends CApiService {
'searchWildcardsEnabled' => ['type' => API_BOOLEAN, 'default' => false],
// output
'output' => ['type' => API_OUTPUT, 'in' => implode(',', ['valuemapid', 'uuid', 'name', 'hostid']), 'default' => API_OUTPUT_EXTEND],
- 'selectMappings' => ['type' => API_OUTPUT, 'flags' => API_ALLOW_NULL | API_ALLOW_COUNT, 'in' => implode(',', ['value', 'newvalue']), 'default' => null],
+ 'selectMappings' => ['type' => API_OUTPUT, 'flags' => API_ALLOW_NULL | API_ALLOW_COUNT, 'in' => implode(',', ['type', 'value', 'newvalue']), 'default' => null],
'countOutput' => ['type' => API_FLAG, 'default' => false],
// sort and limit
'sortfield' => ['type' => API_STRINGS_UTF8, 'flags' => API_NORMALIZE, 'in' => implode(',', $this->sortColumns), 'uniq' => true, 'default' => []],
@@ -139,12 +139,15 @@ class CValueMap extends CApiService {
foreach ($valuemaps as $index => &$valuemap) {
$valuemap['valuemapid'] = $valuemapids[$index];
+ $sortorder = 0;
foreach ($valuemap['mappings'] as $mapping) {
$mappings[] = [
+ 'type' => array_key_exists('type', $mapping) ? $mapping['type'] : VALUEMAP_MAPPING_TYPE_EQUAL,
'valuemapid' => $valuemap['valuemapid'],
- 'value' => $mapping['value'],
- 'newvalue' => $mapping['newvalue']
+ 'value' => array_key_exists('value', $mapping) ? $mapping['value'] : '',
+ 'newvalue' => $mapping['newvalue'],
+ 'sortorder' => $sortorder++
];
}
}
@@ -166,7 +169,7 @@ class CValueMap extends CApiService {
$this->validateUpdate($valuemaps, $db_valuemaps);
$upd_valuemaps = [];
- $mappings = [];
+ $valuemaps_mappings = [];
foreach ($valuemaps as $valuemap) {
$valuemapid = $valuemap['valuemapid'];
@@ -181,9 +184,17 @@ class CValueMap extends CApiService {
}
if (array_key_exists('mappings', $valuemap)) {
- $mappings[$valuemapid] = [];
+ $valuemaps_mappings[$valuemapid] = [];
+ $sortorder = 0;
+
foreach ($valuemap['mappings'] as $mapping) {
- $mappings[$valuemapid][$mapping['value']] = $mapping['newvalue'];
+ $mapping += ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => ''];
+ $valuemaps_mappings[$valuemapid][] = [
+ 'type' => $mapping['type'],
+ 'value' => $mapping['value'],
+ 'newvalue' => $mapping['newvalue'],
+ 'sortorder' => $sortorder++
+ ];
}
}
}
@@ -192,37 +203,57 @@ class CValueMap extends CApiService {
DB::update('valuemap', $upd_valuemaps);
}
- if ($mappings) {
+ if ($valuemaps_mappings) {
$db_mappings = DB::select('valuemap_mapping', [
- 'output' => ['valuemap_mappingid', 'valuemapid', 'value', 'newvalue'],
- 'filter' => ['valuemapid' => array_keys($mappings)]
+ 'output' => ['valuemap_mappingid', 'valuemapid', 'type', 'value', 'newvalue', 'sortorder'],
+ 'filter' => ['valuemapid' => array_keys($valuemaps_mappings)]
]);
+ CArrayHelper::sort($db_mappings, [['field' => 'sortorder', 'order' => ZBX_SORT_UP]]);
$ins_mapings = [];
$upd_mapings = [];
$del_mapingids = [];
+ $valuemapid_db_mappings = array_fill_keys(array_keys($valuemaps_mappings), []);
foreach ($db_mappings as $db_mapping) {
- $mapping = &$mappings[$db_mapping['valuemapid']];
+ $valuemapid_db_mappings[$db_mapping['valuemapid']][] = $db_mapping;
+ }
+
+ foreach ($valuemaps_mappings as $valuemapid => $mappings) {
+ $db_mappings = &$valuemapid_db_mappings[$valuemapid];
+
+ foreach ($mappings as $mapping) {
+ $exists = false;
+
+ foreach ($db_mappings as $i => $db_mapping) {
+ if ($db_mapping['type'] == $mapping['type'] && $db_mapping['value'] == $mapping['value']) {
+ $exists = true;
+ break;
+ }
+ }
+
+ if (!$exists) {
+ $ins_mapings[] = ['valuemapid' => $valuemapid] + $mapping;
+ continue;
+ }
+
+ $update_fields = array_diff_assoc($mapping, $db_mapping);
- if (array_key_exists($db_mapping['value'], $mapping)) {
- if ($mapping[$db_mapping['value']] !== $db_mapping['newvalue']) {
+ if ($update_fields) {
$upd_mapings[] = [
- 'values' => ['newvalue' => $mapping[$db_mapping['value']]],
+ 'values' => $update_fields,
'where' => ['valuemap_mappingid' => $db_mapping['valuemap_mappingid']]
];
}
- unset($mapping[$db_mapping['value']]);
- }
- else {
- $del_mapingids[] = $db_mapping['valuemap_mappingid'];
+
+ unset($db_mappings[$i]);
}
}
- unset($mapping);
+ unset($db_mappings);
- foreach ($mappings as $valuemapid => $mapping) {
- foreach ($mapping as $value => $newvalue) {
- $ins_mapings[] = ['valuemapid' => $valuemapid, 'value' => $value, 'newvalue' => $newvalue];
+ foreach ($valuemapid_db_mappings as $db_mappings) {
+ if ($db_mappings) {
+ $del_mapingids = array_merge($del_mapingids, array_column($db_mappings, 'valuemap_mappingid'));
}
}
@@ -306,7 +337,6 @@ class CValueMap extends CApiService {
}
}
-
/**
* @param array $valuemaps
*
@@ -317,8 +347,36 @@ class CValueMap extends CApiService {
'hostid' => ['type' => API_ID, 'flags' => API_REQUIRED | API_NOT_EMPTY],
'uuid' => ['type' => API_UUID],
'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('valuemap', 'name')],
- 'mappings' => ['type' => API_OBJECTS, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'uniq' => [['value']], 'fields' => [
- 'value' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('valuemap_mapping', 'value')],
+ 'mappings' => ['type' => API_OBJECTS, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'fields' => [
+ 'type' => ['type' => API_INT32, 'default' => VALUEMAP_MAPPING_TYPE_EQUAL, 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_EQUAL, VALUEMAP_MAPPING_TYPE_GREATER_EQUAL, VALUEMAP_MAPPING_TYPE_LESS_EQUAL, VALUEMAP_MAPPING_TYPE_IN_RANGE, VALUEMAP_MAPPING_TYPE_REGEXP, VALUEMAP_MAPPING_TYPE_DEFAULT])],
+ 'value' => ['type' => API_MULTIPLE, 'rules' => [
+ [
+ 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_EQUAL])],
+ 'type' => API_STRING_UTF8,
+ 'length' => DB::getFieldLength('valuemap_mapping', 'value')
+ ],
+ [
+ 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_GREATER_EQUAL, VALUEMAP_MAPPING_TYPE_LESS_EQUAL])],
+ 'type' => API_FLOAT,
+ 'length' => DB::getFieldLength('valuemap_mapping', 'value')
+ ],
+ [
+ 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_IN_RANGE])],
+ 'type' => API_NUMERIC_RANGES,
+ 'flags' => API_NOT_EMPTY,
+ 'length' => DB::getFieldLength('valuemap_mapping', 'value')
+ ],
+ [
+ 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_REGEXP])],
+ 'type' => API_REGEX,
+ 'flags' => API_NOT_EMPTY,
+ 'length' => DB::getFieldLength('valuemap_mapping', 'value')
+ ],
+ [
+ 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_DEFAULT])],
+ 'type' => API_STRING_UTF8
+ ]
+ ]],
'newvalue' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('valuemap_mapping', 'newvalue')]
]]
]];
@@ -326,7 +384,9 @@ class CValueMap extends CApiService {
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
+ $this->validateValuemapMappings($valuemaps);
$hostids = [];
+
foreach ($valuemaps as $valuemap) {
$hostids[$valuemap['hostid']] = true;
}
@@ -403,8 +463,36 @@ class CValueMap extends CApiService {
$api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['valuemapid']], 'fields' => [
'valuemapid' => ['type' => API_ID, 'flags' => API_REQUIRED],
'name' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('valuemap', 'name')],
- 'mappings' => ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY, 'uniq' => [['value']], 'fields' => [
- 'value' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('valuemap_mapping', 'value')],
+ 'mappings' => ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY, 'fields' => [
+ 'type' => ['type' => API_INT32, 'default' => VALUEMAP_MAPPING_TYPE_EQUAL, 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_EQUAL, VALUEMAP_MAPPING_TYPE_GREATER_EQUAL, VALUEMAP_MAPPING_TYPE_LESS_EQUAL, VALUEMAP_MAPPING_TYPE_IN_RANGE, VALUEMAP_MAPPING_TYPE_REGEXP, VALUEMAP_MAPPING_TYPE_DEFAULT])],
+ 'value' => ['type' => API_MULTIPLE, 'rules' => [
+ [
+ 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_EQUAL])],
+ 'type' => API_STRING_UTF8,
+ 'length' => DB::getFieldLength('valuemap_mapping', 'value')
+ ],
+ [
+ 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_GREATER_EQUAL, VALUEMAP_MAPPING_TYPE_LESS_EQUAL])],
+ 'type' => API_FLOAT,
+ 'length' => DB::getFieldLength('valuemap_mapping', 'value')
+ ],
+ [
+ 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_IN_RANGE])],
+ 'type' => API_NUMERIC_RANGES,
+ 'flags' => API_NOT_EMPTY,
+ 'length' => DB::getFieldLength('valuemap_mapping', 'value')
+ ],
+ [
+ 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_REGEXP])],
+ 'type' => API_REGEX,
+ 'flags' => API_NOT_EMPTY,
+ 'length' => DB::getFieldLength('valuemap_mapping', 'value')
+ ],
+ [
+ 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_DEFAULT])],
+ 'type' => API_STRING_UTF8
+ ]
+ ]],
'newvalue' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('valuemap_mapping', 'newvalue')]
]]
]];
@@ -412,6 +500,7 @@ class CValueMap extends CApiService {
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
}
+ $this->validateValuemapMappings($valuemaps);
$db_valuemaps = $this->get([
'output' => ['valuemapid', 'hostid', 'name'],
'valuemapids' => array_column($valuemaps, 'valuemapid'),
@@ -449,6 +538,64 @@ class CValueMap extends CApiService {
}
}
+ /**
+ * Validate uniqueness of mapping value in value maps, type VALUEMAP_MAPPING_TYPE_DEFAULT can be defined only once
+ * per value map mappings.
+ *
+ * @param array $valuemaps Array of valuemaps
+ *
+ * @throws Exception when non uniquw
+ */
+ protected function validateValuemapMappings(array $valuemaps) {
+ $i = 0;
+ $error = '';
+
+ foreach ($valuemaps as $valuemap) {
+ $i++;
+
+ if (!array_key_exists('mappings', $valuemap)) {
+ continue;
+ }
+
+ $type_uniq = array_fill_keys([VALUEMAP_MAPPING_TYPE_EQUAL, VALUEMAP_MAPPING_TYPE_GREATER_EQUAL,
+ VALUEMAP_MAPPING_TYPE_LESS_EQUAL, VALUEMAP_MAPPING_TYPE_IN_RANGE, VALUEMAP_MAPPING_TYPE_REGEXP
+ ], []
+ );
+ $has_default = false;
+
+ foreach (array_values($valuemap['mappings']) as $j => $mapping) {
+ $type = array_key_exists('type', $mapping) ? $mapping['type'] : VALUEMAP_MAPPING_TYPE_EQUAL;
+ $value = array_key_exists('value', $mapping) ? (string) $mapping['value'] : '';
+
+ if ($has_default && $type == VALUEMAP_MAPPING_TYPE_DEFAULT) {
+ $error = _s('value %1$s already exists', '(type)=('.VALUEMAP_MAPPING_TYPE_DEFAULT.')');
+ }
+ elseif (!array_key_exists('value', $mapping) && $type != VALUEMAP_MAPPING_TYPE_DEFAULT) {
+ $error = _s('the parameter "%1$s" is missing', 'value');
+ }
+ elseif ($value !== '' && $type == VALUEMAP_MAPPING_TYPE_DEFAULT) {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ sprintf('/%1$s/mappings/%2$s/value', $i, $j + 1),
+ _('should be empty')
+ ));
+ }
+ elseif ($type != VALUEMAP_MAPPING_TYPE_DEFAULT && array_key_exists($value, $type_uniq[$type])) {
+ $error = _s('value %1$s already exists', '(value)=('.$value.')');
+ }
+
+ if ($error !== '') {
+ self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.',
+ sprintf('/%1$s/mappings/%2$s', $i, $j + 1),
+ $error
+ ));
+ }
+
+ $has_default = ($has_default || $type == VALUEMAP_MAPPING_TYPE_DEFAULT);
+ $type_uniq[$type][$value] = true;
+ }
+ }
+ }
+
protected function addRelatedObjects(array $options, array $db_valuemaps) {
$db_valuemaps = parent::addRelatedObjects($options, $db_valuemaps);
@@ -474,13 +621,16 @@ class CValueMap extends CApiService {
}
else {
$db_mappings = API::getApiService()->select('valuemap_mapping', [
- 'output' => $this->outputExtend($options['selectMappings'], ['valuemapid']),
+ 'output' => $this->outputExtend($options['selectMappings'], ['valuemapid',
+ 'valuemap_mappingid', 'sortorder'
+ ]),
'filter' => ['valuemapid' => array_keys($db_valuemaps)]
]);
+ CArrayHelper::sort($db_mappings, [['field' => 'sortorder', 'order' => ZBX_SORT_UP]]);
foreach ($db_mappings as $db_mapping) {
$valuemapid = $db_mapping['valuemapid'];
- unset($db_mapping['valuemap_mappingid'], $db_mapping['valuemapid']);
+ unset($db_mapping['valuemap_mappingid'], $db_mapping['valuemapid'], $db_mapping['sortorder']);
$db_valuemaps[$valuemapid]['mappings'][] = $db_mapping;
}
diff --git a/ui/include/classes/data/CHistFunctionData.php b/ui/include/classes/data/CHistFunctionData.php
index 150c7f98e02..f4ed868fbb7 100644
--- a/ui/include/classes/data/CHistFunctionData.php
+++ b/ui/include/classes/data/CHistFunctionData.php
@@ -26,8 +26,9 @@ final class CHistFunctionData {
public const PERIOD_MODE_DEFAULT = 0;
public const PERIOD_MODE_SEC = 1;
- public const PERIOD_MODE_NUM = 2;
- public const PERIOD_MODE_TREND = 3;
+ public const PERIOD_MODE_SEC_ONLY = 2;
+ public const PERIOD_MODE_NUM_ONLY = 3;
+ public const PERIOD_MODE_TREND = 4;
/**
* Known history functions along with definition of parameters.
@@ -41,7 +42,7 @@ final class CHistFunctionData {
],
'avg_foreach' => [
['rules' => [['type' => 'query']]],
- ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC]]]
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC_ONLY]]]
],
'count' => [
['rules' => [['type' => 'query']]],
@@ -51,9 +52,17 @@ final class CHistFunctionData {
],
['required' => false]
],
+ 'countunique' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]],
+ ['rules' => [['type' => 'regexp', 'pattern' => '/^(eq|ne|gt|ge|lt|le|like|bitand|regexp|iregexp)$/']],
+ 'required' => false
+ ],
+ ['required' => false]
+ ],
'count_foreach' => [
['rules' => [['type' => 'query']]],
- ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC]]]
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC_ONLY]]]
],
'change' => [
['rules' => [['type' => 'query']]]
@@ -66,6 +75,10 @@ final class CHistFunctionData {
],
['required' => false]
],
+ 'first' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC]]]
+ ],
'forecast' => [
['rules' => [['type' => 'query']]],
['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]],
@@ -79,35 +92,43 @@ final class CHistFunctionData {
['rules' => [['type' => 'query']]],
['rules' => [['type' => 'time', 'min' => 1]]]
],
+ 'kurtosis' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]]
+ ],
'last' => [
['rules' => [['type' => 'query']]],
- ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_NUM]], 'required' => false]
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_NUM_ONLY]], 'required' => false]
],
'last_foreach' => [
['rules' => [['type' => 'query']]],
- ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC]], 'required' => false]
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC_ONLY]], 'required' => false]
],
'logeventid' => [
['rules' => [['type' => 'query']]],
- ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_NUM]], 'required' => false],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_NUM_ONLY]], 'required' => false],
['required' => false]
],
'logseverity' => [
['rules' => [['type' => 'query']]],
- ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_NUM]], 'required' => false]
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_NUM_ONLY]], 'required' => false]
],
'logsource' => [
['rules' => [['type' => 'query']]],
- ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_NUM]], 'required' => false],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_NUM_ONLY]], 'required' => false],
['required' => false]
],
+ 'mad' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]]
+ ],
'max' => [
['rules' => [['type' => 'query']]],
['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]]
],
'max_foreach' => [
['rules' => [['type' => 'query']]],
- ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC]]]
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC_ONLY]]]
],
'min' => [
['rules' => [['type' => 'query']]],
@@ -115,7 +136,7 @@ final class CHistFunctionData {
],
'min_foreach' => [
['rules' => [['type' => 'query']]],
- ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC]]]
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC_ONLY]]]
],
'nodata' => [
['rules' => [['type' => 'query']]],
@@ -130,13 +151,38 @@ final class CHistFunctionData {
['type' => 'number', 'min' => 0, 'max' => 100]
]]
],
+ 'skewness' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]]
+ ],
+
+ 'stddevpop' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]]
+ ],
+ 'stddevsamp' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]]
+ ],
+ 'sumofsquares' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]]
+ ],
+ 'varpop' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]]
+ ],
+ 'varsamp' => [
+ ['rules' => [['type' => 'query']]],
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]]
+ ],
'sum' => [
['rules' => [['type' => 'query']]],
['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_DEFAULT]]]
],
'sum_foreach' => [
['rules' => [['type' => 'query']]],
- ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC]]]
+ ['rules' => [['type' => 'period', 'mode' => self::PERIOD_MODE_SEC_ONLY]]]
],
'timeleft' => [
['rules' => [['type' => 'query']]],
@@ -185,22 +231,32 @@ final class CHistFunctionData {
'avg' => self::ITEM_VALUE_TYPES_NUM,
'avg_foreach' => self::ITEM_VALUE_TYPES_NUM,
'count' => self::ITEM_VALUE_TYPES_ALL,
+ 'countunique' => self::ITEM_VALUE_TYPES_ALL,
'count_foreach' => self::ITEM_VALUE_TYPES_ALL,
'change' => self::ITEM_VALUE_TYPES_ALL,
'find' => self::ITEM_VALUE_TYPES_ALL,
+ 'first' => self::ITEM_VALUE_TYPES_ALL,
'forecast' => self::ITEM_VALUE_TYPES_NUM,
'fuzzytime' => self::ITEM_VALUE_TYPES_NUM,
+ 'kurtosis' => self::ITEM_VALUE_TYPES_NUM,
'last' => self::ITEM_VALUE_TYPES_ALL,
'last_foreach' => self::ITEM_VALUE_TYPES_ALL,
'logeventid' => self::ITEM_VALUE_TYPES_LOG,
'logseverity' => self::ITEM_VALUE_TYPES_LOG,
'logsource' => self::ITEM_VALUE_TYPES_LOG,
+ 'mad' => self::ITEM_VALUE_TYPES_NUM,
'max' => self::ITEM_VALUE_TYPES_NUM,
'max_foreach' => self::ITEM_VALUE_TYPES_NUM,
'min' => self::ITEM_VALUE_TYPES_NUM,
'min_foreach' => self::ITEM_VALUE_TYPES_NUM,
'nodata' => self::ITEM_VALUE_TYPES_ALL,
'percentile' => self::ITEM_VALUE_TYPES_NUM,
+ 'skewness' => self::ITEM_VALUE_TYPES_NUM,
+ 'stddevpop' => self::ITEM_VALUE_TYPES_NUM,
+ 'stddevsamp' => self::ITEM_VALUE_TYPES_NUM,
+ 'sumofsquares' => self::ITEM_VALUE_TYPES_NUM,
+ 'varpop' => self::ITEM_VALUE_TYPES_NUM,
+ 'varsamp' => self::ITEM_VALUE_TYPES_NUM,
'sum' => self::ITEM_VALUE_TYPES_NUM,
'sum_foreach' => self::ITEM_VALUE_TYPES_NUM,
'timeleft' => self::ITEM_VALUE_TYPES_NUM,
diff --git a/ui/include/classes/data/CMathFunctionData.php b/ui/include/classes/data/CMathFunctionData.php
index 8d1b806c840..4130c4a42bb 100644
--- a/ui/include/classes/data/CMathFunctionData.php
+++ b/ui/include/classes/data/CMathFunctionData.php
@@ -25,23 +25,72 @@
final class CMathFunctionData {
/**
- * Known math functions along with number of required parameters (-1 for number of required parameters >= 1).
+ * Known math functions along with number or range of required parameters.
*
* @var array
*/
private const PARAMETERS = [
'abs' => 1,
- 'avg' => -1,
+ 'acos' => 1,
+ 'ascii' => 1,
+ 'asin' => 1,
+ 'atan' => 1,
+ 'atan2' => 2,
+ 'avg' => [1, null],
+ 'between' => 3,
'bitand' => 2,
+ 'bitlength' => 1,
+ 'bitlshift' => 2,
+ 'bitnot' => 1,
+ 'bitor' => 2,
+ 'bitrshift' => 2,
+ 'bitxor' => 2,
+ 'bytelength' => 1,
+ 'cbrt' => 1,
+ 'ceil' => 1,
+ 'char' => 1,
+ 'concat' => 2,
+ 'cos' => 1,
+ 'cosh' => 1,
+ 'cot' => 1,
'date' => 0,
'dayofmonth' => 0,
'dayofweek' => 0,
+ 'degrees' => 1,
+ 'e' => 0,
+ 'exp' => 1,
+ 'expm1' => 1,
+ 'floor' => 1,
+ 'in' => [2, null],
+ 'insert' => 4,
+ 'left' => 2,
'length' => 1,
- 'max' => -1,
- 'min' => -1,
+ 'log' => 1,
+ 'log10' => 1,
+ 'ltrim' => [1, 2],
+ 'max' => [1, null],
+ 'mid' => 3,
+ 'min' => [1, null],
+ 'mod' => 2,
'now' => 0,
- 'sum' => -1,
- 'time' => 0
+ 'pi' => 0,
+ 'power' => 2,
+ 'radians' => 1,
+ 'rand' => 0,
+ 'repeat' => 2,
+ 'replace' => 3,
+ 'right' => 2,
+ 'rtrim' => [1, 2],
+ 'round' => 2,
+ 'sin' => 1,
+ 'sinh' => 1,
+ 'signum' => 1,
+ 'sqrt' => 1,
+ 'sum' => [1, null],
+ 'tan' => 1,
+ 'time' => 0,
+ 'trim' => [1, 2],
+ 'truncate' => 2
];
/**
@@ -56,7 +105,7 @@ final class CMathFunctionData {
}
/**
- * Get known math functions along with number of required parameters (-1 for number of required parameters >= 1).
+ * Get known math functions along with number or range of required parameters.
*
* @return array
*/
diff --git a/ui/include/classes/export/CConfigurationExport.php b/ui/include/classes/export/CConfigurationExport.php
index d1efffabefc..b340043ff5d 100644
--- a/ui/include/classes/export/CConfigurationExport.php
+++ b/ui/include/classes/export/CConfigurationExport.php
@@ -1118,6 +1118,22 @@ class CConfigurationExport {
}
/**
+ * Get value maps for export builder from database.
+ *
+ * @param array $valuemapids
+ *
+ * return array
+ */
+ protected function gatherValueMaps(array $valuemapids) {
+ $this->data['valueMaps'] = API::ValueMap()->get([
+ 'output' => ['valuemapid', 'name'],
+ 'selectMappings' => ['type', 'value', 'newvalue'],
+ 'valuemapids' => $valuemapids,
+ 'preservekeys' => true
+ ]);
+ }
+
+ /**
* Change map elements real database selement id and icons ids to unique field references.
*
* @param array $exportMaps
diff --git a/ui/include/classes/export/CConfigurationExportBuilder.php b/ui/include/classes/export/CConfigurationExportBuilder.php
index b436d2324ca..cf00a382a02 100644
--- a/ui/include/classes/export/CConfigurationExportBuilder.php
+++ b/ui/include/classes/export/CConfigurationExportBuilder.php
@@ -441,16 +441,22 @@ class CConfigurationExportBuilder {
* @param array $valuemaps
*/
protected function formatValueMaps(array $valuemaps) {
- $result = [];
-
CArrayHelper::sort($valuemaps, ['name']);
- foreach ($valuemaps as $valuemap) {
- CArrayHelper::sort($valuemap['mappings'], ['value']);
- $result[] = $valuemap;
+ foreach ($valuemaps as &$valuemap) {
+ foreach ($valuemap['mappings'] as &$mapping) {
+ if ($mapping['type'] == VALUEMAP_MAPPING_TYPE_EQUAL) {
+ unset($mapping['type']);
+ }
+ elseif ($mapping['type'] == VALUEMAP_MAPPING_TYPE_DEFAULT) {
+ unset($mapping['value']);
+ }
+ }
+ unset($mapping);
}
+ unset($valuemap);
- return $result;
+ return $valuemaps;
}
/**
diff --git a/ui/include/classes/helpers/CValueMapHelper.php b/ui/include/classes/helpers/CValueMapHelper.php
index 5977634a412..9e981a1a44f 100644
--- a/ui/include/classes/helpers/CValueMapHelper.php
+++ b/ui/include/classes/helpers/CValueMapHelper.php
@@ -26,14 +26,15 @@ class CValueMapHelper {
* If value map or mapping is not found, unchanged value is returned,
* otherwise mapped value returned in format: "<mapped_value> (<initial_value>)".
*
+ * @param int $value_type Item value type.
* @param string $value Value that mapping should be applied to.
* @param array $valuemap Valuemap array.
* @param array $valuemap['mappings'] (optional) Valuemap mappings array.
*
* @return string
*/
- static public function applyValueMap(string $value, array $valuemap): string {
- $newvalue = static::getMappedValue($value, $valuemap);
+ public static function applyValueMap($value_type, string $value, array $valuemap): string {
+ $newvalue = static::getMappedValue($value_type, $value, $valuemap);
return ($newvalue !== false) ? $newvalue.' ('.$value.')' : $value;
}
@@ -41,19 +42,98 @@ class CValueMapHelper {
/**
* Get mapping for value.
*
+ * @param int $value_type Item value type.
* @param string $value Value that mapping should be applied to.
* @param array $valuemap Valuemap array.
- * @param array $valuemap['mappings'] (optional) Valuemap mappings array.
+ * @param array $valuemap['mappings'] Valuemap mappings array.
*
* @return string|bool If there is no mapping return false, return mapped value otherwise.
*/
- static public function getMappedValue(string $value, array $valuemap) {
- if (!$valuemap) {
- return false;
+ public static function getMappedValue($value_type, string $value, array $valuemap) {
+ $newvalue = false;
+
+ if (array_key_exists('mappings', $valuemap)) {
+ foreach ($valuemap['mappings'] as $mapping) {
+ if ($mapping['type'] == VALUEMAP_MAPPING_TYPE_DEFAULT) {
+ $newvalue = $mapping['newvalue'];
+ }
+ elseif (static::matchMapping($value_type, $value, $mapping)) {
+ $newvalue = $mapping['newvalue'];
+
+ break;
+ }
+ }
}
- $mappings = array_column($valuemap['mappings'], 'newvalue', 'value');
+ return $newvalue;
+ }
+
+ /**
+ * Check value match against single mapping. Return true on success, false otherwise.
+ *
+ * @param int $value_type Item value type.
+ * @param string $value Value to check against mapping.
+ * @param array $mapping Array of single mapping.
+ * @param string $mapping['type'] Type of mapping.
+ * @param string $mapping['value] Value of mapping.
+ */
+ public static function matchMapping($value_type, string $value, array $mapping): bool {
+ $match_numeric = ($value_type == ITEM_VALUE_TYPE_FLOAT || $value_type == ITEM_VALUE_TYPE_UINT64);
+ $result = false;
+
+ switch ($mapping['type']) {
+ case VALUEMAP_MAPPING_TYPE_EQUAL:
+ $result = $match_numeric
+ ? (floatval($value) == floatval($mapping['value']))
+ : ($value === $mapping['value']);
+
+ break;
+
+ case VALUEMAP_MAPPING_TYPE_GREATER_EQUAL:
+ $result = ($match_numeric && floatval($value) >= floatval($mapping['value']));
+
+ break;
+
+ case VALUEMAP_MAPPING_TYPE_LESS_EQUAL:
+ $result = ($match_numeric && floatval($value) <= floatval($mapping['value']));
+
+ break;
+
+ case VALUEMAP_MAPPING_TYPE_IN_RANGE:
+ if (!$match_numeric) {
+ break;
+ }
+
+ $ranges_parser = new CRangesParser(['with_minus' => true, 'with_float' => true, 'with_suffix' => true]);
+
+ if ($ranges_parser->parse($mapping['value']) == CParser::PARSE_SUCCESS) {
+ $value = floatval($value);
+
+ foreach ($ranges_parser->getRanges() as $ranges) {
+ if ($value == floatval($ranges[0])
+ || (count($ranges) == 2 && $value >= floatval($ranges[0])
+ && $value <= floatval($ranges[1]))) {
+ $result = true;
+
+ break;
+ }
+ }
+ }
+
+ break;
+
+ case VALUEMAP_MAPPING_TYPE_REGEXP:
+ $result = (!$match_numeric
+ && @preg_match('/'.str_replace('/', '\/', $mapping['value']).'/', $value) == 1);
+
+ break;
+
+ case VALUEMAP_MAPPING_TYPE_DEFAULT:
+ $result = true;
+
+ break;
+ }
- return array_key_exists($value, $mappings) ? $mappings[$value] : false;
+ return $result;
}
}
diff --git a/ui/include/classes/import/validators/C54XmlValidator.php b/ui/include/classes/import/validators/C54XmlValidator.php
index 3ac4574bb18..ed04c539c54 100644
--- a/ui/include/classes/import/validators/C54XmlValidator.php
+++ b/ui/include/classes/import/validators/C54XmlValidator.php
@@ -383,6 +383,15 @@ class C54XmlValidator extends CXmlValidatorGeneral {
CXmlConstantValue::DASHBOARD_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE => CXmlConstantName::DASHBOARD_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE
];
+ private $VALUEMAP_MAPPING_TYPE = [
+ CXmlConstantValue::MAPPING_EQUAL => CXmlConstantName::MAPPING_EQUAL,
+ CXmlConstantValue::MAPPING_GREATER_EQUAL => CXmlConstantName::MAPPING_GREATER_EQUAL,
+ CXmlConstantValue::MAPPING_LESS_EQUAL => CXmlConstantName::MAPPING_LESS_EQUAL,
+ CXmlConstantValue::MAPPING_IN_RANGE => CXmlConstantName::MAPPING_IN_RANGE,
+ CXmlConstantValue::MAPPING_REGEXP => CXmlConstantName::MAPPING_REGEXP,
+ CXmlConstantValue::MAPPING_DEFAULT => CXmlConstantName::MAPPING_DEFAULT
+ ];
+
/**
* Get validation rules schema.
*
@@ -1094,7 +1103,8 @@ class C54XmlValidator extends CXmlValidatorGeneral {
'name' => ['type' => XML_STRING | XML_REQUIRED],
'mappings' => ['type' => XML_INDEXED_ARRAY | XML_REQUIRED, 'prefix' => 'mapping', 'rules' => [
'mapping' => ['type' => XML_ARRAY, 'rules' => [
- 'value' => ['type' => XML_STRING | XML_REQUIRED],
+ 'type' => ['type' => XML_STRING, 'default' => CXmlConstantValue::MAPPING_EQUAL, 'in' => $this->VALUEMAP_MAPPING_TYPE],
+ 'value' => ['type' => XML_STRING, 'default' => ''],
'newvalue' => ['type' => XML_STRING | XML_REQUIRED]
]]
]]
@@ -1734,7 +1744,8 @@ class C54XmlValidator extends CXmlValidatorGeneral {
'name' => ['type' => XML_STRING | XML_REQUIRED],
'mappings' => ['type' => XML_INDEXED_ARRAY | XML_REQUIRED, 'prefix' => 'mapping', 'rules' => [
'mapping' => ['type' => XML_ARRAY, 'rules' => [
- 'value' => ['type' => XML_STRING | XML_REQUIRED],
+ 'type' => ['type' => XML_STRING, 'default' => CXmlConstantValue::MAPPING_EQUAL, 'in' => $this->VALUEMAP_MAPPING_TYPE],
+ 'value' => ['type' => XML_STRING, 'default' => ''],
'newvalue' => ['type' => XML_STRING | XML_REQUIRED]
]]
]]
diff --git a/ui/include/classes/macros/CMacrosResolverGeneral.php b/ui/include/classes/macros/CMacrosResolverGeneral.php
index f2aefdc3600..2df1a33b7e7 100644
--- a/ui/include/classes/macros/CMacrosResolverGeneral.php
+++ b/ui/include/classes/macros/CMacrosResolverGeneral.php
@@ -388,13 +388,13 @@ class CMacrosResolverGeneral {
$function_parameters = [];
foreach ($function_parser->getParamsRaw()['parameters'] as $param_raw) {
- switch ($param_raw->type) {
+ switch ($param_raw['type']) {
case C10FunctionParser::PARAM_UNQUOTED:
- $function_parameters[] = $param_raw->match;
+ $function_parameters[] = $param_raw['raw'];
break;
case C10FunctionParser::PARAM_QUOTED:
- $function_parameters[] = C10FunctionParser::unquoteParam($param_raw->match);
+ $function_parameters[] = C10FunctionParser::unquoteParam($param_raw['raw']);
break;
}
}
@@ -748,8 +748,9 @@ class CMacrosResolverGeneral {
}
$options = [
- 'output' => ['valuemapid', 'value', 'newvalue'],
- 'filter' => ['valuemapid' => array_keys($valuemapids)]
+ 'output' => ['valuemapid', 'type', 'value', 'newvalue'],
+ 'filter' => ['valuemapid' => array_keys($valuemapids)],
+ 'sortfield' => ['sortorder']
];
$db_mappings = DBselect(DB::makeSql('valuemap_mapping', $options));
@@ -757,6 +758,7 @@ class CMacrosResolverGeneral {
while ($db_mapping = DBfetch($db_mappings)) {
$db_valuemaps[$db_mapping['valuemapid']]['mappings'][] = [
+ 'type' => $db_mapping['type'],
'value' => $db_mapping['value'],
'newvalue' => $db_mapping['newvalue']
];
diff --git a/ui/include/classes/mvc/CRouter.php b/ui/include/classes/mvc/CRouter.php
index 86650769ebf..28bdd78c8c3 100644
--- a/ui/include/classes/mvc/CRouter.php
+++ b/ui/include/classes/mvc/CRouter.php
@@ -187,6 +187,7 @@ class CRouter {
'popup.massupdate.trigger' => ['CControllerPopupMassupdateTrigger', 'layout.json', 'popup.massupdate.trigger'],
'popup.massupdate.triggerprototype' => ['CControllerPopupMassupdateTrigger', 'layout.json', 'popup.massupdate.trigger'],
'popup.valuemap.edit' => ['CControllerPopupValueMapEdit', 'layout.json', 'popup.valuemap.edit'],
+ 'popup.valuemap.update' => ['CControllerPopupValueMapUpdate', 'layout.json', null],
'problem.view' => ['CControllerProblemView', 'layout.htmlpage', 'monitoring.problem.view'],
'problem.view.refresh' => ['CControllerProblemViewRefresh', 'layout.json', null],
'problem.view.csv' => ['CControllerProblemView', 'layout.csv', 'monitoring.problem.view'],
diff --git a/ui/include/classes/parsers/CExpressionParser.php b/ui/include/classes/parsers/CExpressionParser.php
index 0c291b7b35b..2415272e9a4 100644
--- a/ui/include/classes/parsers/CExpressionParser.php
+++ b/ui/include/classes/parsers/CExpressionParser.php
@@ -648,7 +648,7 @@ class CExpressionParser extends CParser {
int $depth): bool {
$p = $pos;
- if (!preg_match('/^([a-z]+)\(/', substr($source, $p), $matches)) {
+ if (!preg_match('/^([a-z0-9]+)\(/', substr($source, $p), $matches)) {
return false;
}
diff --git a/ui/include/classes/parsers/CNumberParser.php b/ui/include/classes/parsers/CNumberParser.php
index 3132f715d58..1e83d03d0e4 100644
--- a/ui/include/classes/parsers/CNumberParser.php
+++ b/ui/include/classes/parsers/CNumberParser.php
@@ -31,6 +31,7 @@ class CNumberParser extends CParser {
*/
private $options = [
'with_minus' => true,
+ 'with_float' => true,
'with_suffix' => false
];
@@ -84,22 +85,25 @@ class CNumberParser extends CParser {
$fragment = substr($source, $pos);
+ $pattern = $this->options['with_float'] ? ZBX_PREG_NUMBER : ZBX_PREG_INT;
$pattern = $this->options['with_suffix']
- ? '/^'.ZBX_PREG_NUMBER.'(?<suffix>['.self::$suffixes.'])?/'
- : '/^'.ZBX_PREG_NUMBER.'/';
+ ? '/^'.$pattern.'(?<suffix>['.self::$suffixes.'])?/'
+ : '/^'.$pattern.'/';
if (!preg_match($pattern, $fragment, $matches)) {
return self::PARSE_FAIL;
}
- if ($matches['number'][0] === '-' && !$this->options['with_minus']) {
+ $number = $this->options['with_float'] ? $matches['number'] : $matches['int'];
+
+ if ($number[0] === '-' && !$this->options['with_minus']) {
return self::PARSE_FAIL;
}
$this->length = strlen($matches[0]);
$this->match = $matches[0];
- $this->number = $matches['number'];
+ $this->number = $number;
$this->suffix = array_key_exists('suffix', $matches) ? $matches['suffix'] : null;
return ($pos + $this->length < strlen($source)) ? self::PARSE_SUCCESS_CONT : self::PARSE_SUCCESS;
diff --git a/ui/include/classes/parsers/CRangeParser.php b/ui/include/classes/parsers/CRangeParser.php
index b5b4a4cc24e..afd2aafd67d 100644
--- a/ui/include/classes/parsers/CRangeParser.php
+++ b/ui/include/classes/parsers/CRangeParser.php
@@ -46,7 +46,15 @@ class CRangeParser extends CParser {
private $lld_macro_function_parser;
/**
- * Range.
+ * Number parser.
+ *
+ * @var CNumberParser
+ */
+ private $number_parser;
+
+ /**
+ * Array of range strings. Range value with suffix will be stored as string of calculated value, value "1K"
+ * will be stored as "1024".
*
* @var array
*/
@@ -55,23 +63,32 @@ class CRangeParser extends CParser {
/**
* Options to initialize other parsers.
*
+ * usermacros Allow usermacros in ranges.
+ * lldmacros Allow lldmacros in ranges.
+ * with_minus Allow negative ranges.
+ * with_float Allow float number ranges.
+ * with_suffix Allow number ranges with suffix, supported suffixes see CNumberParser::$suffixes.
+ *
* @var array
*/
private $options = [
'usermacros' => false,
- 'lldmacros' => false
+ 'lldmacros' => false,
+ 'with_minus' => false,
+ 'with_float' => false,
+ 'with_suffix' => false
];
/**
* @param array $options An array of options to initialize other parsers.
*/
public function __construct($options = []) {
- if (array_key_exists('usermacros', $options)) {
- $this->options['usermacros'] = $options['usermacros'];
- }
- if (array_key_exists('lldmacros', $options)) {
- $this->options['lldmacros'] = $options['lldmacros'];
- }
+ $this->options = $options + $this->options;
+ $this->number_parser = new CNumberParser([
+ 'with_minus' => $this->options['with_minus'],
+ 'with_float' => $this->options['with_float'],
+ 'with_suffix' => $this->options['with_suffix']
+ ]);
if ($this->options['usermacros']) {
$this->user_macro_parser = new CUserMacroParser();
@@ -91,6 +108,8 @@ class CRangeParser extends CParser {
* {$M}-{$M}
* {#M}-{#M}
* {$M}-{{#M}.regsub("^([0-9]+)", "{#M}: \1")}
+ * -200--10
+ * -2.5--1.35
*
* @param string $source Source string that needs to be parsed.
* @param int $pos Position offset.
@@ -196,21 +215,25 @@ class CRangeParser extends CParser {
* @return bool|array Returns false if non-numeric character found else returns array of position and match.
*/
private function parseDigits($source, &$pos) {
- if (!preg_match('/^([0-9]+)/', substr($source, $pos), $matches)) {
+ if ($this->number_parser->parse($source, $pos) == CParser::PARSE_FAIL) {
return false;
}
- if ($matches[0] > ZBX_MAX_INT32) {
+ $value = $this->number_parser->calcValue();
+
+ if ($value > ZBX_MAX_INT32) {
return false;
}
// Second value must be greater than or equal to first one.
- if ($this->range && ctype_digit($this->range[0]) && $this->range[0] > $matches[0]) {
+ if ($this->range && is_numeric($this->range[0]) && $this->range[0] > $value) {
return false;
}
- $pos += strlen($matches[0]);
- $this->range[] = $matches[0];
+ $pos += $this->number_parser->getLength();
+ $this->range[] = ($this->number_parser->getSuffix() === null)
+ ? $this->number_parser->getMatch()
+ : strval($value);
return true;
}
diff --git a/ui/include/classes/parsers/CRangesParser.php b/ui/include/classes/parsers/CRangesParser.php
index f0053f7eb7f..6f3df36c16e 100644
--- a/ui/include/classes/parsers/CRangesParser.php
+++ b/ui/include/classes/parsers/CRangesParser.php
@@ -32,7 +32,8 @@ class CRangesParser extends CParser {
private $range_parser;
/**
- * Array of status code ranges.
+ * Array of ranges strings. Range value with suffix will be stored as string of calculated value, value "1K"
+ * will be stored as "1024".
*
* @var array
*/
@@ -41,28 +42,29 @@ class CRangesParser extends CParser {
/**
* Options to initialize other parsers.
*
+ * usermacros Allow usermacros in ranges.
+ * lldmacros Allow lldmacros in ranges.
+ * with_minus Allow negative ranges.
+ * with_float Allow float number ranges.
+ * with_suffix Allow number ranges with suffix, supported suffixes see CNumberParser::$suffixes.
+ *
* @var array
*/
private $options = [
'usermacros' => false,
- 'lldmacros' => false
+ 'lldmacros' => false,
+ 'with_minus' => false,
+ 'with_float' => false,
+ 'with_suffix' => false
];
/**
* @param array $options An array of options to initialize other parsers.
*/
public function __construct($options = []) {
- if (array_key_exists('usermacros', $options)) {
- $this->options['usermacros'] = $options['usermacros'];
- }
- if (array_key_exists('lldmacros', $options)) {
- $this->options['lldmacros'] = $options['lldmacros'];
- }
+ $this->options = $options + $this->options;
- $this->range_parser = new CRangeParser([
- 'usermacros' => $this->options['usermacros'],
- 'lldmacros' => $this->options['lldmacros']
- ]);
+ $this->range_parser = new CRangeParser($this->options);
}
/**
diff --git a/ui/include/classes/screens/CScreenHistory.php b/ui/include/classes/screens/CScreenHistory.php
index a81ccb8c284..56e0fa25251 100644
--- a/ui/include/classes/screens/CScreenHistory.php
+++ b/ui/include/classes/screens/CScreenHistory.php
@@ -424,7 +424,7 @@ class CScreenHistory extends CScreenBase {
$value = formatFloat($value, null, ZBX_UNITS_ROUNDOFF_UNSUFFIXED);
}
- $value = CValueMapHelper::applyValueMap($value, $item['valuemap']);
+ $value = CValueMapHelper::applyValueMap($item['value_type'], $value, $item['valuemap']);
$history_table->addRow([
(new CCol(zbx_date2str(DATE_TIME_FORMAT_SECONDS, $history_row['clock'])))
@@ -508,7 +508,7 @@ class CScreenHistory extends CScreenBase {
$value = formatFloat($value, null, ZBX_UNITS_ROUNDOFF_UNSUFFIXED);
}
- $value = CValueMapHelper::applyValueMap($value, $item['valuemap']);
+ $value = CValueMapHelper::applyValueMap($item['value_type'], $value, $item['valuemap']);
$row[] = ($value === '') ? '' : new CPre($value);
}
diff --git a/ui/include/classes/validators/CApiInputValidator.php b/ui/include/classes/validators/CApiInputValidator.php
index 7f32aeee294..417dd134634 100644
--- a/ui/include/classes/validators/CApiInputValidator.php
+++ b/ui/include/classes/validators/CApiInputValidator.php
@@ -196,6 +196,9 @@ class CApiInputValidator {
case API_DATE:
return self::validateDate($rule, $data, $path, $error);
+ case API_NUMERIC_RANGES:
+ return self::validateNumericRanges($rule, $data, $path, $error);
+
case API_UUID:
return self::validateUuid($rule, $data, $path, $error);
}
@@ -254,6 +257,7 @@ class CApiInputValidator {
case API_JSONRPC_PARAMS:
case API_JSONRPC_ID:
case API_DATE:
+ case API_NUMERIC_RANGES:
case API_UUID:
return true;
@@ -2184,6 +2188,48 @@ class CApiInputValidator {
}
/**
+ * Validate numeric ranges. Multiple ranges separated by comma character.
+ * Example:
+ * 10-20,-20--10,-5-0,0.5-0.7,-20--10,-20.20--20.10
+ * 30,-10,0.7,-0.5
+ *
+ * @param array $rule
+ * @param int $rule['flags'] (optional) API_NOT_EMPTY
+ * @param int $rule['length'] (optional)
+ * @param mixed $data
+ * @param string $path
+ * @param string $error
+ *
+ * @return bool
+ */
+ private static function validateNumericRanges($rule, &$data, $path, &$error) {
+ $flags = array_key_exists('flags', $rule) ? $rule['flags'] : 0x00;
+
+ if (self::checkStringUtf8($flags & API_NOT_EMPTY, $data, $path, $error) === false) {
+ return false;
+ }
+
+ if (($flags & API_NOT_EMPTY) == 0 && $data === '') {
+ return true;
+ }
+
+ if (array_key_exists('length', $rule) && mb_strlen($data) > $rule['length']) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('value is too long'));
+ return false;
+ }
+
+ $parser = new CRangesParser(['with_minus' => true, 'with_float' => true, 'with_suffix' => true]);
+
+ if ($parser->parse($data) != CParser::PARSE_SUCCESS) {
+ $error = _s('Invalid parameter "%1$s": %2$s.', $path, _('invalid range expression'));
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
* UUIDv4 validator.
*
* @param array $rule
diff --git a/ui/include/classes/validators/CExpressionValidator.php b/ui/include/classes/validators/CExpressionValidator.php
index c0adccdd33e..b8edd4b1ad2 100644
--- a/ui/include/classes/validators/CExpressionValidator.php
+++ b/ui/include/classes/validators/CExpressionValidator.php
@@ -29,11 +29,13 @@ class CExpressionValidator extends CValidator {
*
* Supported options:
* 'calculated' => false Validate expression as part of calculated item formula.
+ * 'partial' => false Validate partial expression (relaxed requirements).
*
* @var array
*/
private $options = [
- 'calculated' => false
+ 'calculated' => false,
+ 'partial' => false
];
/**
@@ -44,7 +46,7 @@ class CExpressionValidator extends CValidator {
private $math_function_data;
/**
- * Known math functions along with number of required parameters.
+ * Known math functions along with number or range of required parameters.
*
* @var array
*/
@@ -90,7 +92,7 @@ class CExpressionValidator extends CValidator {
}
if (!$this->options['calculated']) {
- if (!self::hasHistoryFunctions($tokens)) {
+ if (!$this->options['partial'] && !self::hasHistoryFunctions($tokens)) {
$this->setError(_('trigger expression must contain at least one /host/key reference'));
return false;
@@ -188,7 +190,7 @@ class CExpressionValidator extends CValidator {
}
/**
- * Check if history function tokens are contained within the hierarchy of given tokens.
+ * Check if there are history function tokens within the hierarchy of given tokens.
*
* @param array $tokens
*
diff --git a/ui/include/classes/validators/CHistFunctionValidator.php b/ui/include/classes/validators/CHistFunctionValidator.php
index a16d7d7e492..3fee138cdf3 100644
--- a/ui/include/classes/validators/CHistFunctionValidator.php
+++ b/ui/include/classes/validators/CHistFunctionValidator.php
@@ -303,7 +303,8 @@ class CHistFunctionValidator extends CValidator {
return false;
case CHistFunctionData::PERIOD_MODE_SEC:
- if ($time_shift !== '') {
+ case CHistFunctionData::PERIOD_MODE_SEC_ONLY:
+ if ($mode == CHistFunctionData::PERIOD_MODE_SEC_ONLY && $time_shift !== '') {
return false;
}
@@ -315,7 +316,7 @@ class CHistFunctionValidator extends CValidator {
return false;
- case CHistFunctionData::PERIOD_MODE_NUM:
+ case CHistFunctionData::PERIOD_MODE_NUM_ONLY:
if (preg_match('/^#(?<num>\d+)$/', $sec_num, $matches) == 1) {
return ($matches['num'] > 0 && $matches['num'] <= ZBX_MAX_INT32);
}
diff --git a/ui/include/classes/validators/CMathFunctionValidator.php b/ui/include/classes/validators/CMathFunctionValidator.php
index d0187358914..e11383dbfb5 100644
--- a/ui/include/classes/validators/CMathFunctionValidator.php
+++ b/ui/include/classes/validators/CMathFunctionValidator.php
@@ -60,13 +60,18 @@ class CMathFunctionValidator extends CValidator {
$num_required_parameters = $this->options['parameters'][$token['data']['function']];
$num_parameters = count($token['data']['parameters']);
- if (($num_required_parameters == -1 && $num_parameters == 0)
- || ($num_required_parameters != -1 && $num_parameters != $num_required_parameters)) {
- $this->setError(_s('invalid number of parameters in function "%1$s"', $token['data']['function']));
+ if (is_array($num_required_parameters)) {
+ $is_valid = ($num_required_parameters[0] === null || $num_parameters >= $num_required_parameters[0])
+ && ($num_required_parameters[1] === null || $num_parameters <= $num_required_parameters[1]);
+ }
+ else {
+ $is_valid = $num_parameters == $num_required_parameters;
+ }
- return false;
+ if (!$is_valid) {
+ $this->setError(_s('invalid number of parameters in function "%1$s"', $token['data']['function']));
}
- return true;
+ return $is_valid;
}
}
diff --git a/ui/include/classes/xml/CXmlConstantName.php b/ui/include/classes/xml/CXmlConstantName.php
index fba437fb042..cddd4402453 100644
--- a/ui/include/classes/xml/CXmlConstantName.php
+++ b/ui/include/classes/xml/CXmlConstantName.php
@@ -346,4 +346,12 @@ class CXmlConstantName {
const DASHBOARD_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE = 'ITEM_PROTOTYPE';
const DASHBOARD_WIDGET_FIELD_TYPE_GRAPH = 'GRAPH';
const DASHBOARD_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE = 'GRAPH_PROTOTYPE';
+
+ // Constants for value map mapping type.
+ const MAPPING_EQUAL = 'EQUAL';
+ const MAPPING_GREATER_EQUAL = 'GREATER_OR_EQUAL';
+ const MAPPING_LESS_EQUAL = 'LESS_OR_EQUAL';
+ const MAPPING_IN_RANGE = 'IN_RANGE';
+ const MAPPING_REGEXP = 'REGEXP';
+ const MAPPING_DEFAULT = 'DEFAULT';
}
diff --git a/ui/include/classes/xml/CXmlConstantValue.php b/ui/include/classes/xml/CXmlConstantValue.php
index cb4485121ab..dce87e34d2c 100644
--- a/ui/include/classes/xml/CXmlConstantValue.php
+++ b/ui/include/classes/xml/CXmlConstantValue.php
@@ -366,4 +366,12 @@ class CXmlConstantValue {
const DASHBOARD_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE = ZBX_WIDGET_FIELD_TYPE_ITEM_PROTOTYPE;
const DASHBOARD_WIDGET_FIELD_TYPE_GRAPH = ZBX_WIDGET_FIELD_TYPE_GRAPH;
const DASHBOARD_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE = ZBX_WIDGET_FIELD_TYPE_GRAPH_PROTOTYPE;
+
+ // Valuemap mapping type.
+ const MAPPING_EQUAL = VALUEMAP_MAPPING_TYPE_EQUAL;
+ const MAPPING_GREATER_EQUAL = VALUEMAP_MAPPING_TYPE_GREATER_EQUAL;
+ const MAPPING_LESS_EQUAL = VALUEMAP_MAPPING_TYPE_LESS_EQUAL;
+ const MAPPING_IN_RANGE = VALUEMAP_MAPPING_TYPE_IN_RANGE;
+ const MAPPING_REGEXP = VALUEMAP_MAPPING_TYPE_REGEXP;
+ const MAPPING_DEFAULT = VALUEMAP_MAPPING_TYPE_DEFAULT;
}
diff --git a/ui/include/defines.inc.php b/ui/include/defines.inc.php
index 58cc6a34385..41b5570bd63 100644
--- a/ui/include/defines.inc.php
+++ b/ui/include/defines.inc.php
@@ -22,7 +22,7 @@ define('ZABBIX_VERSION', '5.4.0rc1');
define('ZABBIX_API_VERSION', '5.4.0');
define('ZABBIX_EXPORT_VERSION', '5.4');
-define('ZABBIX_DB_VERSION', 5030187);
+define('ZABBIX_DB_VERSION', 5030201);
define('ZABBIX_COPYRIGHT_FROM', '2001');
define('ZABBIX_COPYRIGHT_TO', '2021');
@@ -1174,6 +1174,15 @@ define('EXPRESSION_NOT_A_MACRO_ERROR', '#ERROR_MACRO#');
define('EXPRESSION_FUNCTION_UNKNOWN', '#ERROR_FUNCTION#');
define('EXPRESSION_UNSUPPORTED_VALUE_TYPE', '#ERROR_VALUE_TYPE#');
+define('ZBX_FUNCTION_TYPE_AGGREGATE', 0);
+define('ZBX_FUNCTION_TYPE_BITWISE', 1);
+define('ZBX_FUNCTION_TYPE_DATE_TIME', 2);
+define('ZBX_FUNCTION_TYPE_HISTORY', 3);
+define('ZBX_FUNCTION_TYPE_MATH', 4);
+define('ZBX_FUNCTION_TYPE_OPERATOR', 5);
+define('ZBX_FUNCTION_TYPE_PREDICTION', 6);
+define('ZBX_FUNCTION_TYPE_STRING', 7);
+
/**
* @deprecated use either a literal space " " or a non-breakable space "&nbsp;" instead
*/
@@ -1289,6 +1298,14 @@ define('IPMI_PRIVILEGE_OEM', 5);
define('ZBX_HAVE_IPV6', true);
define('ZBX_DISCOVERER_IPRANGE_LIMIT', 65536);
+// Value map mappings type
+define('VALUEMAP_MAPPING_TYPE_EQUAL', 0);
+define('VALUEMAP_MAPPING_TYPE_GREATER_EQUAL', 1);
+define('VALUEMAP_MAPPING_TYPE_LESS_EQUAL', 2);
+define('VALUEMAP_MAPPING_TYPE_IN_RANGE', 3);
+define('VALUEMAP_MAPPING_TYPE_REGEXP', 4);
+define('VALUEMAP_MAPPING_TYPE_DEFAULT', 5);
+
define('ZBX_SOCKET_BYTES_LIMIT', ZBX_MEBIBYTE * 16); // socket response size limit
// value is also used in servercheck.js file
@@ -1358,7 +1375,8 @@ define('API_EVENT_NAME', 37);
define('API_JSONRPC_PARAMS', 38);
define('API_JSONRPC_ID', 39);
define('API_DATE', 40);
-define('API_UUID', 41);
+define('API_NUMERIC_RANGES', 41);
+define('API_UUID', 42);
// flags
define('API_REQUIRED', 0x0001);
@@ -1861,6 +1879,7 @@ define('ZBX_STYLE_ROW', 'row');
define('ZBX_STYLE_INLINE_SR_ONLY', 'inline-sr-only');
define('ZBX_STYLE_VALUEMAP_LIST_TABLE', 'valuemap-list-table');
define('ZBX_STYLE_VALUEMAP_CHECKBOX', 'valuemap-checkbox');
+define('ZBX_STYLE_VALUEMAP_MAPPINGS_TABLE', 'mappings-table');
define('ZBX_STYLE_SEARCH', 'search');
define('ZBX_STYLE_FORM_SEARCH', 'form-search');
define('ZBX_STYLE_SECOND_COLUMN_LABEL', 'second-column-label');
diff --git a/ui/include/items.inc.php b/ui/include/items.inc.php
index f7f362e0f40..3b5677c4349 100644
--- a/ui/include/items.inc.php
+++ b/ui/include/items.inc.php
@@ -1419,7 +1419,7 @@ function formatHistoryValue($value, array $item, $trim = true) {
// apply value mapping
switch ($item['value_type']) {
case ITEM_VALUE_TYPE_STR:
- $mapping = CValueMapHelper::getMappedValue($value, $item['valuemap']);
+ $mapping = CValueMapHelper::getMappedValue($item['value_type'], $value, $item['valuemap']);
// break; is not missing here
case ITEM_VALUE_TYPE_TEXT:
@@ -1435,7 +1435,7 @@ function formatHistoryValue($value, array $item, $trim = true) {
break;
default:
- $value = CValueMapHelper::applyValueMap($value, $item['valuemap']);
+ $value = CValueMapHelper::applyValueMap($item['value_type'], $value, $item['valuemap']);
}
return $value;
diff --git a/ui/include/schema.inc.php b/ui/include/schema.inc.php
index 9a834cb2fbe..7e475e91399 100644
--- a/ui/include/schema.inc.php
+++ b/ui/include/schema.inc.php
@@ -3371,6 +3371,18 @@ return [
'type' => DB::FIELD_TYPE_CHAR,
'length' => 64,
'default' => ''
+ ],
+ 'type' => [
+ 'null' => false,
+ 'type' => DB::FIELD_TYPE_INT,
+ 'length' => 10,
+ 'default' => '0'
+ ],
+ 'sortorder' => [
+ 'null' => false,
+ 'type' => DB::FIELD_TYPE_INT,
+ 'length' => 10,
+ 'default' => '0'
]
]
],
@@ -8056,8 +8068,13 @@ return [
]
],
'trigger_queue' => [
- 'key' => '',
+ 'key' => 'trigger_queueid',
'fields' => [
+ 'trigger_queueid' => [
+ 'null' => false,
+ 'type' => DB::FIELD_TYPE_ID,
+ 'length' => 20
+ ],
'objectid' => [
'null' => false,
'type' => DB::FIELD_TYPE_ID,
diff --git a/ui/include/triggers.inc.php b/ui/include/triggers.inc.php
index e2126606294..6e43ca2391a 100644
--- a/ui/include/triggers.inc.php
+++ b/ui/include/triggers.inc.php
@@ -1820,19 +1820,28 @@ function get_item_function_info(string $expr) {
$hist_functions = [
'avg' => $rules['numeric_as_float'],
'count' => $rules['numeric_as_uint'] + $rules['string_as_uint'],
+ 'countunique' => $rules['numeric_as_uint'] + $rules['string_as_uint'],
'change' => $rules['numeric'] + $rules['string_as_0or1'],
'find' => $rules['numeric_as_0or1'] + $rules['string_as_0or1'],
+ 'first' => $rules['numeric'] + $rules['string'],
'forecast' => $rules['numeric_as_float'],
'fuzzytime' => $rules['numeric_as_0or1'],
+ 'kurtosis' => $rules['numeric_as_float'],
'last' => $rules['numeric'] + $rules['string'],
- 'length' => $rules['numeric'] + $rules['string'],
'logeventid' => $rules['log_as_0or1'],
'logseverity' => $rules['log_as_uint'],
'logsource' => $rules['log_as_0or1'],
+ 'mad' => $rules['numeric_as_float'],
'max' => $rules['numeric'],
'min' => $rules['numeric'],
'nodata' => $rules['numeric_as_0or1'] + $rules['string_as_0or1'],
'percentile' => $rules['numeric'],
+ 'skewness' => $rules['numeric_as_float'],
+ 'stddevpop' => $rules['numeric_as_float'],
+ 'stddevsamp' => $rules['numeric_as_float'],
+ 'sumofsquares' => $rules['numeric_as_float'],
+ 'varpop' => $rules['numeric_as_float'],
+ 'varsamp' => $rules['numeric_as_float'],
'sum' => $rules['numeric'],
'timeleft' => $rules['numeric_as_float'],
'trendavg' => $rules['numeric'],
@@ -1844,8 +1853,28 @@ function get_item_function_info(string $expr) {
$math_functions = [
'abs' => ['any' => $rule_float],
+ 'acos' => ['any' => $rule_float],
+ 'ascii' => ['any' => $rule_int],
+ 'asin' => ['any' => $rule_float],
+ 'atan' => ['any' => $rule_float],
+ 'atan2' => ['any' => $rule_float],
'avg' => ['any' => $rule_float],
+ 'between' => ['any' => $rule_0or1],
'bitand' => ['any' => $rule_int],
+ 'bitlength' => ['any' => $rule_int],
+ 'bitlshift' => ['any' => $rule_int],
+ 'bitnot' => ['any' => $rule_int],
+ 'bitor' => ['any' => $rule_int],
+ 'bitrshift' => ['any' => $rule_int],
+ 'bitxor' => ['any' => $rule_int],
+ 'bytelength' => ['any' => $rule_int],
+ 'cbrt' => ['any' => $rule_float],
+ 'ceil' => ['any' => $rule_int],
+ 'char' => ['any' => $rule_str],
+ 'concat' => ['any' => $rule_str],
+ 'cos' => ['any' => $rule_float],
+ 'cosh' => ['any' => $rule_float],
+ 'cot' => ['any' => $rule_float],
'date' => [
'any' => ['value_type' => 'YYYYMMDD', 'values' => null]
],
@@ -1855,14 +1884,43 @@ function get_item_function_info(string $expr) {
'dayofweek' => [
'any' => ['value_type' => '1-7', 'values' => [1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7]]
],
+ 'degrees' => ['any' => $rule_float],
+ 'e' => ['any' => $rule_float],
+ 'exp' => ['any' => $rule_float],
+ 'expm1' => ['any' => $rule_float],
+ 'floor' => ['any' => $rule_int],
+ 'in' => ['any' => $rule_0or1],
+ 'insert' => ['any' => $rule_str],
+ 'left' => ['any' => $rule_str],
'length' => ['any' => $rule_int],
+ 'log' => ['any' => $rule_float],
+ 'log10' => ['any' => $rule_float],
+ 'ltrim' => ['any' => $rule_str],
'max' => ['any' => $rule_float],
+ 'mid' => ['any' => $rule_str],
'min' => ['any' => $rule_float],
+ 'mod' => ['any' => $rule_float],
'now' => ['any' => $rule_int],
+ 'pi' => ['any' => $rule_float],
+ 'power' => ['any' => $rule_float],
+ 'radians' => ['any' => $rule_float],
+ 'rand' => ['any' => $rule_int],
+ 'repeat' => ['any' => $rule_str],
+ 'replace' => ['any' => $rule_str],
+ 'right' => ['any' => $rule_str],
+ 'round' => ['any' => $rule_float],
+ 'rtrim' => ['any' => $rule_str],
+ 'sin' => ['any' => $rule_float],
+ 'sinh' => ['any' => $rule_float],
+ 'signum' => ['any' => $rule_int],
+ 'sqrt' => ['any' => $rule_float],
'sum' => ['any' => $rule_float],
+ 'tan' => ['any' => $rule_float],
'time' => [
'any' => ['value_type' => 'HHMMSS', 'values' => null]
- ]
+ ],
+ 'trim' => ['any' => $rule_str],
+ 'truncate' => ['any' => $rule_float]
];
$expression_parser = new CExpressionParser(['lldmacros' => true]);
@@ -2540,3 +2598,12 @@ function makeTriggerDependencies(array $dependencies, $freeze_on_click = true) {
function getStandaloneFunctions(): array {
return ['date', 'dayofmonth', 'dayofweek', 'time', 'now'];
}
+
+/**
+ * Returns a list of functions that return a constant or random number.
+ *
+ * @return array
+ */
+function getFunctionsConstants(): array {
+ return ['e', 'pi', 'rand'];
+}
diff --git a/ui/templates.php b/ui/templates.php
index c9ba79196af..0d0f6089741 100644
--- a/ui/templates.php
+++ b/ui/templates.php
@@ -516,13 +516,6 @@ if (hasRequest('form')) {
$data['tags'] = $data['dbTemplate']['tags'];
$data['macros'] = $data['dbTemplate']['macros'];
order_result($data['dbTemplate']['valuemaps'], 'name');
-
- foreach ($data['dbTemplate']['valuemaps'] as &$valuemap) {
- order_result($valuemap['mappings'], 'value');
- $valuemap['mappings'] = array_values($valuemap['mappings']);
- }
- unset($valuemap);
-
$data['valuemaps'] = array_values($data['dbTemplate']['valuemaps']);
}
}
diff --git a/ui/tests/api_json/testValuemap.php b/ui/tests/api_json/testValuemap.php
index 69c1a82555e..d8cf74bcd8a 100644
--- a/ui/tests/api_json/testValuemap.php
+++ b/ui/tests/api_json/testValuemap.php
@@ -252,11 +252,206 @@ class testValuemap extends CAPITest {
],
'expected_error' => 'Invalid parameter "/1/mappings/2": value (value)=(0) already exists.'
],
+ [
+ 'valuemap' => [
+ [
+ 'hostid' => '50009',
+ 'name' => 'API value is required for type equal',
+ 'mappings' => [
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_EQUAL,
+ 'newvalue' => 'fail'
+ ]
+ ]
+ ]
+ ],
+ 'expected_error' => 'Invalid parameter "/1/mappings/1": the parameter "value" is missing.'
+ ],
+ [
+ 'valuemap' => [
+ [
+ 'hostid' => '50009',
+ 'name' => 'API value is required for type greater or equal',
+ 'mappings' => [
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_GREATER_EQUAL,
+ 'newvalue' => 'fail'
+ ]
+ ]
+ ]
+ ],
+ 'expected_error' => 'Invalid parameter "/1/mappings/1": the parameter "value" is missing.'
+ ],
+ [
+ 'valuemap' => [
+ [
+ 'hostid' => '50009',
+ 'name' => 'API value is required for type less or equal',
+ 'mappings' => [
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_LESS_EQUAL,
+ 'newvalue' => 'fail'
+ ]
+ ]
+ ]
+ ],
+ 'expected_error' => 'Invalid parameter "/1/mappings/1": the parameter "value" is missing.'
+ ],
+ [
+ 'valuemap' => [
+ [
+ 'hostid' => '50009',
+ 'name' => 'API value is required for type range',
+ 'mappings' => [
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_IN_RANGE,
+ 'newvalue' => 'fail'
+ ]
+ ]
+ ]
+ ],
+ 'expected_error' => 'Invalid parameter "/1/mappings/1": the parameter "value" is missing.'
+ ],
+ [
+ 'valuemap' => [
+ [
+ 'hostid' => '50009',
+ 'name' => 'API value is required for type greater or equal',
+ 'mappings' => [
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_REGEXP,
+ 'newvalue' => 'fail'
+ ]
+ ]
+ ]
+ ],
+ 'expected_error' => 'Invalid parameter "/1/mappings/1": the parameter "value" is missing.'
+ ],
+ [
+ 'valuemap' => [
+ [
+ 'hostid' => '50009',
+ 'name' => 'API value should be empty for type default',
+ 'mappings' => [
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_DEFAULT,
+ 'value' => 'fail',
+ 'newvalue' => 'fail'
+ ]
+ ]
+ ]
+ ],
+ 'expected_error' => 'Invalid parameter "/1/mappings/1/value": should be empty.'
+ ],
+ [
+ 'valuemap' => [
+ [
+ 'hostid' => '50009',
+ 'name' => 'type and value combination should be unique',
+ 'mappings' => [
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_EQUAL,
+ 'value' => '10',
+ 'newvalue' => '1 fail'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_EQUAL,
+ 'value' => '10',
+ 'newvalue' => '2 fail'
+ ]
+ ]
+ ]
+ ],
+ 'expected_error' => 'Invalid parameter "/1/mappings/2": value (value)=(10) already exists.'
+ ],
+ [
+ 'valuemap' => [
+ [
+ 'hostid' => '50009',
+ 'name' => 'API only one type default is allowed',
+ 'mappings' => [
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_DEFAULT,
+ 'newvalue' => '1 fail'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_DEFAULT,
+ 'value' => '',
+ 'newvalue' => '2 fail'
+ ]
+ ]
+ ]
+ ],
+ 'expected_error' => 'Invalid parameter "/1/mappings/2": value (type)=(5) already exists.'
+ ],
+ [
+ 'valuemap' => [
+ [
+ 'hostid' => '50009',
+ 'name' => 'API float value with zero suffix check for geq mapping',
+ 'mappings' => [
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_GREATER_EQUAL,
+ 'value' => '7',
+ 'newvalue' => '1 fail'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_GREATER_EQUAL,
+ 'value' => '7.00',
+ 'newvalue' => '2 fail'
+ ]
+ ]
+ ]
+ ],
+ 'expected_error' => 'Invalid parameter "/1/mappings/2": value (value)=(7) already exists.'
+ ],
// Successfully create.
[
'valuemap' => [
[
'hostid' => '50009',
+ 'name' => 'API equal value uniqueness check as string for eq mapping',
+ 'mappings' => [
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_EQUAL,
+ 'value' => '5',
+ 'newvalue' => '5'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_EQUAL,
+ 'value' => '5.00',
+ 'newvalue' => '5.00'
+ ]
+ ]
+ ]
+ ],
+ 'expected_error' => null
+ ],
+ [
+ 'valuemap' => [
+ [
+ 'hostid' => '50009',
+ 'name' => 'API float value uniqueness validation for geq mapping',
+ 'mappings' => [
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_GREATER_EQUAL,
+ 'value' => '5',
+ 'newvalue' => '5'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_GREATER_EQUAL,
+ 'value' => '5.001',
+ 'newvalue' => '5.001'
+ ]
+ ]
+ ]
+ ],
+ 'expected_error' => null
+ ],
+ [
+ 'valuemap' => [
+ [
+ 'hostid' => '50009',
'name' => 'API value map created',
'mappings' => [
[
@@ -326,6 +521,72 @@ class testValuemap extends CAPITest {
]
],
'expected_error' => null
+ ],
+ [
+ 'valuemap' => [
+ [
+ 'hostid' => '50009',
+ 'name' => 'API create value map with mapping types',
+ 'mappings' => [
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_DEFAULT,
+ 'newvalue' => 'default'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_REGEXP,
+ 'value' => '/[0-9]{3,}/',
+ 'newvalue' => 'regexp'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_IN_RANGE,
+ 'value' => '10-20,-20--10,20,0.5-0.7,-0.7--0.5',
+ 'newvalue' => 'range'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_LESS_EQUAL,
+ 'value' => '10',
+ 'newvalue' => '<=10'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_GREATER_EQUAL,
+ 'value' => '10',
+ 'newvalue' => '>=10'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_EQUAL,
+ 'value' => '10',
+ 'newvalue' => '10'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_EQUAL,
+ 'value' => 'A',
+ 'newvalue' => 'A'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_EQUAL,
+ 'value' => '0.5',
+ 'newvalue' => '0.5'
+ ]
+ ]
+ ]
+ ],
+ 'expected_error' => null
+ ],
+ [
+ 'valuemap' => [
+ [
+ 'hostid' => '50009',
+ 'name' => 'API mapping type default value can be empty when defined',
+ 'mappings' => [
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_DEFAULT,
+ 'value' => '',
+ 'newvalue' => 'default'
+ ]
+ ]
+ ]
+ ],
+ 'expected_error' => null
]
];
}
@@ -343,6 +604,7 @@ class testValuemap extends CAPITest {
$this->assertEquals($dbRow['name'], $valuemap[$key]['name']);
foreach ($valuemap[$key]['mappings'] as $values) {
+ $values += ['value' => ''];
$this->assertEquals(1, CDBHelper::getCount('select * from valuemap_mapping where valuemapid='.zbx_dbstr($id).
' and value='.zbx_dbstr($values['value']).' and newvalue='.zbx_dbstr($values['newvalue']))
);
@@ -611,6 +873,56 @@ class testValuemap extends CAPITest {
]
],
'expected_error' => null
+ ],
+ [
+ 'valuemap' => [
+ [
+ 'valuemapid' => '18',
+ 'name' => 'API update valuemap one',
+ 'mappings' => [
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_DEFAULT,
+ 'newvalue' => 'default'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_REGEXP,
+ 'value' => '/[0-9]{3,}/',
+ 'newvalue' => 'regexp'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_IN_RANGE,
+ 'value' => '10-20,-20--10,20,0.5-0.7,-0.7--0.5',
+ 'newvalue' => 'range'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_LESS_EQUAL,
+ 'value' => '10.5',
+ 'newvalue' => '<=10.5'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_GREATER_EQUAL,
+ 'value' => '20.5',
+ 'newvalue' => '>=20.5'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_EQUAL,
+ 'value' => 'A',
+ 'newvalue' => 'A'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_EQUAL,
+ 'value' => '10',
+ 'newvalue' => '10'
+ ],
+ [
+ 'type' => VALUEMAP_MAPPING_TYPE_EQUAL,
+ 'value' => '0.5',
+ 'newvalue' => '0.5'
+ ]
+ ]
+ ]
+ ],
+ 'expected_error' => null
]
];
}
@@ -628,11 +940,16 @@ class testValuemap extends CAPITest {
$this->assertEquals($dbRow['name'], $valuemaps[$key]['name']);
if (array_key_exists('mappings', $valuemaps[$key])){
- foreach ($valuemaps[$key]['mappings'] as $values) {
- $this->assertEquals(1, CDBHelper::getCount('select * from valuemap_mapping where valuemapid='.zbx_dbstr($id).
- ' and value='.zbx_dbstr($values['value']).' and newvalue='.
- zbx_dbstr($values['newvalue']))
- );
+ $db_mappings = CDBHelper::getAll(
+ 'SELECT type,value,newvalue'.
+ ' FROM valuemap_mapping'.
+ ' WHERE valuemapid='.zbx_dbstr($id).
+ ' ORDER BY sortorder ASC'
+ );
+
+ foreach ($valuemaps[$key]['mappings'] as $i => $mapping) {
+ $mapping = $mapping + ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => ''];
+ $this->assertEquals($mapping, $db_mappings[$i]);
}
}
}
diff --git a/ui/tests/integration/IntegrationTests.php b/ui/tests/integration/IntegrationTests.php
index 42a9d3c1e43..62731bf3911 100644
--- a/ui/tests/integration/IntegrationTests.php
+++ b/ui/tests/integration/IntegrationTests.php
@@ -23,6 +23,7 @@ require_once dirname(__FILE__).'/testDiagnosticDataTask.php';
require_once dirname(__FILE__).'/testLowLevelDiscovery.php';
require_once dirname(__FILE__).'/testGoAgentDataCollection.php';
require_once dirname(__FILE__).'/testItemState.php';
+require_once dirname(__FILE__).'/testValuemaps.php';
class IntegrationTests {
public static function suite() {
@@ -32,6 +33,7 @@ class IntegrationTests {
$suite->addTestSuite('testLowLevelDiscovery');
$suite->addTestSuite('testGoAgentDataCollection');
$suite->addTestSuite('testItemState');
+ $suite->addTestSuite('testValuemaps');
return $suite;
}
diff --git a/ui/tests/integration/testValuemaps.php b/ui/tests/integration/testValuemaps.php
new file mode 100644
index 00000000000..30003911070
--- /dev/null
+++ b/ui/tests/integration/testValuemaps.php
@@ -0,0 +1,304 @@
+<?php
+/*
+** Zabbix
+** Copyright (C) 2001-2021 Zabbix SIA
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+**/
+
+require_once dirname(__FILE__).'/../include/CIntegrationTest.php';
+require_once dirname(__FILE__).'/../include/helpers/CDataHelper.php';
+
+/**
+ * Test suite for value mapping.
+ *
+ * @required-components server
+ * @hosts test_valuemaps
+ * @backup history
+ */
+class testValuemaps extends CIntegrationTest {
+ const VALUEMAP_NAME = 'valuemap';
+ const HOST_NAME = 'test_valuemaps';
+ const ITEM_NAME = 'trap';
+
+ private static $hostid;
+ private static $itemid;
+
+ /**
+ * @inheritdoc
+ */
+ public function prepareData() {
+ // Create host HOST_NAME.
+ $response = $this->call('host.create', [
+ 'host' => self::HOST_NAME,
+ 'interfaces' => [
+ [
+ 'type' => 1,
+ 'main' => 1,
+ 'useip' => 1,
+ 'ip' => '127.0.0.1',
+ 'dns' => '',
+ 'port' => $this->getConfigurationValue(self::COMPONENT_AGENT, 'ListenPort')
+ ]
+ ],
+ 'groups' => [
+ [
+ 'groupid' => 4
+ ]
+ ]
+ ]);
+
+ $this->assertArrayHasKey('hostids', $response['result']);
+ $this->assertArrayHasKey(0, $response['result']['hostids']);
+ self::$hostid = $response['result']['hostids'][0];
+ // create trapper
+ $response = $this->call('item.create', [
+ 'hostid' => self::$hostid,
+ 'name' => self::ITEM_NAME,
+ 'key_' => self::ITEM_NAME,
+ 'type' => ITEM_TYPE_TRAPPER,
+ 'value_type' => ITEM_VALUE_TYPE_FLOAT
+ ]);
+ $this->assertArrayHasKey('itemids', $response['result']);
+ $this->assertEquals(1, count($response['result']['itemids']));
+ self::$itemid = $response['result']['itemids'];
+
+ return true;
+ }
+
+ /**
+ * Data provider (valuemaps).
+ *
+ * @return array
+ */
+ public function getValuemaps() {
+ $valuemap_patterns = [
+ 'exactMatch' => [
+ 'name' => self::VALUEMAP_NAME,
+ 'hostid' => null,
+ 'mappings' => [
+ [
+ 'value' => '0',
+ 'newvalue' => 'Value 0'
+ ],
+ [
+ 'value' => '1',
+ 'newvalue' => 'Value 1'
+ ]
+ ]
+ ],
+ 'rangeWithoutDefault' => [
+ 'name' => self::VALUEMAP_NAME,
+ 'hostid' => null,
+ 'mappings' => [
+ [
+ 'value' => '0',
+ 'newvalue' => 'Value 0'
+ ],
+ [
+ 'value' => '^1$',
+ 'newvalue' => 'Regexp 1',
+ 'type' => VALUEMAP_MAPPING_TYPE_REGEXP
+ ],
+ [
+ 'value' => '3',
+ 'newvalue' => 'Value <= 3',
+ 'type' => VALUEMAP_MAPPING_TYPE_LESS_EQUAL
+ ],
+ [
+ 'value' => '10',
+ 'newvalue' => 'Value >= 10',
+ 'type' => VALUEMAP_MAPPING_TYPE_GREATER_EQUAL
+ ],
+ [
+ 'value' => '5-7,8',
+ 'newvalue' => 'Range 5-7,8',
+ 'type' => VALUEMAP_MAPPING_TYPE_IN_RANGE
+ ]
+ ]
+ ],
+ 'rangeWithDefault' => [
+ 'name' => self::VALUEMAP_NAME,
+ 'hostid' => null,
+ 'mappings' => [
+ [
+ 'value' => '-1.2e-1K--3e1, -10, -7--5, -1-1, 5-7.8K',
+ 'newvalue' => 'Range',
+ 'type' => VALUEMAP_MAPPING_TYPE_IN_RANGE
+ ],
+ [
+ 'newvalue' => 'Default',
+ 'type' => VALUEMAP_MAPPING_TYPE_DEFAULT
+ ]
+ ]
+ ]
+ ];
+
+ return [
+ [
+ 'inputData' => '1',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['exactMatch'],
+ 'outputData' => 'Value 1 (1)'
+ ],
+ [
+ 'inputData' => '0',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['exactMatch'],
+ 'outputData' => 'Value 0 (0)'
+ ],
+ [
+ 'inputData' => '2',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['exactMatch'],
+ 'outputData' => '2'
+ ],
+ [
+ 'inputData' => '0',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['rangeWithoutDefault'],
+ 'outputData' => 'Value 0 (0)'
+ ],
+ [
+ 'inputData' => '1',
+ 'inputType'=> ITEM_VALUE_TYPE_STR,
+ 'valuemap' => $valuemap_patterns['rangeWithoutDefault'],
+ 'outputData' => 'Regexp 1 (1)'
+ ],
+ [
+ 'inputData' => '3',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['rangeWithoutDefault'],
+ 'outputData' => 'Value <= 3 (3)'
+ ],
+ [
+ 'inputData' => '5',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['rangeWithoutDefault'],
+ 'outputData' => 'Range 5-7,8 (5)'
+ ],
+ [
+ 'inputData' => '8',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['rangeWithoutDefault'],
+ 'outputData' => 'Range 5-7,8 (8)'
+ ],
+ [
+ 'inputData' => '9',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['rangeWithoutDefault'],
+ 'outputData' => '9'
+ ],
+ [
+ 'inputData' => '10',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['rangeWithoutDefault'],
+ 'outputData' => 'Value >= 10 (10)'
+ ],
+ [
+ 'inputData' => '-123',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['rangeWithDefault'],
+ 'outputData' => 'Default (-123)'
+ ],
+ [
+ 'inputData' => '-122.88',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['rangeWithDefault'],
+ 'outputData' => 'Range (-122.88)'
+ ],
+ [
+ 'inputData' => '-30',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['rangeWithDefault'],
+ 'outputData' => 'Range (-30)'
+ ],
+ [
+ 'inputData' => '0',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['rangeWithDefault'],
+ 'outputData' => 'Range (0)'
+ ],
+ [
+ 'inputData' => '7987.2',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['rangeWithDefault'],
+ 'outputData' => 'Range (7987.2)'
+ ],
+ [
+ 'inputData' => '7988',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['rangeWithDefault'],
+ 'outputData' => 'Default (7988)'
+ ],
+ [
+ 'inputData' => '4',
+ 'inputType'=> ITEM_VALUE_TYPE_FLOAT,
+ 'valuemap' => $valuemap_patterns['rangeWithDefault'],
+ 'outputData' => 'Default (4)'
+ ]
+ ];
+ }
+
+ /**
+ * Test valuemaps cases.
+ *
+ * @dataProvider getValuemaps
+ */
+ public function testValuemaps_checkProblemName($inputData, $inputType, $valuemap, $outputData) {
+ $valuemap['hostid'] = self::$hostid;
+ $response = $this->call('valuemap.create', $valuemap);
+ $this->assertArrayHasKey('valuemapids', $response['result']);
+ $this->assertEquals(1, count($response['result']['valuemapids']));
+ $valuemapid = $response['result']['valuemapids'];
+
+ $response = $this->call('item.update', [
+ 'itemid' => self::$itemid[0],
+ 'valuemapid' => $valuemapid[0],
+ 'value_type' => $inputType
+ ]);
+ $this->assertArrayHasKey('itemids', $response['result']);
+ $this->assertEquals(1, count($response['result']['itemids']));
+ $this->assertEquals(self::$itemid, $response['result']['itemids']);
+
+ $response = $this->call('trigger.create', [
+ 'description' => ' {ITEM.VALUE}',
+ 'expression' => 'last(/'.self::HOST_NAME.'/'.self::ITEM_NAME.')='.$inputData
+ ]);
+ $this->assertArrayHasKey('triggerids', $response['result']);
+ $this->assertEquals(1, count($response['result']['triggerids']));
+ $triggerid = $response['result']['triggerids'];
+
+ $this->reloadConfigurationCache();
+
+ $this->sendSenderValue(self::HOST_NAME, self::ITEM_NAME, $inputData);
+
+ ['result' => $result] = $this->call('problem.get', [
+ 'output' => ['name'],
+ 'objectids' => $triggerid
+ ]);
+
+ $result = array_column($result, 'name');
+ $this->assertEquals(' '.$outputData, $result[0]);
+
+ $response = $this->call('trigger.delete', $triggerid);
+ $this->assertArrayHasKey('triggerids', $response['result']);
+ $this->assertEquals($triggerid, $response['result']['triggerids']);
+
+ $response = $this->call('valuemap.delete', $valuemapid);
+ $this->assertArrayHasKey('valuemapids', $response['result']);
+ $this->assertEquals($valuemapid, $response['result']['valuemapids']);
+ }
+}
diff --git a/ui/tests/selenium/common/testFormValueMappings.php b/ui/tests/selenium/common/testFormValueMappings.php
index b2379df8a0a..3af7ab9e1c2 100644
--- a/ui/tests/selenium/common/testFormValueMappings.php
+++ b/ui/tests/selenium/common/testFormValueMappings.php
@@ -47,18 +47,18 @@ class testFormValueMappings extends CWebTest {
const EXISTING_VALUEMAPS = [
[
'Name' => 'Valuemap for delete',
- 'Value' => "four ⇒ 4\noneoneoneoneoneoneoneoneoneoneone ⇒ 11111111111\nthreethreethreethreethreethree".
- "threethreethreethree ⇒ 3333333333\n…",
+ 'Value' => "=oneoneoneoneoneoneoneoneoneoneone\n⇒\n11111111111\n=two\n⇒\n2\n=threethreethreethreethreethree".
+ "threethreethreethree\n⇒\n3333333333\n…",
'Action' => 'Remove'
],
[
'Name' => 'Valuemap for update 1',
- 'Value' => '⇒ reference newvalue',
+ 'Value' => "=\n⇒\nreference newvalue",
'Action' => 'Remove'
],
[
'Name' => 'Valuemap for update 2',
- 'Value' => "⇒ no data\n1 ⇒ one\n2 ⇒ two\n…",
+ 'Value' => "=\n⇒\nno data\n=1\n⇒\none\n=2\n⇒\ntwo\n…",
'Action' => 'Remove'
]
];
@@ -89,7 +89,7 @@ class testFormValueMappings extends CWebTest {
// Check mappings table layout.
$mappings_table = $mapping_form->query('id:mappings_table')->asTable()->one();
- $this->assertEquals(['Value', '', 'Mapped to', 'Action'], $mappings_table->getHeadersText());
+ $this->assertEquals(['', 'Type', 'Value', '', 'Mapped to', 'Action', ''], $mappings_table->getHeadersText());
$row = $mappings_table->getRow(0);
foreach (['Value', 'Mapped to'] as $mapping_column) {
$mapping_field = $row->getColumn($mapping_column)->query('xpath:.//input')->one();
@@ -387,11 +387,6 @@ class testFormValueMappings extends CWebTest {
}
unset($mapping);
- // Sort reference mappings array by field "Value".
- usort($mappings, function($a, $b) {
- return $a['value'] <=> $b['value'];
- });
-
$mappings_table->checkValue($mappings);
}
@@ -522,7 +517,7 @@ class testFormValueMappings extends CWebTest {
$reference_valuemaps = [
[
'Name' => $valuemap['name'],
- 'Value' => $valuemap['mappings']['value'].' ⇒ '.$valuemap['mappings']['newvalue'],
+ 'Value' => "=".$valuemap['mappings']['value']."\n⇒\n".$valuemap['mappings']['newvalue'],
'Action' => 'Remove'
]
];
diff --git a/ui/tests/selenium/testFormItem.php b/ui/tests/selenium/testFormItem.php
index 2c10e93ce7f..04768acb1ba 100644
--- a/ui/tests/selenium/testFormItem.php
+++ b/ui/tests/selenium/testFormItem.php
@@ -751,22 +751,22 @@ class testFormItem extends CLegacyWebTest {
$mappings = [];
$i = 0;
foreach ($db_mappings as $db_mapping) {
- $mappings_text = $value_mapping->getColumn('Mapping')->getText();
-
if ($db_mapping['name'] === $valuemap_name) {
- $mappings_text = $value_mapping->getColumn('Mapping')->getText();
- $i++;
- // Only the first three mappings are displayed in the form for each value mapping
- if ($i < 4) {
- $this->assertTrue(str_contains($mappings_text, $db_mapping['value'].' ⇒ '.$db_mapping['newvalue']));
+ if ($i < 3) {
+ $mappings[] = '='.$db_mapping['value'].' ⇒ '.$db_mapping['newvalue'];
+ $i++;
}
else {
- $this->assertTrue(str_contains($mappings_text, '…'));
+ $mappings[] = '…';
break;
}
}
}
+ // Transform newlines in value map table text.
+ $source = $value_mapping->getColumn('Mapping')->getText();
+ $text = rtrim(preg_replace("/(.*)\n⇒\n(.*)\n?/", "\\1 ⇒ \\2\n", $source), "\n");
+ $this->assertEquals(implode("\n", $mappings), $text);
}
}
else {
diff --git a/ui/tests/selenium/testFormItemPrototype.php b/ui/tests/selenium/testFormItemPrototype.php
index 77bb96c6f0c..48288de631d 100644
--- a/ui/tests/selenium/testFormItemPrototype.php
+++ b/ui/tests/selenium/testFormItemPrototype.php
@@ -951,22 +951,22 @@ class testFormItemPrototype extends CLegacyWebTest {
$mappings = [];
$i = 0;
foreach ($db_mappings as $db_mapping) {
- $mappings_text = $value_mapping->getColumn('Mapping')->getText();
-
if ($db_mapping['name'] === $valuemap_name) {
- $mappings_text = $value_mapping->getColumn('Mapping')->getText();
- $i++;
- // Only the first three mappings are displayed in the form for each value mapping
- if ($i < 4) {
- $this->assertTrue(str_contains($mappings_text, $db_mapping['value'].' ⇒ '.$db_mapping['newvalue']));
+ if ($i < 3) {
+ $mappings[] = '='.$db_mapping['value'].' ⇒ '.$db_mapping['newvalue'];
+ $i++;
}
else {
- $this->assertTrue(str_contains($mappings_text, '…'));
+ $mappings[] = '…';
break;
}
}
}
+ // Transform newlines in value map table text.
+ $source = $value_mapping->getColumn('Mapping')->getText();
+ $text = rtrim(preg_replace("/(.*)\n⇒\n(.*)\n?/", "\\1 ⇒ \\2\n", $source), "\n");
+ $this->assertEquals(implode("\n", $mappings), $text);
}
}
else {
diff --git a/ui/tests/unit/include/classes/helpers/CValueMapHelperTest.php b/ui/tests/unit/include/classes/helpers/CValueMapHelperTest.php
new file mode 100644
index 00000000000..ac0d782e56a
--- /dev/null
+++ b/ui/tests/unit/include/classes/helpers/CValueMapHelperTest.php
@@ -0,0 +1,261 @@
+<?php declare(strict_types=1);
+/*
+** Zabbix
+** Copyright (C) 2001-2021 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.
+**/
+
+
+use PHPUnit\Framework\TestCase;
+
+class CValueMapHelperTest extends TestCase {
+
+ /**
+ * Data for testing single rule match.
+ */
+ public function dataMatchMapping(): array {
+ return [
+ // VALUEMAP_MAPPING_TYPE_EQUAL
+ [
+ '.1', ITEM_VALUE_TYPE_FLOAT,
+ ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => '0.1', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '0.2', ITEM_VALUE_TYPE_FLOAT,
+ ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => '.2', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '2', ITEM_VALUE_TYPE_UINT64,
+ ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => '2', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '2', ITEM_VALUE_TYPE_STR,
+ ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => '2', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ 'match case sensitive', ITEM_VALUE_TYPE_STR,
+ ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => 'match case sensitive', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '1024', ITEM_VALUE_TYPE_UINT64,
+ ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => '1K', 'newvalue' => 'ok'],
+ false
+ ],
+ [
+ '.1', ITEM_VALUE_TYPE_STR,
+ ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => '0.1', 'newvalue' => 'ok'],
+ false
+ ],
+ [
+ '0.2', ITEM_VALUE_TYPE_STR,
+ ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => '.2', 'newvalue' => 'ok'],
+ false
+ ],
+ [
+ 'match Case Sensitive', ITEM_VALUE_TYPE_STR,
+ ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => 'match case sensitive', 'newvalue' => 'ok'],
+ false
+ ],
+ [
+ '11 ', ITEM_VALUE_TYPE_STR,
+ ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => '11', 'newvalue' => 'ok'],
+ false
+ ],
+ [
+ '2K', ITEM_VALUE_TYPE_STR,
+ ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => '2000', 'newvalue' => 'ok'],
+ false
+ ],
+ // VALUEMAP_MAPPING_TYPE_GREATER_EQUAL
+ [
+ '.1', ITEM_VALUE_TYPE_FLOAT,
+ ['type' => VALUEMAP_MAPPING_TYPE_GREATER_EQUAL, 'value' => '0.1', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '3', ITEM_VALUE_TYPE_UINT64,
+ ['type' => VALUEMAP_MAPPING_TYPE_GREATER_EQUAL, 'value' => '0', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '2048', ITEM_VALUE_TYPE_STR,
+ ['type' => VALUEMAP_MAPPING_TYPE_GREATER_EQUAL, 'value' => '1K', 'newvalue' => 'ok'],
+ false
+ ],
+ [
+ '3', ITEM_VALUE_TYPE_STR,
+ ['type' => VALUEMAP_MAPPING_TYPE_GREATER_EQUAL, 'value' => '0', 'newvalue' => 'ok'],
+ false
+ ],
+ // VALUEMAP_MAPPING_TYPE_LESS_EQUAL
+ [
+ '.1', ITEM_VALUE_TYPE_FLOAT,
+ ['type' => VALUEMAP_MAPPING_TYPE_LESS_EQUAL, 'value' => '10', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '4', ITEM_VALUE_TYPE_UINT64,
+ ['type' => VALUEMAP_MAPPING_TYPE_LESS_EQUAL, 'value' => '10', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '100', ITEM_VALUE_TYPE_UINT64,
+ ['type' => VALUEMAP_MAPPING_TYPE_LESS_EQUAL, 'value' => '1K', 'newvalue' => 'ok'],
+ false
+ ],
+ // VALUEMAP_MAPPING_TYPE_IN_RANGE
+ [
+ '-5.2', ITEM_VALUE_TYPE_FLOAT,
+ ['type' => VALUEMAP_MAPPING_TYPE_IN_RANGE, 'value' => '-5.5--5', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '5', ITEM_VALUE_TYPE_UINT64,
+ ['type' => VALUEMAP_MAPPING_TYPE_IN_RANGE, 'value' => '1-10', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '5', ITEM_VALUE_TYPE_UINT64,
+ ['type' => VALUEMAP_MAPPING_TYPE_IN_RANGE, 'value' => '4-5', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '5', ITEM_VALUE_TYPE_UINT64,
+ ['type' => VALUEMAP_MAPPING_TYPE_IN_RANGE, 'value' => '0.5-5.5', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '5', ITEM_VALUE_TYPE_UINT64,
+ ['type' => VALUEMAP_MAPPING_TYPE_IN_RANGE, 'value' => '10-30,5', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '2048', ITEM_VALUE_TYPE_UINT64,
+ ['type' => VALUEMAP_MAPPING_TYPE_IN_RANGE, 'value' => '2K-2.5K', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '1K', ITEM_VALUE_TYPE_STR,
+ ['type' => VALUEMAP_MAPPING_TYPE_IN_RANGE, 'value' => '1000-2000', 'newvalue' => 'ok'],
+ false
+ ],
+ // VALUEMAP_MAPPING_TYPE_REGEXP
+ [
+ '12', ITEM_VALUE_TYPE_STR,
+ ['type' => VALUEMAP_MAPPING_TYPE_REGEXP, 'value' => '\d{2}', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ 'test two words', ITEM_VALUE_TYPE_STR,
+ ['type' => VALUEMAP_MAPPING_TYPE_REGEXP, 'value' => 'test(\s\w+){2}', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ 'test/ slash escaped', ITEM_VALUE_TYPE_STR,
+ ['type' => VALUEMAP_MAPPING_TYPE_REGEXP, 'value' => 'test/(\s\w+){2}', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '12', ITEM_VALUE_TYPE_UINT64,
+ ['type' => VALUEMAP_MAPPING_TYPE_REGEXP, 'value' => '\d{2}', 'newvalue' => 'ok'],
+ false
+ ],
+ [
+ 'test no modifiers', ITEM_VALUE_TYPE_STR,
+ ['type' => VALUEMAP_MAPPING_TYPE_REGEXP, 'value' => '/test(\s\w+){2}/i', 'newvalue' => 'ok'],
+ false
+ ],
+ // VALUEMAP_MAPPING_TYPE_DEFAULT
+ [
+ '12', ITEM_VALUE_TYPE_UINT64,
+ ['type' => VALUEMAP_MAPPING_TYPE_DEFAULT, 'value' => '', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ '128K', ITEM_VALUE_TYPE_UINT64,
+ ['type' => VALUEMAP_MAPPING_TYPE_DEFAULT, 'value' => '', 'newvalue' => 'ok'],
+ true
+ ],
+ [
+ 'any should match', ITEM_VALUE_TYPE_STR,
+ ['type' => VALUEMAP_MAPPING_TYPE_DEFAULT, 'value' => '', 'newvalue' => 'ok'],
+ true
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider dataMatchMapping
+ *
+ * @param string $value
+ * @param int $value_type
+ * @param array $mapping
+ * @param bool $expected
+ */
+ public function testMatchMapping(string $value, int $value_type, array $mapping, bool $expected) {
+ $this->assertSame(CValueMapHelper::matchMapping($value_type, $value, $mapping), $expected);
+ }
+
+ /**
+ * Data for testing multiple mappings.
+ */
+ public function dataMatchMappingOrder(): array {
+ return [
+ ['1', [], false],
+ [
+ '1',
+ [
+ ['type' => VALUEMAP_MAPPING_TYPE_DEFAULT, 'newvalue' => 'newvalue-1'],
+ ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => '10', 'newvalue' => 'newvalue-2']
+ ],
+ 'newvalue-1'
+ ],
+ [
+ '10',
+ [
+ ['type' => VALUEMAP_MAPPING_TYPE_DEFAULT, 'newvalue' => 'newvalue-1'],
+ ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => '10', 'newvalue' => 'newvalue-2']
+ ],
+ 'newvalue-2'
+ ],
+ [
+ '10',
+ [
+ ['type' => VALUEMAP_MAPPING_TYPE_DEFAULT, 'newvalue' => 'newvalue-1'],
+ ['type' => VALUEMAP_MAPPING_TYPE_GREATER_EQUAL, 'value' => '10', 'newvalue' => 'newvalue-2'],
+ ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => '10', 'newvalue' => 'newvalue-3']
+ ],
+ 'newvalue-2'
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider dataMatchMappingOrder
+ *
+ * @param string $value
+ * @param array $mapping
+ * @param string|bool $expected
+ */
+ public function testMatchMappingOrder(string $value, array $mappings, $expected) {
+ $this->assertSame(CValueMapHelper::getMappedValue(ITEM_VALUE_TYPE_UINT64, $value, ['mappings' => $mappings]), $expected);
+ }
+}
diff --git a/ui/tests/unit/include/classes/parsers/CRangeParserTest.php b/ui/tests/unit/include/classes/parsers/CRangeParserTest.php
index 2959290dfaa..dfdce85448e 100644
--- a/ui/tests/unit/include/classes/parsers/CRangeParserTest.php
+++ b/ui/tests/unit/include/classes/parsers/CRangeParserTest.php
@@ -27,6 +27,10 @@ class CRangeParserTest extends TestCase {
* An array of time periods and parsed results.
*/
public static function dataProvider() {
+ $negative = ['with_minus' => true];
+ $float = ['with_float' => true];
+ $suffix= ['with_suffix' => true];
+
return [
// success
[
@@ -313,6 +317,102 @@ class CRangeParserTest extends TestCase {
'range' => ['{{#M}.regsub("^([0-9]+)", "{#M}: \1")}', '{$M}']
]
],
+ [
+ '-20--10', 0, $negative,
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '-20--10',
+ 'range' => ['-20', '-10']
+ ]
+ ],
+ [
+ ' -20 - -10 ', 0, $negative,
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => ' -20 - -10 ',
+ 'range' => ['-20', '-10']
+ ]
+ ],
+ [
+ '20.0-30.0000', 0, $float,
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '20.0-30.0000',
+ 'range' => ['20.0', '30.0000']
+ ]
+ ],
+ [
+ ' 20.0 - 30.0000 ', 0, $float,
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => ' 20.0 - 30.0000 ',
+ 'range' => ['20.0', '30.0000']
+ ]
+ ],
+ [
+ '-20.0--10.0', 0, $float + $negative,
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '-20.0--10.0',
+ 'range' => ['-20.0', '-10.0']
+ ]
+ ],
+ [
+ '-2.0K--1.0K', 0, $float + $negative + $suffix,
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '-2.0K--1.0K',
+ 'range' => [strval(ZBX_KIBIBYTE * -2), strval(ZBX_KIBIBYTE * -1)]
+ ]
+ ],
+ [
+ '1h-1.5h', 0, $float + $suffix,
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '1h-1.5h',
+ 'range' => [strval(SEC_PER_HOUR), strval(SEC_PER_HOUR * 1.5)]
+ ]
+ ],
+ [
+ '.5K-1K', 0, $float + $suffix,
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '.5K-1K',
+ 'range' => [strval(ZBX_KIBIBYTE * 0.5), strval(ZBX_KIBIBYTE * 1)]
+ ]
+ ],
+ [
+ '.2-10', 0, $float,
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '.2-10',
+ 'range' => ['.2', '10']
+ ]
+ ],
+ [
+ '0.2-10', 0, $float,
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '0.2-10',
+ 'range' => ['0.2', '10']
+ ]
+ ],
+ [
+ '{$M}-10', 0, ['usermacros' => true] + $float,
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{$M}-10',
+ 'range' => ['{$M}', '10']
+ ]
+ ],
+ [
+ '{#M}-10.0', 0, ['lldmacros' => true] + $float,
+ [
+ 'rc' => CParser::PARSE_SUCCESS,
+ 'match' => '{#M}-10.0',
+ 'range' => ['{#M}', '10.0']
+ ]
+ ],
// partial success
[
'random text.....0....text', 16, [],
@@ -603,6 +703,54 @@ class CRangeParserTest extends TestCase {
'range' => ['100']
]
],
+ [
+ '20-30.001', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '20-30',
+ 'range' => ['20', '30']
+ ]
+ ],
+ [
+ '20--30.001', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '20',
+ 'range' => ['20']
+ ]
+ ],
+ [
+ '10.00-.2', 0, $float,
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '10.00',
+ 'range' => ['10.00']
+ ]
+ ],
+ [
+ '10.00-', 0, [],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '10',
+ 'range' => ['10']
+ ]
+ ],
+ [
+ '{$M}-10.0', 0, ['usermacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{$M}-10',
+ 'range' => ['{$M}', '10']
+ ]
+ ],
+ [
+ '{#M}-10K', 0, ['lldmacros' => true],
+ [
+ 'rc' => CParser::PARSE_SUCCESS_CONT,
+ 'match' => '{#M}-10',
+ 'range' => ['{#M}', '10']
+ ]
+ ],
// fail
[
'', 0, [],
@@ -749,6 +897,22 @@ class CRangeParserTest extends TestCase {
'match' => '',
'range' => []
]
+ ],
+ [
+ '-10.2--20.3', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'range' => []
+ ]
+ ],
+ [
+ '.2', 0, [],
+ [
+ 'rc' => CParser::PARSE_FAIL,
+ 'match' => '',
+ 'range' => []
+ ]
]
];
}
diff --git a/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php b/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php
index a4bc6e61301..fc6dacd80fc 100644
--- a/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php
+++ b/ui/tests/unit/include/classes/validators/CApiInputValidatorTest.php
@@ -3882,6 +3882,78 @@ class CApiInputValidatorTest extends TestCase {
'2038-01-18'
],
[
+ ['type' => API_NUMERIC_RANGES],
+ null,
+ '/1/numeric_ranges',
+ 'Invalid parameter "/1/numeric_ranges": a character string is expected.'
+ ],
+ [
+ ['type' => API_NUMERIC_RANGES],
+ '',
+ '/1/numeric_ranges',
+ ''
+ ],
+ [
+ ['type' => API_NUMERIC_RANGES, 'flags' => API_NOT_EMPTY],
+ '',
+ '/1/numeric_ranges',
+ 'Invalid parameter "/1/numeric_ranges": cannot be empty.'
+ ],
+ [
+ ['type' => API_NUMERIC_RANGES, 'length' => 5],
+ '12-15',
+ '/1/numeric_ranges',
+ '12-15'
+ ],
+ [
+ ['type' => API_NUMERIC_RANGES, 'length' => 5],
+ '12-150',
+ '/1/numeric_ranges',
+ 'Invalid parameter "/1/numeric_ranges": value is too long.'
+ ],
+ [
+ ['type' => API_NUMERIC_RANGES],
+ [],
+ '/1/numeric_ranges',
+ 'Invalid parameter "/1/numeric_ranges": a character string is expected.'
+ ],
+ [
+ ['type' => API_NUMERIC_RANGES],
+ true,
+ '/1/numeric_ranges',
+ 'Invalid parameter "/1/numeric_ranges": a character string is expected.'
+ ],
+ [
+ ['type' => API_NUMERIC_RANGES],
+ false,
+ '/1/numeric_ranges',
+ 'Invalid parameter "/1/numeric_ranges": a character string is expected.'
+ ],
+ [
+ ['type' => API_NUMERIC_RANGES],
+ 'aaa',
+ '/1/numeric_ranges',
+ 'Invalid parameter "/1/numeric_ranges": invalid range expression.'
+ ],
+ [
+ ['type' => API_NUMERIC_RANGES],
+ '123',
+ '/1/numeric_ranges',
+ '123'
+ ],
+ [
+ ['type' => API_NUMERIC_RANGES],
+ '-5',
+ '/1/numeric_ranges',
+ '-5'
+ ],
+ [
+ ['type' => API_NUMERIC_RANGES],
+ '20.0-30.0000',
+ '/1/numeric_ranges',
+ '20.0-30.0000'
+ ],
+ [
['type' => API_UUID],
null,
'/uuid',
diff --git a/ui/tests/unit/include/classes/validators/CHistFunctionValidatorTest.php b/ui/tests/unit/include/classes/validators/CHistFunctionValidatorTest.php
index bc7c61fd059..0f4e6c92c91 100644
--- a/ui/tests/unit/include/classes/validators/CHistFunctionValidatorTest.php
+++ b/ui/tests/unit/include/classes/validators/CHistFunctionValidatorTest.php
@@ -60,7 +60,6 @@ class CHistFunctionValidatorTest extends TestCase {
['avg(/host/key, 596524h)', [], ['rc' => false, 'error' => 'invalid second parameter in function "avg"']],
['avg(/host/key, 24856d)', [], ['rc' => false, 'error' => 'invalid second parameter in function "avg"']],
['avg(/host/key, 3551w)', [], ['rc' => false, 'error' => 'invalid second parameter in function "avg"']],
-
['avg(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "avg"']],
['min(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "min"']],
@@ -111,6 +110,134 @@ class CHistFunctionValidatorTest extends TestCase {
['sum(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
['sum(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "sum"']],
+ ['kurtosis(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "kurtosis"']],
+ ['kurtosis(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "kurtosis"']],
+ ['kurtosis(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "kurtosis"']],
+ ['kurtosis(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "kurtosis"']],
+ ['kurtosis(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['kurtosis(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['kurtosis(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['kurtosis(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['kurtosis(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "kurtosis"']],
+ ['kurtosis(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['kurtosis(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['kurtosis(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['kurtosis(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['kurtosis(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['kurtosis(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "kurtosis"']],
+
+ ['mad(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "mad"']],
+ ['mad(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "mad"']],
+ ['mad(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "mad"']],
+ ['mad(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "mad"']],
+ ['mad(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['mad(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['mad(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['mad(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['mad(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "mad"']],
+ ['mad(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['mad(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['mad(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['mad(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['mad(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['mad(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "mad"']],
+
+ ['skewness(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "skewness"']],
+ ['skewness(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "skewness"']],
+ ['skewness(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "skewness"']],
+ ['skewness(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "skewness"']],
+ ['skewness(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['skewness(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['skewness(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['skewness(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['skewness(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "skewness"']],
+ ['skewness(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['skewness(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['skewness(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['skewness(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['skewness(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['skewness(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "skewness"']],
+
+ ['stddevpop(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "stddevpop"']],
+ ['stddevpop(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "stddevpop"']],
+ ['stddevpop(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "stddevpop"']],
+ ['stddevpop(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "stddevpop"']],
+ ['stddevpop(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['stddevpop(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['stddevpop(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['stddevpop(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['stddevpop(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "stddevpop"']],
+ ['stddevpop(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['stddevpop(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['stddevpop(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['stddevpop(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['stddevpop(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['stddevpop(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "stddevpop"']],
+
+ ['stddevsamp(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "stddevsamp"']],
+ ['stddevsamp(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "stddevsamp"']],
+ ['stddevsamp(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "stddevsamp"']],
+ ['stddevsamp(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "stddevsamp"']],
+ ['stddevsamp(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['stddevsamp(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['stddevsamp(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['stddevsamp(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['stddevsamp(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "stddevsamp"']],
+ ['stddevsamp(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['stddevsamp(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['stddevsamp(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['stddevsamp(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['stddevsamp(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['stddevsamp(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "stddevsamp"']],
+
+ ['sumofsquares(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "sumofsquares"']],
+ ['sumofsquares(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "sumofsquares"']],
+ ['sumofsquares(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "sumofsquares"']],
+ ['sumofsquares(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "sumofsquares"']],
+ ['sumofsquares(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['sumofsquares(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['sumofsquares(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['sumofsquares(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['sumofsquares(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "sumofsquares"']],
+ ['sumofsquares(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['sumofsquares(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['sumofsquares(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['sumofsquares(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['sumofsquares(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['sumofsquares(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "sumofsquares"']],
+
+ ['varpop(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "varpop"']],
+ ['varpop(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "varpop"']],
+ ['varpop(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "varpop"']],
+ ['varpop(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "varpop"']],
+ ['varpop(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['varpop(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['varpop(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['varpop(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['varpop(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "varpop"']],
+ ['varpop(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['varpop(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['varpop(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['varpop(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['varpop(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['varpop(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "varpop"']],
+
+ ['varsamp(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "varsamp"']],
+ ['varsamp(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "varsamp"']],
+ ['varsamp(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "varsamp"']],
+ ['varsamp(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "varsamp"']],
+ ['varsamp(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['varsamp(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['varsamp(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['varsamp(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['varsamp(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "varsamp"']],
+ ['varsamp(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['varsamp(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['varsamp(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['varsamp(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['varsamp(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['varsamp(/host/key, #256,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "varsamp"']],
+
['change(/host/key)', [], ['rc' => true, 'error' => null]],
['change(/host/key,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "change"']],
@@ -226,6 +353,38 @@ class CHistFunctionValidatorTest extends TestCase {
['count(/host/key, #256, "regexp", {#LLDMACRO})', [], ['rc' => true, 'error' => null]],
['count(/host/key, #256, "regexp",,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "count"']],
+ ['countunique(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "countunique"']],
+ ['countunique(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "countunique"']],
+ ['countunique(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "countunique"']],
+ ['countunique(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "countunique"']],
+ ['countunique(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key,#1)', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "countunique"']],
+ ['countunique(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #3:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #5:now/M)', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #2147483647)', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #256,)', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #256, "str")', [], ['rc' => false, 'error' => 'invalid third parameter in function "countunique"']],
+ ['countunique(/host/key, #256, 10)', [], ['rc' => false, 'error' => 'invalid third parameter in function "countunique"']],
+ ['countunique(/host/key, #256, 10K)', [], ['rc' => false, 'error' => 'invalid third parameter in function "countunique"']],
+ ['countunique(/host/key, #256, "eq")', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #256, "ne")', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #256, "like")', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #256, "bitand")', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #256, "{$MACRO}{$LLDMACRO}")', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #256, {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #256, {$LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #256, "regexp",)', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #256, "regexp", 100)', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #256, "regexp", 1s)', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #256, "regexp", {$MACRO})', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #256, "regexp", {#LLDMACRO})', [], ['rc' => true, 'error' => null]],
+ ['countunique(/host/key, #256, "regexp",,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "countunique"']],
+
['find(/host/key)', [], ['rc' => true, 'error' => null]],
['find(/host/key,)', [], ['rc' => true, 'error' => null]],
['find(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "find"']],
@@ -258,6 +417,22 @@ class CHistFunctionValidatorTest extends TestCase {
['find(/host/key, #256, "regexp", {#LLDMACRO})', [], ['rc' => true, 'error' => null]],
['find(/host/key, #256, "regexp",,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "find"']],
+ ['first(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "first"']],
+ ['first(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "first"']],
+ ['first(/host/key,0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "first"']],
+ ['first(/host/key,#0)', [], ['rc' => false, 'error' => 'invalid second parameter in function "first"']],
+ ['first(/host/key,1)', [], ['rc' => true, 'error' => null]],
+ ['first(/host/key,#1)', [], ['rc' => false, 'error' => 'invalid second parameter in function "first"']],
+ ['first(/host/key,1s)', [], ['rc' => true, 'error' => null]],
+ ['first(/host/key, 1m)', [], ['rc' => true, 'error' => null]],
+ ['first(/host/key, 1M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "first"']],
+ ['first(/host/key, 1m:now/h-1h)', [], ['rc' => true, 'error' => null]],
+ ['first(/host/key, #3:now/h-1h)', [], ['rc' => false, 'error' => 'invalid second parameter in function "first"']],
+ ['first(/host/key, #5:now/M)', [], ['rc' => false, 'error' => 'invalid second parameter in function "first"']],
+ ['first(/host/key, #2147483647)', [], ['rc' => false, 'error' => 'invalid second parameter in function "first"']],
+ ['first(/host/key, 2147483647)', [], ['rc' => true, 'error' => null]],
+ ['first(/host/key, 1,)', [], ['rc' => false, 'error' => 'invalid number of parameters in function "first"']],
+
['forecast(/host/key)', [], ['rc' => false, 'error' => 'mandatory parameter is missing in function "forecast"']],
['forecast(/host/key,)', [], ['rc' => false, 'error' => 'invalid second parameter in function "forecast"']],
['forecast(/host/key,,10h)', [], ['rc' => false, 'error' => 'invalid second parameter in function "forecast"']],